@nextclaw/nextclaw-ncp-runtime-codex-sdk 0.1.54 → 0.1.55-beta.1
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/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +15 -5
- package/dist/index.js.map +1 -1
- package/dist/services/codex-app-server-client.service.js +242 -0
- package/dist/services/codex-app-server-client.service.js.map +1 -0
- package/dist/services/codex-app-server-ncp-agent-runtime.service.d.ts +32 -0
- package/dist/services/codex-app-server-ncp-agent-runtime.service.d.ts.map +1 -0
- package/dist/services/codex-app-server-ncp-agent-runtime.service.js +410 -0
- package/dist/services/codex-app-server-ncp-agent-runtime.service.js.map +1 -0
- package/dist/services/codex-desktop-thread-index-sync.service.d.ts +47 -0
- package/dist/services/codex-desktop-thread-index-sync.service.d.ts.map +1 -0
- package/dist/services/codex-desktop-thread-index-sync.service.js +256 -0
- package/dist/services/codex-desktop-thread-index-sync.service.js.map +1 -0
- package/dist/types/codex-app-server-runtime.types.d.ts +26 -0
- package/dist/types/codex-app-server-runtime.types.d.ts.map +1 -0
- package/dist/utils/codex-app-server-item-mapper.utils.js +67 -0
- package/dist/utils/codex-app-server-item-mapper.utils.js.map +1 -0
- package/dist/utils/codex-app-server-request.utils.js +49 -0
- package/dist/utils/codex-app-server-request.utils.js.map +1 -0
- package/dist/{codex-openai-responses-bridge-assistant-output.utils.js → utils/codex-openai-responses-bridge-assistant-output.utils.js} +2 -2
- package/dist/utils/codex-openai-responses-bridge-assistant-output.utils.js.map +1 -0
- package/dist/utils/codex-openai-responses-bridge-request.utils.js.map +1 -1
- package/dist/utils/codex-openai-responses-bridge-stream.utils.js +1 -1
- package/dist/utils/codex-openai-responses-bridge-stream.utils.js.map +1 -1
- package/dist/utils/codex-openai-responses-bridge.utils.js.map +1 -1
- package/dist/utils/codex-openai-responses-stream-writer.utils.d.ts.map +1 -1
- package/dist/utils/codex-openai-responses-stream-writer.utils.js +2 -9
- package/dist/utils/codex-openai-responses-stream-writer.utils.js.map +1 -1
- package/dist/utils/codex-openai-sse-chunks.utils.js.map +1 -1
- package/dist/utils/codex-rollout-thread-summary.utils.js +121 -0
- package/dist/utils/codex-rollout-thread-summary.utils.js.map +1 -0
- package/package.json +4 -3
- package/dist/codex-openai-responses-bridge-assistant-output.utils.js.map +0 -1
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { findCodexRolloutPathForThreadId, readCodexRolloutSummary } from "../utils/codex-rollout-thread-summary.utils.js";
|
|
2
|
+
import { isAbsolute, join } from "node:path";
|
|
3
|
+
import { existsSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
//#region src/services/codex-desktop-thread-index-sync.service.ts
|
|
6
|
+
const THREAD_INDEX_SYNC_ENV = "NEXTCLAW_CODEX_DESKTOP_THREAD_INDEX_SYNC";
|
|
7
|
+
const CODEX_SQLITE_DATABASE = "state_5.sqlite";
|
|
8
|
+
const CODEX_SESSIONS_DIRECTORY = "sessions";
|
|
9
|
+
const REQUIRED_THREAD_COLUMNS = [
|
|
10
|
+
"id",
|
|
11
|
+
"rollout_path",
|
|
12
|
+
"updated_at",
|
|
13
|
+
"tokens_used",
|
|
14
|
+
"has_user_event"
|
|
15
|
+
];
|
|
16
|
+
const THREAD_INSERT_COLUMN_ORDER = [
|
|
17
|
+
"id",
|
|
18
|
+
"rollout_path",
|
|
19
|
+
"created_at",
|
|
20
|
+
"updated_at",
|
|
21
|
+
"source",
|
|
22
|
+
"model_provider",
|
|
23
|
+
"cwd",
|
|
24
|
+
"title",
|
|
25
|
+
"sandbox_policy",
|
|
26
|
+
"approval_mode",
|
|
27
|
+
"tokens_used",
|
|
28
|
+
"has_user_event",
|
|
29
|
+
"archived",
|
|
30
|
+
"cli_version",
|
|
31
|
+
"first_user_message",
|
|
32
|
+
"memory_mode",
|
|
33
|
+
"model",
|
|
34
|
+
"reasoning_effort",
|
|
35
|
+
"created_at_ms",
|
|
36
|
+
"updated_at_ms",
|
|
37
|
+
"preview"
|
|
38
|
+
];
|
|
39
|
+
var CodexDesktopThreadIndexSyncService = class {
|
|
40
|
+
databasePath;
|
|
41
|
+
env;
|
|
42
|
+
homeDirectory;
|
|
43
|
+
loadDatabaseSync;
|
|
44
|
+
logger;
|
|
45
|
+
sessionsDirectory;
|
|
46
|
+
constructor(options = {}) {
|
|
47
|
+
this.databasePath = options.databasePath;
|
|
48
|
+
this.env = options.env ?? process.env;
|
|
49
|
+
this.homeDirectory = options.homeDirectory ?? homedir();
|
|
50
|
+
this.loadDatabaseSync = options.loadDatabaseSync ?? loadCodexDesktopDatabaseSync;
|
|
51
|
+
this.logger = options.logger ?? console;
|
|
52
|
+
this.sessionsDirectory = options.sessionsDirectory;
|
|
53
|
+
}
|
|
54
|
+
syncThread = async (params) => {
|
|
55
|
+
if (isDisabled(this.env[THREAD_INDEX_SYNC_ENV])) return;
|
|
56
|
+
const threadId = readString(params.threadId);
|
|
57
|
+
if (!threadId) return;
|
|
58
|
+
try {
|
|
59
|
+
await this.syncThreadIndex(threadId);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
this.logger.error(`[nextclaw-codex-app-server] failed to sync Codex Desktop thread index: ${formatError(error)}`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
syncThreadIndex = async (threadId) => {
|
|
65
|
+
const databasePath = this.resolveDatabasePath();
|
|
66
|
+
if (!existsSync(databasePath)) return;
|
|
67
|
+
const database = new (await (this.loadDatabaseSync()))(databasePath);
|
|
68
|
+
try {
|
|
69
|
+
database.exec("PRAGMA busy_timeout = 100;");
|
|
70
|
+
const columns = readThreadTableColumns(database);
|
|
71
|
+
if (!hasRequiredThreadColumns(columns)) {
|
|
72
|
+
this.logger.warn("[nextclaw-codex-app-server] skipped Codex Desktop thread index sync: unknown threads schema.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
const row = readThreadRow(database, threadId, columns);
|
|
76
|
+
const rolloutPath = this.resolveRolloutPath(row, threadId);
|
|
77
|
+
if (!rolloutPath) return;
|
|
78
|
+
const summary = readCodexRolloutSummary(rolloutPath);
|
|
79
|
+
if (!row) {
|
|
80
|
+
this.insertThreadRow({
|
|
81
|
+
columns,
|
|
82
|
+
database,
|
|
83
|
+
rolloutPath,
|
|
84
|
+
summary,
|
|
85
|
+
threadId
|
|
86
|
+
});
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.updateThreadRow({
|
|
90
|
+
columns,
|
|
91
|
+
database,
|
|
92
|
+
rolloutPath,
|
|
93
|
+
row,
|
|
94
|
+
summary,
|
|
95
|
+
threadId
|
|
96
|
+
});
|
|
97
|
+
} finally {
|
|
98
|
+
database.close();
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
insertThreadRow = (params) => {
|
|
102
|
+
const { columns, database, rolloutPath, summary, threadId } = params;
|
|
103
|
+
const sessionMeta = summary.sessionMeta;
|
|
104
|
+
const cwd = readString(sessionMeta?.cwd);
|
|
105
|
+
const createdAtMs = summary.createdAtMs ?? sessionMeta?.timestampMs ?? summary.updatedAtMs;
|
|
106
|
+
const updatedAtMs = summary.updatedAtMs ?? createdAtMs;
|
|
107
|
+
if (!cwd || createdAtMs === void 0 || updatedAtMs === void 0) return;
|
|
108
|
+
const firstUserMessage = limitText(summary.firstUserMessage ?? threadId);
|
|
109
|
+
const valuesByColumn = {
|
|
110
|
+
approval_mode: readString(sessionMeta?.approvalMode) ?? "never",
|
|
111
|
+
archived: 0,
|
|
112
|
+
cli_version: readString(sessionMeta?.cliVersion) ?? "",
|
|
113
|
+
created_at: Math.floor(createdAtMs / 1e3),
|
|
114
|
+
created_at_ms: createdAtMs,
|
|
115
|
+
cwd,
|
|
116
|
+
first_user_message: firstUserMessage,
|
|
117
|
+
has_user_event: summary.hasUserEvent ? 1 : 0,
|
|
118
|
+
id: threadId,
|
|
119
|
+
memory_mode: readString(sessionMeta?.memoryMode) ?? "enabled",
|
|
120
|
+
model: readString(sessionMeta?.model) ?? null,
|
|
121
|
+
model_provider: readString(sessionMeta?.modelProvider) ?? "openai",
|
|
122
|
+
preview: firstUserMessage,
|
|
123
|
+
reasoning_effort: readString(sessionMeta?.reasoningEffort) ?? null,
|
|
124
|
+
rollout_path: rolloutPath,
|
|
125
|
+
sandbox_policy: readString(sessionMeta?.sandboxPolicy) ?? JSON.stringify({ type: "disabled" }),
|
|
126
|
+
source: readString(sessionMeta?.source) ?? "vscode",
|
|
127
|
+
title: firstUserMessage,
|
|
128
|
+
tokens_used: summary.tokensUsed ?? 0,
|
|
129
|
+
updated_at: Math.floor(updatedAtMs / 1e3),
|
|
130
|
+
updated_at_ms: updatedAtMs
|
|
131
|
+
};
|
|
132
|
+
const insertColumns = THREAD_INSERT_COLUMN_ORDER.filter((column) => columns.has(column));
|
|
133
|
+
if (!hasRequiredInsertValues(insertColumns, valuesByColumn)) return;
|
|
134
|
+
database.prepare(`
|
|
135
|
+
INSERT INTO threads (${insertColumns.join(", ")})
|
|
136
|
+
VALUES (${insertColumns.map(() => "?").join(", ")})
|
|
137
|
+
`).run(...insertColumns.map((column) => valuesByColumn[column]));
|
|
138
|
+
};
|
|
139
|
+
updateThreadRow = (params) => {
|
|
140
|
+
const updates = [];
|
|
141
|
+
const values = [];
|
|
142
|
+
const { columns, database, rolloutPath, row, summary, threadId } = params;
|
|
143
|
+
if (readString(row.rolloutPath) !== rolloutPath) {
|
|
144
|
+
updates.push("rollout_path = ?");
|
|
145
|
+
values.push(rolloutPath);
|
|
146
|
+
}
|
|
147
|
+
if (shouldUpdateTimestamp(row, summary)) {
|
|
148
|
+
const updatedAtMs = summary.updatedAtMs;
|
|
149
|
+
if (updatedAtMs !== void 0) {
|
|
150
|
+
updates.push("updated_at = ?");
|
|
151
|
+
values.push(Math.floor(updatedAtMs / 1e3));
|
|
152
|
+
if (columns.has("updated_at_ms")) {
|
|
153
|
+
updates.push("updated_at_ms = ?");
|
|
154
|
+
values.push(updatedAtMs);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (summary.tokensUsed !== void 0 && summary.tokensUsed > (readNumber(row.tokensUsed) ?? 0)) {
|
|
159
|
+
updates.push("tokens_used = ?");
|
|
160
|
+
values.push(summary.tokensUsed);
|
|
161
|
+
}
|
|
162
|
+
if (summary.hasUserEvent && (readNumber(row.hasUserEvent) ?? 0) !== 1) {
|
|
163
|
+
updates.push("has_user_event = ?");
|
|
164
|
+
values.push(1);
|
|
165
|
+
}
|
|
166
|
+
if (updates.length === 0) return;
|
|
167
|
+
database.prepare(`UPDATE threads SET ${updates.join(", ")} WHERE id = ?`).run(...values, threadId);
|
|
168
|
+
};
|
|
169
|
+
resolveRolloutPath = (row, threadId) => {
|
|
170
|
+
const rolloutPath = readString(row?.rolloutPath);
|
|
171
|
+
if (rolloutPath && isAbsolute(rolloutPath) && existsSync(rolloutPath)) return rolloutPath;
|
|
172
|
+
return findCodexRolloutPathForThreadId(this.resolveSessionsDirectory(), threadId);
|
|
173
|
+
};
|
|
174
|
+
resolveDatabasePath = () => {
|
|
175
|
+
if (this.databasePath) return this.databasePath;
|
|
176
|
+
return join(this.resolveCodexHome(), "sqlite", CODEX_SQLITE_DATABASE);
|
|
177
|
+
};
|
|
178
|
+
resolveSessionsDirectory = () => {
|
|
179
|
+
return this.sessionsDirectory ?? join(this.resolveCodexHome(), CODEX_SESSIONS_DIRECTORY);
|
|
180
|
+
};
|
|
181
|
+
resolveCodexHome = () => {
|
|
182
|
+
return readString(this.env.CODEX_HOME) ?? join(this.homeDirectory, ".codex");
|
|
183
|
+
};
|
|
184
|
+
};
|
|
185
|
+
async function loadCodexDesktopDatabaseSync() {
|
|
186
|
+
return (await import("node:sqlite")).DatabaseSync;
|
|
187
|
+
}
|
|
188
|
+
function readThreadTableColumns(database) {
|
|
189
|
+
const rows = database.prepare("PRAGMA table_info(threads)").all();
|
|
190
|
+
const columns = /* @__PURE__ */ new Set();
|
|
191
|
+
for (const row of rows) {
|
|
192
|
+
const column = readString(row.name);
|
|
193
|
+
if (column) columns.add(column);
|
|
194
|
+
}
|
|
195
|
+
return columns;
|
|
196
|
+
}
|
|
197
|
+
function hasRequiredThreadColumns(columns) {
|
|
198
|
+
return REQUIRED_THREAD_COLUMNS.every((column) => columns.has(column));
|
|
199
|
+
}
|
|
200
|
+
function readThreadRow(database, threadId, columns) {
|
|
201
|
+
const selectedColumns = [
|
|
202
|
+
"rollout_path AS rolloutPath",
|
|
203
|
+
"updated_at AS updatedAt",
|
|
204
|
+
"tokens_used AS tokensUsed",
|
|
205
|
+
"has_user_event AS hasUserEvent"
|
|
206
|
+
];
|
|
207
|
+
if (columns.has("updated_at_ms")) selectedColumns.push("updated_at_ms AS updatedAtMs");
|
|
208
|
+
const row = database.prepare(`
|
|
209
|
+
SELECT ${selectedColumns.join(", ")}
|
|
210
|
+
FROM threads
|
|
211
|
+
WHERE id = ?
|
|
212
|
+
`).get(threadId);
|
|
213
|
+
return isRecord(row) ? row : void 0;
|
|
214
|
+
}
|
|
215
|
+
function shouldUpdateTimestamp(row, summary) {
|
|
216
|
+
const nextUpdatedAtMs = summary.updatedAtMs;
|
|
217
|
+
if (nextUpdatedAtMs === void 0) return false;
|
|
218
|
+
const currentUpdatedAtMs = readNumber(row.updatedAtMs);
|
|
219
|
+
if (currentUpdatedAtMs !== void 0) return nextUpdatedAtMs > currentUpdatedAtMs;
|
|
220
|
+
return Math.floor(nextUpdatedAtMs / 1e3) > (readNumber(row.updatedAt) ?? 0);
|
|
221
|
+
}
|
|
222
|
+
function hasRequiredInsertValues(columns, valuesByColumn) {
|
|
223
|
+
return REQUIRED_THREAD_COLUMNS.every((column) => columns.includes(column)) && [
|
|
224
|
+
"created_at",
|
|
225
|
+
"source",
|
|
226
|
+
"model_provider",
|
|
227
|
+
"cwd",
|
|
228
|
+
"title"
|
|
229
|
+
].every((column) => columns.includes(column) && valuesByColumn[column] != null);
|
|
230
|
+
}
|
|
231
|
+
function limitText(value) {
|
|
232
|
+
const normalized = value.trim().replace(/\s+/g, " ");
|
|
233
|
+
return normalized.length > 2e3 ? `${normalized.slice(0, 2e3).trimEnd()}...` : normalized;
|
|
234
|
+
}
|
|
235
|
+
function readNumber(value, fallback) {
|
|
236
|
+
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
237
|
+
}
|
|
238
|
+
function readString(value) {
|
|
239
|
+
if (typeof value !== "string") return;
|
|
240
|
+
const trimmed = value.trim();
|
|
241
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
242
|
+
}
|
|
243
|
+
function isDisabled(value) {
|
|
244
|
+
const normalized = readString(value)?.toLowerCase();
|
|
245
|
+
return normalized === "0" || normalized === "false" || normalized === "off";
|
|
246
|
+
}
|
|
247
|
+
function isRecord(value) {
|
|
248
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
249
|
+
}
|
|
250
|
+
function formatError(error) {
|
|
251
|
+
return error instanceof Error ? error.message : String(error);
|
|
252
|
+
}
|
|
253
|
+
//#endregion
|
|
254
|
+
export { CodexDesktopThreadIndexSyncService };
|
|
255
|
+
|
|
256
|
+
//# sourceMappingURL=codex-desktop-thread-index-sync.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-desktop-thread-index-sync.service.js","names":[],"sources":["../../src/services/codex-desktop-thread-index-sync.service.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { isAbsolute, join } from \"node:path\";\nimport {\n findCodexRolloutPathForThreadId,\n readCodexRolloutSummary,\n type CodexRolloutSummary,\n} from \"@/utils/codex-rollout-thread-summary.utils.js\";\n\nconst THREAD_INDEX_SYNC_ENV = \"NEXTCLAW_CODEX_DESKTOP_THREAD_INDEX_SYNC\";\nconst CODEX_SQLITE_DATABASE = \"state_5.sqlite\";\nconst CODEX_SESSIONS_DIRECTORY = \"sessions\";\nconst REQUIRED_THREAD_COLUMNS = [\n \"id\",\n \"rollout_path\",\n \"updated_at\",\n \"tokens_used\",\n \"has_user_event\",\n] as const;\nconst THREAD_INSERT_COLUMN_ORDER = [\n \"id\",\n \"rollout_path\",\n \"created_at\",\n \"updated_at\",\n \"source\",\n \"model_provider\",\n \"cwd\",\n \"title\",\n \"sandbox_policy\",\n \"approval_mode\",\n \"tokens_used\",\n \"has_user_event\",\n \"archived\",\n \"cli_version\",\n \"first_user_message\",\n \"memory_mode\",\n \"model\",\n \"reasoning_effort\",\n \"created_at_ms\",\n \"updated_at_ms\",\n \"preview\",\n] as const;\n\ntype CodexDesktopStatement = {\n all: (...params: unknown[]) => unknown[];\n get: (...params: unknown[]) => unknown;\n run: (...params: unknown[]) => unknown;\n};\n\ntype CodexDesktopDatabase = {\n close: () => void;\n exec: (sql: string) => void;\n prepare: (sql: string) => CodexDesktopStatement;\n};\n\ntype CodexDesktopDatabaseSyncCtor = new (path: string) => CodexDesktopDatabase;\n\ntype CodexDesktopThreadRow = {\n hasUserEvent?: number;\n rolloutPath?: string;\n tokensUsed?: number;\n updatedAt?: number;\n updatedAtMs?: number | null;\n};\n\nexport interface CodexDesktopThreadIndexSync {\n syncThread(params: { threadId?: string | null }): Promise<void>;\n}\n\nexport interface CodexDesktopThreadIndexSyncServiceOptions {\n databasePath?: string;\n env?: NodeJS.ProcessEnv;\n homeDirectory?: string;\n loadDatabaseSync?: () => Promise<CodexDesktopDatabaseSyncCtor>;\n logger?: Pick<Console, \"error\" | \"warn\">;\n sessionsDirectory?: string;\n}\n\nexport class CodexDesktopThreadIndexSyncService implements CodexDesktopThreadIndexSync {\n private readonly databasePath?: string;\n private readonly env: NodeJS.ProcessEnv;\n private readonly homeDirectory: string;\n private readonly loadDatabaseSync: () => Promise<CodexDesktopDatabaseSyncCtor>;\n private readonly logger: Pick<Console, \"error\" | \"warn\">;\n private readonly sessionsDirectory?: string;\n\n constructor(options: CodexDesktopThreadIndexSyncServiceOptions = {}) {\n this.databasePath = options.databasePath;\n this.env = options.env ?? process.env;\n this.homeDirectory = options.homeDirectory ?? homedir();\n this.loadDatabaseSync =\n options.loadDatabaseSync ?? loadCodexDesktopDatabaseSync;\n this.logger = options.logger ?? console;\n this.sessionsDirectory = options.sessionsDirectory;\n }\n\n syncThread = async (params: { threadId?: string | null }): Promise<void> => {\n if (isDisabled(this.env[THREAD_INDEX_SYNC_ENV])) {\n return;\n }\n const threadId = readString(params.threadId);\n if (!threadId) {\n return;\n }\n try {\n await this.syncThreadIndex(threadId);\n } catch (error) {\n this.logger.error(\n `[nextclaw-codex-app-server] failed to sync Codex Desktop thread index: ${formatError(error)}`,\n );\n }\n };\n\n private syncThreadIndex = async (threadId: string): Promise<void> => {\n const databasePath = this.resolveDatabasePath();\n if (!existsSync(databasePath)) {\n return;\n }\n const DatabaseSync = await this.loadDatabaseSync();\n const database = new DatabaseSync(databasePath);\n try {\n database.exec(\"PRAGMA busy_timeout = 100;\");\n const columns = readThreadTableColumns(database);\n if (!hasRequiredThreadColumns(columns)) {\n this.logger.warn(\n \"[nextclaw-codex-app-server] skipped Codex Desktop thread index sync: unknown threads schema.\",\n );\n return;\n }\n const row = readThreadRow(database, threadId, columns);\n const rolloutPath = this.resolveRolloutPath(row, threadId);\n if (!rolloutPath) {\n return;\n }\n const summary = readCodexRolloutSummary(rolloutPath);\n if (!row) {\n this.insertThreadRow({\n columns,\n database,\n rolloutPath,\n summary,\n threadId,\n });\n return;\n }\n this.updateThreadRow({\n columns,\n database,\n rolloutPath,\n row,\n summary,\n threadId,\n });\n } finally {\n database.close();\n }\n };\n\n private insertThreadRow = (params: {\n columns: Set<string>;\n database: CodexDesktopDatabase;\n rolloutPath: string;\n summary: CodexRolloutSummary;\n threadId: string;\n }): void => {\n const { columns, database, rolloutPath, summary, threadId } = params;\n const sessionMeta = summary.sessionMeta;\n const cwd = readString(sessionMeta?.cwd);\n const createdAtMs =\n summary.createdAtMs ?? sessionMeta?.timestampMs ?? summary.updatedAtMs;\n const updatedAtMs = summary.updatedAtMs ?? createdAtMs;\n if (!cwd || createdAtMs === undefined || updatedAtMs === undefined) {\n return;\n }\n\n const firstUserMessage = limitText(summary.firstUserMessage ?? threadId);\n const valuesByColumn: Record<string, unknown> = {\n approval_mode: readString(sessionMeta?.approvalMode) ?? \"never\",\n archived: 0,\n cli_version: readString(sessionMeta?.cliVersion) ?? \"\",\n created_at: Math.floor(createdAtMs / 1000),\n created_at_ms: createdAtMs,\n cwd,\n first_user_message: firstUserMessage,\n has_user_event: summary.hasUserEvent ? 1 : 0,\n id: threadId,\n memory_mode: readString(sessionMeta?.memoryMode) ?? \"enabled\",\n model: readString(sessionMeta?.model) ?? null,\n model_provider: readString(sessionMeta?.modelProvider) ?? \"openai\",\n preview: firstUserMessage,\n reasoning_effort: readString(sessionMeta?.reasoningEffort) ?? null,\n rollout_path: rolloutPath,\n sandbox_policy:\n readString(sessionMeta?.sandboxPolicy) ??\n JSON.stringify({ type: \"disabled\" }),\n source: readString(sessionMeta?.source) ?? \"vscode\",\n title: firstUserMessage,\n tokens_used: summary.tokensUsed ?? 0,\n updated_at: Math.floor(updatedAtMs / 1000),\n updated_at_ms: updatedAtMs,\n };\n const insertColumns = THREAD_INSERT_COLUMN_ORDER.filter((column) =>\n columns.has(column),\n );\n if (!hasRequiredInsertValues(insertColumns, valuesByColumn)) {\n return;\n }\n database\n .prepare(\n `\n INSERT INTO threads (${insertColumns.join(\", \")})\n VALUES (${insertColumns.map(() => \"?\").join(\", \")})\n `,\n )\n .run(...insertColumns.map((column) => valuesByColumn[column]));\n };\n\n private updateThreadRow = (params: {\n columns: Set<string>;\n database: CodexDesktopDatabase;\n rolloutPath: string;\n row: CodexDesktopThreadRow;\n summary: CodexRolloutSummary;\n threadId: string;\n }): void => {\n const updates: string[] = [];\n const values: unknown[] = [];\n const { columns, database, rolloutPath, row, summary, threadId } = params;\n\n if (readString(row.rolloutPath) !== rolloutPath) {\n updates.push(\"rollout_path = ?\");\n values.push(rolloutPath);\n }\n if (shouldUpdateTimestamp(row, summary)) {\n const updatedAtMs = summary.updatedAtMs;\n if (updatedAtMs !== undefined) {\n updates.push(\"updated_at = ?\");\n values.push(Math.floor(updatedAtMs / 1000));\n if (columns.has(\"updated_at_ms\")) {\n updates.push(\"updated_at_ms = ?\");\n values.push(updatedAtMs);\n }\n }\n }\n if (\n summary.tokensUsed !== undefined &&\n summary.tokensUsed > (readNumber(row.tokensUsed) ?? 0)\n ) {\n updates.push(\"tokens_used = ?\");\n values.push(summary.tokensUsed);\n }\n if (summary.hasUserEvent && (readNumber(row.hasUserEvent) ?? 0) !== 1) {\n updates.push(\"has_user_event = ?\");\n values.push(1);\n }\n if (updates.length === 0) {\n return;\n }\n database\n .prepare(`UPDATE threads SET ${updates.join(\", \")} WHERE id = ?`)\n .run(...values, threadId);\n };\n\n private resolveRolloutPath = (\n row: CodexDesktopThreadRow | undefined,\n threadId: string,\n ): string | undefined => {\n const rolloutPath = readString(row?.rolloutPath);\n if (rolloutPath && isAbsolute(rolloutPath) && existsSync(rolloutPath)) {\n return rolloutPath;\n }\n return findCodexRolloutPathForThreadId(\n this.resolveSessionsDirectory(),\n threadId,\n );\n };\n\n private resolveDatabasePath = (): string => {\n if (this.databasePath) {\n return this.databasePath;\n }\n return join(this.resolveCodexHome(), \"sqlite\", CODEX_SQLITE_DATABASE);\n };\n\n private resolveSessionsDirectory = (): string => {\n return (\n this.sessionsDirectory ??\n join(this.resolveCodexHome(), CODEX_SESSIONS_DIRECTORY)\n );\n };\n\n private resolveCodexHome = (): string => {\n return readString(this.env.CODEX_HOME) ?? join(this.homeDirectory, \".codex\");\n };\n}\n\nasync function loadCodexDesktopDatabaseSync(): Promise<CodexDesktopDatabaseSyncCtor> {\n const moduleName = \"node:sqlite\";\n const module = (await import(moduleName)) as {\n DatabaseSync: CodexDesktopDatabaseSyncCtor;\n };\n return module.DatabaseSync;\n}\n\nfunction readThreadTableColumns(database: CodexDesktopDatabase): Set<string> {\n const rows = database.prepare(\"PRAGMA table_info(threads)\").all();\n const columns = new Set<string>();\n for (const row of rows) {\n const column = readString((row as { name?: unknown }).name);\n if (column) {\n columns.add(column);\n }\n }\n return columns;\n}\n\nfunction hasRequiredThreadColumns(columns: Set<string>): boolean {\n return REQUIRED_THREAD_COLUMNS.every((column) => columns.has(column));\n}\n\nfunction readThreadRow(\n database: CodexDesktopDatabase,\n threadId: string,\n columns: Set<string>,\n): CodexDesktopThreadRow | undefined {\n const selectedColumns = [\n \"rollout_path AS rolloutPath\",\n \"updated_at AS updatedAt\",\n \"tokens_used AS tokensUsed\",\n \"has_user_event AS hasUserEvent\",\n ];\n if (columns.has(\"updated_at_ms\")) {\n selectedColumns.push(\"updated_at_ms AS updatedAtMs\");\n }\n const row = database\n .prepare(\n `\n SELECT ${selectedColumns.join(\", \")}\n FROM threads\n WHERE id = ?\n `,\n )\n .get(threadId);\n return isRecord(row) ? (row as CodexDesktopThreadRow) : undefined;\n}\n\nfunction shouldUpdateTimestamp(\n row: CodexDesktopThreadRow,\n summary: CodexRolloutSummary,\n): boolean {\n const nextUpdatedAtMs = summary.updatedAtMs;\n if (nextUpdatedAtMs === undefined) {\n return false;\n }\n const currentUpdatedAtMs = readNumber(row.updatedAtMs);\n if (currentUpdatedAtMs !== undefined) {\n return nextUpdatedAtMs > currentUpdatedAtMs;\n }\n return Math.floor(nextUpdatedAtMs / 1000) > (readNumber(row.updatedAt) ?? 0);\n}\n\nfunction hasRequiredInsertValues(\n columns: readonly string[],\n valuesByColumn: Record<string, unknown>,\n): boolean {\n return REQUIRED_THREAD_COLUMNS.every((column) => columns.includes(column)) &&\n [\"created_at\", \"source\", \"model_provider\", \"cwd\", \"title\"].every(\n (column) => columns.includes(column) && valuesByColumn[column] != null,\n );\n}\n\nfunction limitText(value: string): string {\n const normalized = value.trim().replace(/\\s+/g, \" \");\n return normalized.length > 2_000\n ? `${normalized.slice(0, 2_000).trimEnd()}...`\n : normalized;\n}\n\nfunction readNumber(value: unknown, fallback?: number): number | undefined {\n return typeof value === \"number\" && Number.isFinite(value) ? value : fallback;\n}\n\nfunction readString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction isDisabled(value: string | undefined): boolean {\n const normalized = readString(value)?.toLowerCase();\n return normalized === \"0\" || normalized === \"false\" || normalized === \"off\";\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { CodexAssetContentPathResolver, CodexThreadInput } from "../codex-input.utils.js";
|
|
2
|
+
import { CodexDesktopThreadIndexSync } from "../services/codex-desktop-thread-index-sync.service.js";
|
|
3
|
+
import { NcpAgentConversationStateManager, NcpAgentRunInput } from "@nextclaw/ncp";
|
|
4
|
+
import { CodexOptions, ThreadOptions } from "@openai/codex-sdk";
|
|
5
|
+
|
|
6
|
+
//#region src/types/codex-app-server-runtime.types.d.ts
|
|
7
|
+
type CodexAppServerNcpAgentRuntimeConfig = {
|
|
8
|
+
sessionId: string;
|
|
9
|
+
apiKey: string;
|
|
10
|
+
apiBase?: string;
|
|
11
|
+
model?: string;
|
|
12
|
+
threadId?: string | null;
|
|
13
|
+
codexPathOverride?: string;
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
cliConfig?: CodexOptions["config"];
|
|
16
|
+
threadOptions?: ThreadOptions;
|
|
17
|
+
sessionMetadata?: Record<string, unknown>;
|
|
18
|
+
setSessionMetadata?: (nextMetadata: Record<string, unknown>) => void | Promise<void>;
|
|
19
|
+
inputBuilder?: (input: NcpAgentRunInput) => Promise<CodexThreadInput> | CodexThreadInput;
|
|
20
|
+
resolveAssetContentPath?: CodexAssetContentPathResolver;
|
|
21
|
+
stateManager?: NcpAgentConversationStateManager;
|
|
22
|
+
desktopThreadIndexSync?: CodexDesktopThreadIndexSync | false;
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { CodexAppServerNcpAgentRuntimeConfig };
|
|
26
|
+
//# sourceMappingURL=codex-app-server-runtime.types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-app-server-runtime.types.d.ts","names":[],"sources":["../../src/types/codex-app-server-runtime.types.ts"],"mappings":";;;;;;KA0BY,mCAAA;EACV,SAAA;EACA,MAAA;EACA,OAAA;EACA,KAAA;EACA,QAAA;EACA,iBAAA;EACA,GAAA,GAAM,MAAA;EACN,SAAA,GAAY,YAAA;EACZ,aAAA,GAAgB,aAAA;EAChB,eAAA,GAAkB,MAAA;EAClB,kBAAA,IAAsB,YAAA,EAAc,MAAA,6BAAmC,OAAA;EACvE,YAAA,IAAgB,KAAA,EAAO,gBAAA,KAAqB,OAAA,CAAQ,gBAAA,IAAoB,gBAAA;EACxE,uBAAA,GAA0B,6BAAA;EAC1B,YAAA,GAAe,gCAAA;EACf,sBAAA,GAAyB,2BAAA;AAAA"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//#region src/utils/codex-app-server-item-mapper.utils.ts
|
|
2
|
+
function readAppServerReasoningText(item) {
|
|
3
|
+
const content = Array.isArray(item.content) ? item.content.filter((value) => typeof value === "string") : [];
|
|
4
|
+
if (content.length > 0) return content.join("");
|
|
5
|
+
const summary = Array.isArray(item.summary) ? item.summary.filter((value) => typeof value === "string") : [];
|
|
6
|
+
return summary.length > 0 ? summary.join("\n") : void 0;
|
|
7
|
+
}
|
|
8
|
+
function isAppServerToolLikeItem(type) {
|
|
9
|
+
return type === "mcpToolCall" || type === "dynamicToolCall" || type === "commandExecution" || type === "webSearch" || type === "fileChange" || type === "plan";
|
|
10
|
+
}
|
|
11
|
+
function readAppServerToolName(item) {
|
|
12
|
+
if (item.type === "mcpToolCall") {
|
|
13
|
+
const server = readString(item.server);
|
|
14
|
+
const tool = readString(item.tool) ?? "tool";
|
|
15
|
+
return server ? `mcp:${server}.${tool}` : `mcp:${tool}`;
|
|
16
|
+
}
|
|
17
|
+
if (item.type === "dynamicToolCall") {
|
|
18
|
+
const namespace = readString(item.namespace);
|
|
19
|
+
const tool = readString(item.tool) ?? "tool";
|
|
20
|
+
return namespace ? `${namespace}.${tool}` : tool;
|
|
21
|
+
}
|
|
22
|
+
return readString(item.type) ?? "tool";
|
|
23
|
+
}
|
|
24
|
+
function readAppServerToolArgs(item) {
|
|
25
|
+
if (item.type === "mcpToolCall" || item.type === "dynamicToolCall") return item.arguments;
|
|
26
|
+
if (item.type === "commandExecution") return {
|
|
27
|
+
command: item.command,
|
|
28
|
+
cwd: item.cwd
|
|
29
|
+
};
|
|
30
|
+
if (item.type === "webSearch") return {
|
|
31
|
+
query: item.query,
|
|
32
|
+
action: item.action
|
|
33
|
+
};
|
|
34
|
+
if (item.type === "fileChange") return { changes: item.changes };
|
|
35
|
+
if (item.type === "plan") return { text: item.text };
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
function readAppServerToolResult(item) {
|
|
39
|
+
if (item.type === "mcpToolCall" || item.type === "dynamicToolCall") return {
|
|
40
|
+
status: item.status,
|
|
41
|
+
result: item.result ?? item.contentItems ?? null,
|
|
42
|
+
error: item.error ?? null
|
|
43
|
+
};
|
|
44
|
+
if (item.type === "commandExecution") return {
|
|
45
|
+
status: item.status,
|
|
46
|
+
command: item.command,
|
|
47
|
+
aggregated_output: item.aggregatedOutput,
|
|
48
|
+
exit_code: item.exitCode
|
|
49
|
+
};
|
|
50
|
+
return item;
|
|
51
|
+
}
|
|
52
|
+
function stringifyAppServerToolArgs(args) {
|
|
53
|
+
try {
|
|
54
|
+
return JSON.stringify(args ?? {});
|
|
55
|
+
} catch {
|
|
56
|
+
return JSON.stringify({ __serialization_error__: "tool arguments are not JSON serializable" });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function readString(value) {
|
|
60
|
+
if (typeof value !== "string") return;
|
|
61
|
+
const trimmed = value.trim();
|
|
62
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
63
|
+
}
|
|
64
|
+
//#endregion
|
|
65
|
+
export { isAppServerToolLikeItem, readAppServerReasoningText, readAppServerToolArgs, readAppServerToolName, readAppServerToolResult, stringifyAppServerToolArgs };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=codex-app-server-item-mapper.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-app-server-item-mapper.utils.js","names":[],"sources":["../../src/utils/codex-app-server-item-mapper.utils.ts"],"sourcesContent":["import type { AppServerThreadItem } from \"@/types/codex-app-server-runtime.types.js\";\n\nexport function readAppServerReasoningText(\n item: AppServerThreadItem,\n): string | undefined {\n const content = Array.isArray(item.content)\n ? item.content.filter((value): value is string => typeof value === \"string\")\n : [];\n if (content.length > 0) {\n return content.join(\"\");\n }\n const summary = Array.isArray(item.summary)\n ? item.summary.filter((value): value is string => typeof value === \"string\")\n : [];\n return summary.length > 0 ? summary.join(\"\\n\") : undefined;\n}\n\nexport function isAppServerToolLikeItem(type: unknown): boolean {\n return (\n type === \"mcpToolCall\" ||\n type === \"dynamicToolCall\" ||\n type === \"commandExecution\" ||\n type === \"webSearch\" ||\n type === \"fileChange\" ||\n type === \"plan\"\n );\n}\n\nexport function readAppServerToolName(item: AppServerThreadItem): string {\n if (item.type === \"mcpToolCall\") {\n const server = readString(item.server);\n const tool = readString(item.tool) ?? \"tool\";\n return server ? `mcp:${server}.${tool}` : `mcp:${tool}`;\n }\n if (item.type === \"dynamicToolCall\") {\n const namespace = readString(item.namespace);\n const tool = readString(item.tool) ?? \"tool\";\n return namespace ? `${namespace}.${tool}` : tool;\n }\n return readString(item.type) ?? \"tool\";\n}\n\nexport function readAppServerToolArgs(item: AppServerThreadItem): unknown {\n if (item.type === \"mcpToolCall\" || item.type === \"dynamicToolCall\") {\n return item.arguments;\n }\n if (item.type === \"commandExecution\") {\n return { command: item.command, cwd: item.cwd };\n }\n if (item.type === \"webSearch\") {\n return { query: item.query, action: item.action };\n }\n if (item.type === \"fileChange\") {\n return { changes: item.changes };\n }\n if (item.type === \"plan\") {\n return { text: item.text };\n }\n return {};\n}\n\nexport function readAppServerToolResult(item: AppServerThreadItem): unknown {\n if (item.type === \"mcpToolCall\" || item.type === \"dynamicToolCall\") {\n return {\n status: item.status,\n result: item.result ?? item.contentItems ?? null,\n error: item.error ?? null,\n };\n }\n if (item.type === \"commandExecution\") {\n return {\n status: item.status,\n command: item.command,\n aggregated_output: item.aggregatedOutput,\n exit_code: item.exitCode,\n };\n }\n return item;\n}\n\nexport function stringifyAppServerToolArgs(args: unknown): string {\n try {\n return JSON.stringify(args ?? {});\n } catch {\n return JSON.stringify({\n __serialization_error__: \"tool arguments are not JSON serializable\",\n });\n }\n}\n\nfunction readString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n"],"mappings":";AAEA,SAAgB,2BACd,MACoB;CACpB,MAAM,UAAU,MAAM,QAAQ,KAAK,QAAQ,GACvC,KAAK,QAAQ,QAAQ,UAA2B,OAAO,UAAU,SAAS,GAC1E,EAAE;AACN,KAAI,QAAQ,SAAS,EACnB,QAAO,QAAQ,KAAK,GAAG;CAEzB,MAAM,UAAU,MAAM,QAAQ,KAAK,QAAQ,GACvC,KAAK,QAAQ,QAAQ,UAA2B,OAAO,UAAU,SAAS,GAC1E,EAAE;AACN,QAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,KAAK,GAAG,KAAA;;AAGnD,SAAgB,wBAAwB,MAAwB;AAC9D,QACE,SAAS,iBACT,SAAS,qBACT,SAAS,sBACT,SAAS,eACT,SAAS,gBACT,SAAS;;AAIb,SAAgB,sBAAsB,MAAmC;AACvE,KAAI,KAAK,SAAS,eAAe;EAC/B,MAAM,SAAS,WAAW,KAAK,OAAO;EACtC,MAAM,OAAO,WAAW,KAAK,KAAK,IAAI;AACtC,SAAO,SAAS,OAAO,OAAO,GAAG,SAAS,OAAO;;AAEnD,KAAI,KAAK,SAAS,mBAAmB;EACnC,MAAM,YAAY,WAAW,KAAK,UAAU;EAC5C,MAAM,OAAO,WAAW,KAAK,KAAK,IAAI;AACtC,SAAO,YAAY,GAAG,UAAU,GAAG,SAAS;;AAE9C,QAAO,WAAW,KAAK,KAAK,IAAI;;AAGlC,SAAgB,sBAAsB,MAAoC;AACxE,KAAI,KAAK,SAAS,iBAAiB,KAAK,SAAS,kBAC/C,QAAO,KAAK;AAEd,KAAI,KAAK,SAAS,mBAChB,QAAO;EAAE,SAAS,KAAK;EAAS,KAAK,KAAK;EAAK;AAEjD,KAAI,KAAK,SAAS,YAChB,QAAO;EAAE,OAAO,KAAK;EAAO,QAAQ,KAAK;EAAQ;AAEnD,KAAI,KAAK,SAAS,aAChB,QAAO,EAAE,SAAS,KAAK,SAAS;AAElC,KAAI,KAAK,SAAS,OAChB,QAAO,EAAE,MAAM,KAAK,MAAM;AAE5B,QAAO,EAAE;;AAGX,SAAgB,wBAAwB,MAAoC;AAC1E,KAAI,KAAK,SAAS,iBAAiB,KAAK,SAAS,kBAC/C,QAAO;EACL,QAAQ,KAAK;EACb,QAAQ,KAAK,UAAU,KAAK,gBAAgB;EAC5C,OAAO,KAAK,SAAS;EACtB;AAEH,KAAI,KAAK,SAAS,mBAChB,QAAO;EACL,QAAQ,KAAK;EACb,SAAS,KAAK;EACd,mBAAmB,KAAK;EACxB,WAAW,KAAK;EACjB;AAEH,QAAO;;AAGT,SAAgB,2BAA2B,MAAuB;AAChE,KAAI;AACF,SAAO,KAAK,UAAU,QAAQ,EAAE,CAAC;SAC3B;AACN,SAAO,KAAK,UAAU,EACpB,yBAAyB,4CAC1B,CAAC;;;AAIN,SAAS,WAAW,OAAoC;AACtD,KAAI,OAAO,UAAU,SACnB;CAEF,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAO,QAAQ,SAAS,IAAI,UAAU,KAAA"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/utils/codex-app-server-request.utils.ts
|
|
2
|
+
function toAppServerInput(input) {
|
|
3
|
+
if (typeof input === "string") return input.trim() ? [{
|
|
4
|
+
type: "text",
|
|
5
|
+
text: input
|
|
6
|
+
}] : [];
|
|
7
|
+
const out = [];
|
|
8
|
+
for (const item of input) {
|
|
9
|
+
if (item.type === "text") {
|
|
10
|
+
out.push({
|
|
11
|
+
type: "text",
|
|
12
|
+
text: item.text
|
|
13
|
+
});
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
if (item.type === "local_image") out.push({
|
|
17
|
+
type: "localImage",
|
|
18
|
+
path: item.path
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
}
|
|
23
|
+
function splitModelRoute(value) {
|
|
24
|
+
const normalized = readString(value);
|
|
25
|
+
if (!normalized) return {};
|
|
26
|
+
const slashIndex = normalized.indexOf("/");
|
|
27
|
+
if (slashIndex <= 0 || slashIndex === normalized.length - 1) return { model: normalized };
|
|
28
|
+
return {
|
|
29
|
+
modelProvider: normalized.slice(0, slashIndex),
|
|
30
|
+
model: normalized.slice(slashIndex + 1)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function compactObject(value) {
|
|
34
|
+
const out = {};
|
|
35
|
+
for (const [key, child] of Object.entries(value)) if (child !== void 0) out[key] = child;
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
function normalizeSandbox(value) {
|
|
39
|
+
if (value === "read-only" || value === "workspace-write" || value === "danger-full-access") return value;
|
|
40
|
+
}
|
|
41
|
+
function readString(value) {
|
|
42
|
+
if (typeof value !== "string") return;
|
|
43
|
+
const trimmed = value.trim();
|
|
44
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { compactObject, normalizeSandbox, splitModelRoute, toAppServerInput };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=codex-app-server-request.utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-app-server-request.utils.js","names":[],"sources":["../../src/utils/codex-app-server-request.utils.ts"],"sourcesContent":["import type { ThreadOptions } from \"@openai/codex-sdk\";\nimport type { CodexThreadInput } from \"@/codex-input.utils.js\";\nimport type { JsonObject } from \"@/types/codex-app-server-runtime.types.js\";\n\ntype AppServerUserInput =\n | { type: \"text\"; text: string }\n | { type: \"localImage\"; path: string };\n\nexport function toAppServerInput(input: CodexThreadInput): AppServerUserInput[] {\n if (typeof input === \"string\") {\n return input.trim() ? [{ type: \"text\", text: input }] : [];\n }\n const out: AppServerUserInput[] = [];\n for (const item of input) {\n if (item.type === \"text\") {\n out.push({ type: \"text\", text: item.text });\n continue;\n }\n if (item.type === \"local_image\") {\n out.push({ type: \"localImage\", path: item.path });\n }\n }\n return out;\n}\n\nexport function splitModelRoute(value: string | undefined): {\n model?: string;\n modelProvider?: string;\n} {\n const normalized = readString(value);\n if (!normalized) {\n return {};\n }\n const slashIndex = normalized.indexOf(\"/\");\n if (slashIndex <= 0 || slashIndex === normalized.length - 1) {\n return { model: normalized };\n }\n return {\n modelProvider: normalized.slice(0, slashIndex),\n model: normalized.slice(slashIndex + 1),\n };\n}\n\nexport function compactObject(value: JsonObject): JsonObject {\n const out: JsonObject = {};\n for (const [key, child] of Object.entries(value)) {\n if (child !== undefined) {\n out[key] = child;\n }\n }\n return out;\n}\n\nexport function normalizeSandbox(\n value: ThreadOptions[\"sandboxMode\"] | undefined,\n): string | undefined {\n if (value === \"read-only\" || value === \"workspace-write\" || value === \"danger-full-access\") {\n return value;\n }\n return undefined;\n}\n\nfunction readString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n"],"mappings":";AAQA,SAAgB,iBAAiB,OAA+C;AAC9E,KAAI,OAAO,UAAU,SACnB,QAAO,MAAM,MAAM,GAAG,CAAC;EAAE,MAAM;EAAQ,MAAM;EAAO,CAAC,GAAG,EAAE;CAE5D,MAAM,MAA4B,EAAE;AACpC,MAAK,MAAM,QAAQ,OAAO;AACxB,MAAI,KAAK,SAAS,QAAQ;AACxB,OAAI,KAAK;IAAE,MAAM;IAAQ,MAAM,KAAK;IAAM,CAAC;AAC3C;;AAEF,MAAI,KAAK,SAAS,cAChB,KAAI,KAAK;GAAE,MAAM;GAAc,MAAM,KAAK;GAAM,CAAC;;AAGrD,QAAO;;AAGT,SAAgB,gBAAgB,OAG9B;CACA,MAAM,aAAa,WAAW,MAAM;AACpC,KAAI,CAAC,WACH,QAAO,EAAE;CAEX,MAAM,aAAa,WAAW,QAAQ,IAAI;AAC1C,KAAI,cAAc,KAAK,eAAe,WAAW,SAAS,EACxD,QAAO,EAAE,OAAO,YAAY;AAE9B,QAAO;EACL,eAAe,WAAW,MAAM,GAAG,WAAW;EAC9C,OAAO,WAAW,MAAM,aAAa,EAAE;EACxC;;AAGH,SAAgB,cAAc,OAA+B;CAC3D,MAAM,MAAkB,EAAE;AAC1B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,MAAM,CAC9C,KAAI,UAAU,KAAA,EACZ,KAAI,OAAO;AAGf,QAAO;;AAGT,SAAgB,iBACd,OACoB;AACpB,KAAI,UAAU,eAAe,UAAU,qBAAqB,UAAU,qBACpE,QAAO;;AAKX,SAAS,WAAW,OAAoC;AACtD,KAAI,OAAO,UAAU,SACnB;CAEF,MAAM,UAAU,MAAM,MAAM;AAC5B,QAAO,QAAQ,SAAS,IAAI,UAAU,KAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { readArray, readRawString, readRecord, readString } from "
|
|
1
|
+
import { readArray, readRawString, readRecord, readString } from "../codex-openai-responses-bridge-shared.utils.js";
|
|
2
2
|
import { normalizeAssistantText } from "@nextclaw/ncp";
|
|
3
|
-
//#region src/codex-openai-responses-bridge-assistant-output.utils.ts
|
|
3
|
+
//#region src/utils/codex-openai-responses-bridge-assistant-output.utils.ts
|
|
4
4
|
function extractAssistantText(content) {
|
|
5
5
|
if (typeof content === "string") return content;
|
|
6
6
|
if (!Array.isArray(content)) return "";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codex-openai-responses-bridge-assistant-output.utils.js","names":[],"sources":["../../src/utils/codex-openai-responses-bridge-assistant-output.utils.ts"],"sourcesContent":["import type { ServerResponse } from \"node:http\";\nimport { normalizeAssistantText } from \"@nextclaw/ncp\";\nimport {\n nextSequenceNumber,\n readArray,\n readRecord,\n readRawString,\n readString,\n writeSseEvent,\n type OpenAiChatCompletionChoiceMessage,\n type OpenResponsesOutputItem,\n type StreamSequenceState,\n} from \"@/codex-openai-responses-bridge-shared.utils.js\";\n\nfunction extractAssistantText(content: unknown): string {\n if (typeof content === \"string\") {\n return content;\n }\n if (!Array.isArray(content)) {\n return \"\";\n }\n return content\n .map((entry) => {\n const record = readRecord(entry);\n if (!record) {\n return \"\";\n }\n const type = readString(record.type);\n if (type === \"text\" || type === \"output_text\") {\n return readString(record.text) ?? \"\";\n }\n return \"\";\n })\n .filter(Boolean)\n .join(\"\");\n}\n\ntype ReasoningExtraction = {\n present: boolean;\n text: string;\n};\n\nfunction readReasoningRecordText(value: unknown): string {\n const record = readRecord(value);\n if (!record) {\n return \"\";\n }\n return (\n readRawString(record.text) ??\n readRawString(record.content) ??\n readRawString(record.reasoning) ??\n readRawString(record.reasoning_content) ??\n readRawString(record.thinking) ??\n \"\"\n );\n}\n\nfunction extractReasoningDetailsText(value: unknown): ReasoningExtraction | undefined {\n const rawString = readRawString(value);\n if (rawString !== undefined) {\n return {\n present: true,\n text: rawString,\n };\n }\n\n const record = readRecord(value);\n if (record) {\n return {\n present: true,\n text: readReasoningRecordText(record),\n };\n }\n\n if (!Array.isArray(value)) {\n return undefined;\n }\n\n const text = readArray(value)\n .map((entry) => readReasoningRecordText(entry))\n .join(\"\");\n return {\n present: true,\n text,\n };\n}\n\nfunction extractContentReasoningText(content: unknown): string {\n if (!Array.isArray(content)) {\n return \"\";\n }\n return content\n .map((entry) => {\n const record = readRecord(entry);\n if (!record) {\n return \"\";\n }\n const type = readString(record.type);\n if (\n type === \"reasoning\" ||\n type === \"reasoning_text\" ||\n type === \"thinking\" ||\n type === \"thinking_text\"\n ) {\n return readReasoningRecordText(record);\n }\n return \"\";\n })\n .join(\"\");\n}\n\nfunction extractExplicitReasoning(\n message: OpenAiChatCompletionChoiceMessage | undefined,\n): ReasoningExtraction | undefined {\n const candidates = [\n message?.reasoning_content,\n message?.reasoning,\n message?.thinking,\n message?.reasoning_details,\n ];\n\n for (const candidate of candidates) {\n const extracted = extractReasoningDetailsText(candidate);\n if (extracted) {\n return extracted;\n }\n }\n\n const contentReasoning = extractContentReasoningText(message?.content);\n return contentReasoning\n ? {\n present: true,\n text: contentReasoning,\n }\n : undefined;\n}\n\nfunction extractAssistantOutput(message: OpenAiChatCompletionChoiceMessage | undefined): {\n text: string;\n reasoning: string;\n reasoningPresent: boolean;\n} {\n const rawText = extractAssistantText(message?.content);\n const normalized = normalizeAssistantText(rawText, \"think-tags\");\n const explicitReasoning = extractExplicitReasoning(message);\n const reasoning = explicitReasoning?.text ?? readString(normalized.reasoning) ?? \"\";\n const reasoningPresent = explicitReasoning?.present === true || Boolean(normalized.reasoning);\n const text =\n explicitReasoning?.present === true\n ? readString(normalized.text) ?? readString(rawText) ?? \"\"\n : normalized.reasoning\n ? readString(normalized.text) ?? \"\"\n : readString(rawText) ?? \"\";\n\n return {\n text,\n reasoning,\n reasoningPresent,\n };\n}\n\nfunction buildInProgressReasoningItem(item: OpenResponsesOutputItem): OpenResponsesOutputItem {\n return {\n ...structuredClone(item),\n status: \"in_progress\",\n content: [],\n summary: [],\n };\n}\n\nexport function buildAssistantOutputItems(params: {\n message: OpenAiChatCompletionChoiceMessage | undefined;\n responseId: string;\n}): OpenResponsesOutputItem[] {\n const { reasoning, reasoningPresent, text } = extractAssistantOutput(params.message);\n const outputItems: OpenResponsesOutputItem[] = [];\n\n if (reasoningPresent) {\n outputItems.push({\n type: \"reasoning\",\n id: `${params.responseId}:reasoning:0`,\n summary: [\n {\n type: \"summary_text\",\n text: reasoning,\n },\n ],\n content: [\n {\n type: \"reasoning_text\",\n text: reasoning,\n },\n ],\n status: \"completed\",\n });\n }\n\n if (text) {\n outputItems.push({\n type: \"message\",\n id: `${params.responseId}:message:${outputItems.length}`,\n role: \"assistant\",\n status: \"completed\",\n content: [\n {\n type: \"output_text\",\n text,\n annotations: [],\n },\n ],\n });\n }\n\n return outputItems;\n}\n\nexport function writeReasoningOutputItemEvents(params: {\n response: ServerResponse;\n item: OpenResponsesOutputItem;\n outputIndex: number;\n sequenceState: StreamSequenceState;\n}): void {\n const { item, outputIndex, response, sequenceState } = params;\n const itemId = readString(item.id);\n const content = readArray(item.content);\n const textPart = content.find((entry) => readString(readRecord(entry)?.type) === \"reasoning_text\");\n const text = readString(readRecord(textPart)?.text) ?? \"\";\n const summary = readArray(item.summary);\n const summaryIndex = summary.findIndex((entry) => readString(readRecord(entry)?.type) === \"summary_text\");\n const summaryText =\n summaryIndex >= 0 ? readString(readRecord(summary[summaryIndex])?.text) ?? \"\" : \"\";\n\n writeSseEvent(response, \"response.output_item.added\", {\n type: \"response.output_item.added\",\n sequence_number: nextSequenceNumber(sequenceState),\n output_index: outputIndex,\n item: buildInProgressReasoningItem(item),\n });\n\n if (itemId && summaryText) {\n writeSseEvent(response, \"response.reasoning_summary_part.added\", {\n type: \"response.reasoning_summary_part.added\",\n sequence_number: nextSequenceNumber(sequenceState),\n output_index: outputIndex,\n item_id: itemId,\n summary_index: summaryIndex,\n part: {\n type: \"summary_text\",\n text: \"\",\n },\n });\n writeSseEvent(response, \"response.reasoning_summary_text.delta\", {\n type: \"response.reasoning_summary_text.delta\",\n sequence_number: nextSequenceNumber(sequenceState),\n output_index: outputIndex,\n item_id: itemId,\n summary_index: summaryIndex,\n delta: summaryText,\n });\n writeSseEvent(response, \"response.reasoning_summary_text.done\", {\n type: \"response.reasoning_summary_text.done\",\n sequence_number: nextSequenceNumber(sequenceState),\n output_index: outputIndex,\n item_id: itemId,\n summary_index: summaryIndex,\n text: summaryText,\n });\n writeSseEvent(response, \"response.reasoning_summary_part.done\", {\n type: \"response.reasoning_summary_part.done\",\n sequence_number: nextSequenceNumber(sequenceState),\n output_index: outputIndex,\n item_id: itemId,\n summary_index: summaryIndex,\n part: {\n type: \"summary_text\",\n text: summaryText,\n },\n });\n }\n\n if (itemId) {\n writeSseEvent(response, \"response.reasoning_text.done\", {\n type: \"response.reasoning_text.done\",\n sequence_number: nextSequenceNumber(sequenceState),\n output_index: outputIndex,\n item_id: itemId,\n content_index: 0,\n text,\n });\n }\n\n writeSseEvent(response, \"response.output_item.done\", {\n type: \"response.output_item.done\",\n sequence_number: nextSequenceNumber(sequenceState),\n output_index: outputIndex,\n item,\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-openai-responses-bridge-request.utils.js","names":[],"sources":["../../src/utils/codex-openai-responses-bridge-request.utils.ts"],"sourcesContent":["import {\n readArray,\n readBoolean,\n readNumber,\n readRecord,\n readString,\n withTrailingSlash,\n type CodexOpenAiResponsesBridgeConfig,\n type OpenAiChatCompletionResponse,\n type OpenResponsesItemRecord,\n} from \"../codex-openai-responses-bridge-shared.utils.js\";\nimport {\n buildChatContent,\n mergeChatContent,\n normalizeToolOutput,\n readAssistantMessageText,\n type OpenAiChatContent,\n} from \"../codex-openai-responses-bridge-message-content.utils.js\";\nfunction stripModelPrefix(model: string, prefixes: string[]): string {\n const normalizedModel = model.trim();\n for (const prefix of prefixes) {\n const normalizedPrefix = prefix.trim().toLowerCase();\n if (!normalizedPrefix) {\n continue;\n }\n const candidatePrefix = `${normalizedPrefix}/`;\n if (normalizedModel.toLowerCase().startsWith(candidatePrefix)) {\n return normalizedModel.slice(candidatePrefix.length);\n }\n }\n return normalizedModel;\n}\nfunction resolveUpstreamModel(\n requestedModel: unknown,\n config: CodexOpenAiResponsesBridgeConfig,\n): string {\n const prefixes = (config.modelPrefixes ?? []).filter((value) => value.trim().length > 0);\n const model =\n stripModelPrefix(readString(requestedModel) ?? \"\", prefixes) ||\n stripModelPrefix(config.defaultModel ?? \"\", prefixes);\n if (!model) {\n throw new Error(\"Codex bridge could not resolve an upstream model.\");\n }\n return model;\n}\nfunction buildOpenAiMessages(input: unknown, instructions: unknown): Array<Record<string, unknown>> {\n return new OpenAiMessagesBuilder(input, instructions).build();\n}\n\nclass OpenAiMessagesBuilder {\n private readonly messages: Array<Record<string, unknown>> = [];\n private readonly assistantTextParts: string[] = [];\n private readonly assistantToolCalls: Array<Record<string, unknown>> = [];\n private assistantReasoningContent = { present: false, value: \"\" };\n private systemContent: OpenAiChatContent;\n\n constructor(\n private readonly input: unknown,\n instructions: unknown,\n ) {\n this.systemContent = readString(instructions) ?? null;\n }\n\n build = (): Array<Record<string, unknown>> => {\n if (typeof this.input === \"string\") {\n this.messages.push({\n role: \"user\",\n content: this.input,\n });\n return this.withSystemMessage();\n }\n\n for (const rawItem of readArray(this.input)) {\n const item = readRecord(rawItem) as OpenResponsesItemRecord | undefined;\n if (!item) {\n continue;\n }\n this.appendItem(item);\n }\n\n this.flushAssistant();\n return this.withSystemMessage();\n };\n\n private appendItem = (item: OpenResponsesItemRecord): void => {\n const type = readString(item.type);\n if (type === \"message\") {\n this.appendMessageInputItem(item);\n } else if (type === \"reasoning\") {\n this.appendReasoningItem(item);\n } else if (type === \"function_call\") {\n this.appendFunctionCallItem(item);\n } else if (type === \"function_call_output\") {\n this.appendFunctionCallOutputItem(item);\n }\n };\n\n private appendMessageInputItem = (item: OpenResponsesItemRecord): void => {\n const role = readString(item.role);\n const content = buildChatContent(item.content);\n if (role === \"assistant\") {\n const text = readAssistantMessageText(content);\n if (text.trim()) {\n this.assistantTextParts.push(text);\n }\n return;\n }\n\n this.flushAssistant();\n const normalizedRole = role === \"developer\" ? \"system\" : role;\n if (normalizedRole === \"system\") {\n this.systemContent = mergeChatContent(this.systemContent, content);\n } else if (normalizedRole === \"user\" && content !== null) {\n this.messages.push({\n role: \"user\",\n content,\n });\n }\n };\n\n private appendReasoningItem = (item: OpenResponsesItemRecord): void => {\n const content = readArray(item.content);\n const reasoningText = content\n .map((entry) => {\n const record = readRecord(entry);\n if (!record || readString(record.type) !== \"reasoning_text\") {\n return \"\";\n }\n return typeof record.text === \"string\" ? record.text : \"\";\n })\n .join(\"\");\n this.assistantReasoningContent = { present: true, value: reasoningText };\n };\n\n private appendFunctionCallItem = (item: OpenResponsesItemRecord): void => {\n const name = readString(item.name);\n const argumentsText = readString(item.arguments) ?? \"{}\";\n if (!name) {\n return;\n }\n const callId =\n readString(item.call_id) ??\n readString(item.id) ??\n `call_${this.assistantToolCalls.length}`;\n this.assistantToolCalls.push({\n id: callId,\n type: \"function\",\n function: {\n name,\n arguments: argumentsText,\n },\n });\n };\n\n private appendFunctionCallOutputItem = (item: OpenResponsesItemRecord): void => {\n this.flushAssistant();\n const callId = readString(item.call_id);\n if (!callId) {\n return;\n }\n this.messages.push({\n role: \"tool\",\n tool_call_id: callId,\n content: normalizeToolOutput(item.output),\n });\n };\n\n private flushAssistant = (): void => {\n if (\n this.assistantTextParts.length === 0 &&\n this.assistantToolCalls.length === 0 &&\n !this.assistantReasoningContent.present\n ) {\n return;\n }\n this.messages.push({\n role: \"assistant\",\n content: this.assistantTextParts.join(\"\\n\").trim() || null,\n ...(this.assistantReasoningContent.present\n ? {\n reasoning_content: this.assistantReasoningContent.value,\n }\n : {}),\n ...(this.assistantToolCalls.length > 0\n ? {\n tool_calls: structuredClone(this.assistantToolCalls),\n }\n : {}),\n });\n this.assistantTextParts.length = 0;\n this.assistantToolCalls.length = 0;\n this.assistantReasoningContent = { present: false, value: \"\" };\n };\n\n private withSystemMessage = (): Array<Record<string, unknown>> =>\n this.systemContent === null\n ? this.messages\n : [\n {\n role: \"system\",\n content: this.systemContent,\n },\n ...this.messages,\n ];\n}\n\nfunction toOpenAiTools(value: unknown): Array<Record<string, unknown>> | undefined {\n const tools: Array<Record<string, unknown>> = [];\n for (const entry of readArray(value)) {\n const tool = readRecord(entry);\n const type = readString(tool?.type);\n const fn = readRecord(tool?.function);\n const name = readString(fn?.name) ?? readString(tool?.name);\n if (type !== \"function\" || !name) {\n continue;\n }\n const description =\n (fn ? readString(fn.description) : undefined) ?? readString(tool?.description);\n const parameters =\n (fn ? readRecord(fn.parameters) : undefined) ?? readRecord(tool?.parameters);\n const strict =\n (fn ? readBoolean(fn.strict) : undefined) ?? readBoolean(tool?.strict);\n tools.push({\n type: \"function\",\n function: {\n name,\n ...(description ? { description } : {}),\n parameters: parameters ?? {\n type: \"object\",\n properties: {},\n },\n ...(strict !== undefined ? { strict } : {}),\n },\n });\n }\n return tools.length > 0 ? tools : undefined;\n}\n\nfunction toOpenAiToolChoice(value: unknown): Record<string, unknown> | string | undefined {\n if (value === \"auto\" || value === \"none\" || value === \"required\") {\n return value;\n }\n const record = readRecord(value);\n const fn = readRecord(record?.function);\n const name = readString(fn?.name) ?? readString(record?.name);\n if (readString(record?.type) === \"function\" && name) {\n return {\n type: \"function\",\n function: {\n name,\n },\n };\n }\n return undefined;\n}\n\nexport async function callOpenAiCompatibleUpstream(params: {\n config: CodexOpenAiResponsesBridgeConfig;\n body: Record<string, unknown>;\n}): Promise<{\n model: string;\n response: OpenAiChatCompletionResponse;\n}> {\n const { model, request } = buildOpenAiCompatibleUpstreamRequest({\n config: params.config,\n body: params.body,\n stream: false,\n });\n const upstreamResponse = await fetch(request.url, request.init);\n const rawText = await upstreamResponse.text();\n let parsed: OpenAiChatCompletionResponse;\n try {\n parsed = JSON.parse(rawText) as OpenAiChatCompletionResponse;\n } catch {\n throw new Error(`Bridge upstream returned invalid JSON: ${rawText.slice(0, 240)}`);\n }\n\n if (!upstreamResponse.ok) {\n throw new Error(\n readString(parsed.error?.message) ??\n rawText.slice(0, 240) ??\n `HTTP ${upstreamResponse.status}`,\n );\n }\n\n return {\n model,\n response: parsed,\n };\n}\n\nexport function buildOpenAiCompatibleUpstreamRequest(params: {\n config: CodexOpenAiResponsesBridgeConfig;\n body: Record<string, unknown>;\n stream: boolean;\n}): {\n model: string;\n request: {\n url: string;\n init: RequestInit;\n };\n} {\n const { body, config, stream } = params;\n const model = resolveUpstreamModel(body.model, config);\n const upstreamUrl = new URL(\n \"chat/completions\",\n withTrailingSlash(config.upstreamApiBase),\n );\n const tools = toOpenAiTools(body.tools);\n const toolChoice = toOpenAiToolChoice(body.tool_choice);\n return {\n model,\n request: {\n url: upstreamUrl.toString(),\n init: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(config.upstreamApiKey\n ? {\n Authorization: `Bearer ${config.upstreamApiKey}`,\n }\n : {}),\n ...(config.upstreamExtraHeaders ?? {}),\n },\n body: JSON.stringify({\n model,\n messages: buildOpenAiMessages(body.input, body.instructions),\n ...(tools ? { tools } : {}),\n ...(toolChoice ? { tool_choice: toolChoice } : {}),\n ...(config.upstreamReasoningSplit ? { reasoning_split: true } : {}),\n ...(stream ? { stream: true, stream_options: { include_usage: true } } : {}),\n ...(typeof body.max_output_tokens === \"number\"\n ? {\n max_tokens: Math.max(\n 1,\n Math.trunc(readNumber(body.max_output_tokens) ?? 1),\n ),\n }\n : {}),\n }),\n },\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"codex-openai-responses-bridge-request.utils.js","names":[],"sources":["../../src/utils/codex-openai-responses-bridge-request.utils.ts"],"sourcesContent":["import {\n readArray,\n readBoolean,\n readNumber,\n readRecord,\n readString,\n withTrailingSlash,\n type CodexOpenAiResponsesBridgeConfig,\n type OpenAiChatCompletionResponse,\n type OpenResponsesItemRecord,\n} from \"@/codex-openai-responses-bridge-shared.utils.js\";\nimport {\n buildChatContent,\n mergeChatContent,\n normalizeToolOutput,\n readAssistantMessageText,\n type OpenAiChatContent,\n} from \"@/codex-openai-responses-bridge-message-content.utils.js\";\nfunction stripModelPrefix(model: string, prefixes: string[]): string {\n const normalizedModel = model.trim();\n for (const prefix of prefixes) {\n const normalizedPrefix = prefix.trim().toLowerCase();\n if (!normalizedPrefix) {\n continue;\n }\n const candidatePrefix = `${normalizedPrefix}/`;\n if (normalizedModel.toLowerCase().startsWith(candidatePrefix)) {\n return normalizedModel.slice(candidatePrefix.length);\n }\n }\n return normalizedModel;\n}\nfunction resolveUpstreamModel(\n requestedModel: unknown,\n config: CodexOpenAiResponsesBridgeConfig,\n): string {\n const prefixes = (config.modelPrefixes ?? []).filter((value) => value.trim().length > 0);\n const model =\n stripModelPrefix(readString(requestedModel) ?? \"\", prefixes) ||\n stripModelPrefix(config.defaultModel ?? \"\", prefixes);\n if (!model) {\n throw new Error(\"Codex bridge could not resolve an upstream model.\");\n }\n return model;\n}\nfunction buildOpenAiMessages(input: unknown, instructions: unknown): Array<Record<string, unknown>> {\n return new OpenAiMessagesBuilder(input, instructions).build();\n}\n\nclass OpenAiMessagesBuilder {\n private readonly messages: Array<Record<string, unknown>> = [];\n private readonly assistantTextParts: string[] = [];\n private readonly assistantToolCalls: Array<Record<string, unknown>> = [];\n private assistantReasoningContent = { present: false, value: \"\" };\n private systemContent: OpenAiChatContent;\n\n constructor(\n private readonly input: unknown,\n instructions: unknown,\n ) {\n this.systemContent = readString(instructions) ?? null;\n }\n\n build = (): Array<Record<string, unknown>> => {\n if (typeof this.input === \"string\") {\n this.messages.push({\n role: \"user\",\n content: this.input,\n });\n return this.withSystemMessage();\n }\n\n for (const rawItem of readArray(this.input)) {\n const item = readRecord(rawItem) as OpenResponsesItemRecord | undefined;\n if (!item) {\n continue;\n }\n this.appendItem(item);\n }\n\n this.flushAssistant();\n return this.withSystemMessage();\n };\n\n private appendItem = (item: OpenResponsesItemRecord): void => {\n const type = readString(item.type);\n if (type === \"message\") {\n this.appendMessageInputItem(item);\n } else if (type === \"reasoning\") {\n this.appendReasoningItem(item);\n } else if (type === \"function_call\") {\n this.appendFunctionCallItem(item);\n } else if (type === \"function_call_output\") {\n this.appendFunctionCallOutputItem(item);\n }\n };\n\n private appendMessageInputItem = (item: OpenResponsesItemRecord): void => {\n const role = readString(item.role);\n const content = buildChatContent(item.content);\n if (role === \"assistant\") {\n const text = readAssistantMessageText(content);\n if (text.trim()) {\n this.assistantTextParts.push(text);\n }\n return;\n }\n\n this.flushAssistant();\n const normalizedRole = role === \"developer\" ? \"system\" : role;\n if (normalizedRole === \"system\") {\n this.systemContent = mergeChatContent(this.systemContent, content);\n } else if (normalizedRole === \"user\" && content !== null) {\n this.messages.push({\n role: \"user\",\n content,\n });\n }\n };\n\n private appendReasoningItem = (item: OpenResponsesItemRecord): void => {\n const content = readArray(item.content);\n const reasoningText = content\n .map((entry) => {\n const record = readRecord(entry);\n if (!record || readString(record.type) !== \"reasoning_text\") {\n return \"\";\n }\n return typeof record.text === \"string\" ? record.text : \"\";\n })\n .join(\"\");\n this.assistantReasoningContent = { present: true, value: reasoningText };\n };\n\n private appendFunctionCallItem = (item: OpenResponsesItemRecord): void => {\n const name = readString(item.name);\n const argumentsText = readString(item.arguments) ?? \"{}\";\n if (!name) {\n return;\n }\n const callId =\n readString(item.call_id) ??\n readString(item.id) ??\n `call_${this.assistantToolCalls.length}`;\n this.assistantToolCalls.push({\n id: callId,\n type: \"function\",\n function: {\n name,\n arguments: argumentsText,\n },\n });\n };\n\n private appendFunctionCallOutputItem = (item: OpenResponsesItemRecord): void => {\n this.flushAssistant();\n const callId = readString(item.call_id);\n if (!callId) {\n return;\n }\n this.messages.push({\n role: \"tool\",\n tool_call_id: callId,\n content: normalizeToolOutput(item.output),\n });\n };\n\n private flushAssistant = (): void => {\n if (\n this.assistantTextParts.length === 0 &&\n this.assistantToolCalls.length === 0 &&\n !this.assistantReasoningContent.present\n ) {\n return;\n }\n this.messages.push({\n role: \"assistant\",\n content: this.assistantTextParts.join(\"\\n\").trim() || null,\n ...(this.assistantReasoningContent.present\n ? {\n reasoning_content: this.assistantReasoningContent.value,\n }\n : {}),\n ...(this.assistantToolCalls.length > 0\n ? {\n tool_calls: structuredClone(this.assistantToolCalls),\n }\n : {}),\n });\n this.assistantTextParts.length = 0;\n this.assistantToolCalls.length = 0;\n this.assistantReasoningContent = { present: false, value: \"\" };\n };\n\n private withSystemMessage = (): Array<Record<string, unknown>> =>\n this.systemContent === null\n ? this.messages\n : [\n {\n role: \"system\",\n content: this.systemContent,\n },\n ...this.messages,\n ];\n}\n\nfunction toOpenAiTools(value: unknown): Array<Record<string, unknown>> | undefined {\n const tools: Array<Record<string, unknown>> = [];\n for (const entry of readArray(value)) {\n const tool = readRecord(entry);\n const type = readString(tool?.type);\n const fn = readRecord(tool?.function);\n const name = readString(fn?.name) ?? readString(tool?.name);\n if (type !== \"function\" || !name) {\n continue;\n }\n const description =\n (fn ? readString(fn.description) : undefined) ?? readString(tool?.description);\n const parameters =\n (fn ? readRecord(fn.parameters) : undefined) ?? readRecord(tool?.parameters);\n const strict =\n (fn ? readBoolean(fn.strict) : undefined) ?? readBoolean(tool?.strict);\n tools.push({\n type: \"function\",\n function: {\n name,\n ...(description ? { description } : {}),\n parameters: parameters ?? {\n type: \"object\",\n properties: {},\n },\n ...(strict !== undefined ? { strict } : {}),\n },\n });\n }\n return tools.length > 0 ? tools : undefined;\n}\n\nfunction toOpenAiToolChoice(value: unknown): Record<string, unknown> | string | undefined {\n if (value === \"auto\" || value === \"none\" || value === \"required\") {\n return value;\n }\n const record = readRecord(value);\n const fn = readRecord(record?.function);\n const name = readString(fn?.name) ?? readString(record?.name);\n if (readString(record?.type) === \"function\" && name) {\n return {\n type: \"function\",\n function: {\n name,\n },\n };\n }\n return undefined;\n}\n\nexport async function callOpenAiCompatibleUpstream(params: {\n config: CodexOpenAiResponsesBridgeConfig;\n body: Record<string, unknown>;\n}): Promise<{\n model: string;\n response: OpenAiChatCompletionResponse;\n}> {\n const { model, request } = buildOpenAiCompatibleUpstreamRequest({\n config: params.config,\n body: params.body,\n stream: false,\n });\n const upstreamResponse = await fetch(request.url, request.init);\n const rawText = await upstreamResponse.text();\n let parsed: OpenAiChatCompletionResponse;\n try {\n parsed = JSON.parse(rawText) as OpenAiChatCompletionResponse;\n } catch {\n throw new Error(`Bridge upstream returned invalid JSON: ${rawText.slice(0, 240)}`);\n }\n\n if (!upstreamResponse.ok) {\n throw new Error(\n readString(parsed.error?.message) ??\n rawText.slice(0, 240) ??\n `HTTP ${upstreamResponse.status}`,\n );\n }\n\n return {\n model,\n response: parsed,\n };\n}\n\nexport function buildOpenAiCompatibleUpstreamRequest(params: {\n config: CodexOpenAiResponsesBridgeConfig;\n body: Record<string, unknown>;\n stream: boolean;\n}): {\n model: string;\n request: {\n url: string;\n init: RequestInit;\n };\n} {\n const { body, config, stream } = params;\n const model = resolveUpstreamModel(body.model, config);\n const upstreamUrl = new URL(\n \"chat/completions\",\n withTrailingSlash(config.upstreamApiBase),\n );\n const tools = toOpenAiTools(body.tools);\n const toolChoice = toOpenAiToolChoice(body.tool_choice);\n return {\n model,\n request: {\n url: upstreamUrl.toString(),\n init: {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...(config.upstreamApiKey\n ? {\n Authorization: `Bearer ${config.upstreamApiKey}`,\n }\n : {}),\n ...(config.upstreamExtraHeaders ?? {}),\n },\n body: JSON.stringify({\n model,\n messages: buildOpenAiMessages(body.input, body.instructions),\n ...(tools ? { tools } : {}),\n ...(toolChoice ? { tool_choice: toolChoice } : {}),\n ...(config.upstreamReasoningSplit ? { reasoning_split: true } : {}),\n ...(stream ? { stream: true, stream_options: { include_usage: true } } : {}),\n ...(typeof body.max_output_tokens === \"number\"\n ? {\n max_tokens: Math.max(\n 1,\n Math.trunc(readNumber(body.max_output_tokens) ?? 1),\n ),\n }\n : {}),\n }),\n },\n },\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readArray, readNumber, readRecord, readString, writeSseEvent } from "../codex-openai-responses-bridge-shared.utils.js";
|
|
2
|
-
import { buildAssistantOutputItems } from "
|
|
2
|
+
import { buildAssistantOutputItems } from "./codex-openai-responses-bridge-assistant-output.utils.js";
|
|
3
3
|
//#region src/utils/codex-openai-responses-bridge-stream.utils.ts
|
|
4
4
|
function buildOpenResponsesOutputItems(response, responseId) {
|
|
5
5
|
const message = response.choices?.[0]?.message;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"codex-openai-responses-bridge-stream.utils.js","names":[],"sources":["../../src/utils/codex-openai-responses-bridge-stream.utils.ts"],"sourcesContent":["import type { ServerResponse } from \"node:http\";\nimport { buildAssistantOutputItems } from \"
|
|
1
|
+
{"version":3,"file":"codex-openai-responses-bridge-stream.utils.js","names":[],"sources":["../../src/utils/codex-openai-responses-bridge-stream.utils.ts"],"sourcesContent":["import type { ServerResponse } from \"node:http\";\nimport { buildAssistantOutputItems } from \"./codex-openai-responses-bridge-assistant-output.utils.js\";\nimport {\n readArray,\n readNumber,\n readRecord,\n readString,\n writeSseEvent,\n type OpenAiChatCompletionResponse,\n type OpenResponsesOutputItem,\n} from \"@/codex-openai-responses-bridge-shared.utils.js\";\n\nfunction buildOpenResponsesOutputItems(\n response: OpenAiChatCompletionResponse,\n responseId: string,\n): OpenResponsesOutputItem[] {\n const message = response.choices?.[0]?.message;\n if (!message) {\n return [];\n }\n\n const outputItems = buildAssistantOutputItems({\n message,\n responseId,\n });\n\n const toolCalls = readArray(message.tool_calls);\n toolCalls.forEach((entry, index) => {\n const toolCall = readRecord(entry);\n const fn = readRecord(toolCall?.function);\n const name = readString(fn?.name);\n const argumentsText = readString(fn?.arguments) ?? \"{}\";\n if (!name) {\n return;\n }\n const callId =\n readString(toolCall?.id) ??\n `${responseId}:call:${index}`;\n outputItems.push({\n type: \"function_call\",\n id: `${responseId}:function:${index}`,\n call_id: callId,\n name,\n arguments: argumentsText,\n status: \"completed\",\n });\n });\n\n return outputItems;\n}\n\nfunction normalizeUsageValue(value: unknown): number | Record<string, unknown> | null {\n const numericValue = readNumber(value);\n if (numericValue !== undefined) {\n return Math.max(0, Math.trunc(numericValue));\n }\n const record = readRecord(value);\n if (!record) {\n return null;\n }\n const next: Record<string, unknown> = {};\n for (const [key, entry] of Object.entries(record)) {\n const normalizedEntry = normalizeUsageValue(entry);\n if (normalizedEntry !== null) {\n next[key] = normalizedEntry;\n }\n }\n return Object.keys(next).length > 0 ? next : null;\n}\n\nfunction normalizeUsageRecord(rawUsage: unknown): Record<string, unknown> {\n const usage = readRecord(rawUsage);\n if (!usage) {\n return {};\n }\n const next: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(usage)) {\n const normalizedValue = normalizeUsageValue(value);\n if (normalizedValue === null) {\n continue;\n }\n if (key === \"prompt_tokens\") {\n next.input_tokens = normalizedValue;\n continue;\n }\n if (key === \"completion_tokens\") {\n next.output_tokens = normalizedValue;\n continue;\n }\n if (key === \"prompt_tokens_details\") {\n next.input_tokens_details = normalizedValue;\n continue;\n }\n if (key === \"completion_tokens_details\") {\n next.output_tokens_details = normalizedValue;\n continue;\n }\n next[key] = normalizedValue;\n }\n return next;\n}\n\nexport function buildUsage(response: OpenAiChatCompletionResponse): Record<string, unknown> {\n const promptTokens = Math.max(0, Math.trunc(readNumber(response.usage?.prompt_tokens) ?? 0));\n const completionTokens = Math.max(\n 0,\n Math.trunc(readNumber(response.usage?.completion_tokens) ?? 0),\n );\n const totalTokens = Math.max(\n 0,\n Math.trunc(readNumber(response.usage?.total_tokens) ?? promptTokens + completionTokens),\n );\n return {\n ...normalizeUsageRecord(response.usage),\n input_tokens: promptTokens,\n output_tokens: completionTokens,\n total_tokens: totalTokens,\n };\n}\n\nexport function buildResponseResource(params: {\n responseId: string;\n model: string;\n outputItems: OpenResponsesOutputItem[];\n usage: Record<string, unknown>;\n status?: \"in_progress\" | \"completed\";\n}): Record<string, unknown> {\n const { model, outputItems, responseId, status, usage } = params;\n return {\n id: responseId,\n object: \"response\",\n created_at: Math.floor(Date.now() / 1000),\n status: status ?? \"completed\",\n model,\n output: outputItems,\n usage,\n error: null,\n };\n}\n\nexport function writeStreamError(response: ServerResponse, message: string): void {\n new StreamErrorWriter(response, message).write();\n}\n\nclass StreamErrorWriter {\n constructor(\n private readonly response: ServerResponse,\n private readonly message: string,\n ) {}\n\n write = (): void => {\n this.response.statusCode = 200;\n this.response.setHeader(\"content-type\", \"text/event-stream; charset=utf-8\");\n this.response.setHeader(\"cache-control\", \"no-cache, no-transform\");\n this.response.setHeader(\"connection\", \"keep-alive\");\n writeSseEvent(this.response, \"error\", {\n type: \"error\",\n error: {\n code: \"invalid_request_error\",\n message: this.message,\n },\n });\n this.response.end();\n };\n}\n\nexport function buildBridgeResponsePayload(params: {\n responseId: string;\n model: string;\n response: OpenAiChatCompletionResponse;\n}): {\n outputItems: OpenResponsesOutputItem[];\n usage: Record<string, unknown>;\n responseResource: Record<string, unknown>;\n} {\n const { model, response, responseId } = params;\n const outputItems = buildOpenResponsesOutputItems(response, responseId);\n const usage = buildUsage(response);\n return {\n outputItems,\n usage,\n responseResource: buildResponseResource({\n responseId,\n model,\n outputItems,\n usage,\n }),\n };\n}\n"],"mappings":"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"}
|