codex-mobile-re 0.1.120 → 0.1.121
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/assets/{AutomationsPanel-CmxzsIyf.js → AutomationsPanel-Dx0KVoCE.js} +1 -1
- package/dist/assets/DirectoryHub-BOPhx2Of.js +2 -0
- package/dist/assets/{QueuedMessages-frPDrFTB.js → QueuedMessages-Dq60IlFJ.js} +1 -1
- package/dist/assets/{ReviewPane-DGxlMS32.js → ReviewPane-UleDl12M.js} +1 -1
- package/dist/assets/{RightFilePreview-BLsPHyEY.js → RightFilePreview-Baikjk70.js} +1 -1
- package/dist/assets/{RightFilesPanel-CqQWw9ar.js → RightFilesPanel-DNsqEfsl.js} +1 -1
- package/dist/assets/{RightGitPanel-2fBlPeA3.js → RightGitPanel-D5vGDcb7.js} +2 -2
- package/dist/assets/{SettingsDialog-CZR_qYM9.js → SettingsDialog-eR-iNCMC.js} +3 -3
- package/dist/assets/ThreadConversation-9lNMJVzu.js +39 -0
- package/dist/assets/{ThreadConversation-D4HDYQcC.css → ThreadConversation-BFwLGxZ1.css} +1 -1
- package/dist/assets/ThreadPendingRequestPanel-BVMOch66.js +1 -0
- package/dist/assets/{ThreadTerminalPanel-DJOT59si.js → ThreadTerminalPanel-ClNIUPAl.js} +1 -1
- package/dist/assets/{index-CFZ4hm9X.js → index-BBn90yC5.js} +32 -32
- package/dist/index.html +1 -1
- package/dist-cli/index.js +397 -134
- package/dist-cli/index.js.map +1 -1
- package/package.json +94 -94
- package/dist/assets/DirectoryHub-B5fr6TY2.js +0 -2
- package/dist/assets/ThreadConversation-1Re8aTQw.js +0 -39
- package/dist/assets/ThreadPendingRequestPanel-CztF-wEr.js +0 -1
package/dist-cli/index.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
// src/cli/index.ts
|
|
4
4
|
import { createServer as createServer2 } from "http";
|
|
5
5
|
import { chmodSync as chmodSync2, createWriteStream, existsSync as existsSync14, mkdirSync as mkdirSync2 } from "fs";
|
|
6
|
-
import { readFile as readFile19, stat as
|
|
6
|
+
import { readFile as readFile19, stat as stat19, writeFile as writeFile19 } from "fs/promises";
|
|
7
7
|
import { homedir as homedir11, networkInterfaces } from "os";
|
|
8
|
-
import { isAbsolute as isAbsolute13, join as
|
|
8
|
+
import { isAbsolute as isAbsolute13, join as join32, resolve as resolve8 } from "path";
|
|
9
9
|
import { spawn as spawn9 } from "child_process";
|
|
10
10
|
import { createInterface as createInterface2 } from "readline/promises";
|
|
11
11
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
@@ -341,16 +341,16 @@ function parseApprovalPolicy(value) {
|
|
|
341
341
|
|
|
342
342
|
// src/server/httpServer.ts
|
|
343
343
|
import { fileURLToPath } from "url";
|
|
344
|
-
import { dirname as dirname12, extname as extname5, isAbsolute as isAbsolute12, join as
|
|
344
|
+
import { dirname as dirname12, extname as extname5, isAbsolute as isAbsolute12, join as join31 } from "path";
|
|
345
345
|
import { existsSync as existsSync13 } from "fs";
|
|
346
|
-
import { writeFile as
|
|
346
|
+
import { writeFile as writeFile18, stat as stat18 } from "fs/promises";
|
|
347
347
|
import express from "express";
|
|
348
348
|
|
|
349
349
|
// src/server/codexAppServerBridge.ts
|
|
350
350
|
import { spawn as spawn8 } from "child_process";
|
|
351
351
|
import { mkdtemp as mkdtemp4, readFile as readFile18 } from "fs/promises";
|
|
352
|
-
import { tmpdir as
|
|
353
|
-
import { isAbsolute as isAbsolute11, join as
|
|
352
|
+
import { tmpdir as tmpdir7 } from "os";
|
|
353
|
+
import { isAbsolute as isAbsolute11, join as join29 } from "path";
|
|
354
354
|
|
|
355
355
|
// src/server/accountRoutes.ts
|
|
356
356
|
import { spawn as spawn2 } from "child_process";
|
|
@@ -9920,7 +9920,7 @@ async function handleProjectHttpRequest(req, res, url, deps) {
|
|
|
9920
9920
|
|
|
9921
9921
|
// src/server/bridge/threadRoutes.ts
|
|
9922
9922
|
import { isAbsolute as isAbsolute5 } from "path";
|
|
9923
|
-
import { readFile as readFile10, stat as
|
|
9923
|
+
import { readFile as readFile10, stat as stat11 } from "fs/promises";
|
|
9924
9924
|
|
|
9925
9925
|
// src/server/bridge/session.ts
|
|
9926
9926
|
import { mkdir as mkdir8, readFile as readFile9, rename as rename2, rm as rm6, stat as stat9, writeFile as writeFile8 } from "fs/promises";
|
|
@@ -10899,6 +10899,122 @@ async function mergeSessionCommandsIntoThreadResult(result) {
|
|
|
10899
10899
|
}
|
|
10900
10900
|
}
|
|
10901
10901
|
|
|
10902
|
+
// src/server/bridge/payloadSlimming.ts
|
|
10903
|
+
import { createHash as createHash2 } from "crypto";
|
|
10904
|
+
import { mkdir as mkdir9, stat as stat10, writeFile as writeFile9 } from "fs/promises";
|
|
10905
|
+
import { tmpdir as tmpdir5 } from "os";
|
|
10906
|
+
import { join as join19 } from "path";
|
|
10907
|
+
var COMMAND_OUTPUT_INLINE_LIMIT_BYTES = 16 * 1024;
|
|
10908
|
+
var COMMAND_OUTPUT_SPILL_MAX_BYTES = 8 * 1024 * 1024;
|
|
10909
|
+
var COMMAND_OUTPUT_SPILL_DIR_NAME = "codex-web-command-output";
|
|
10910
|
+
var SPILL_REF_PATTERN = /^[0-9a-f]{40}$/u;
|
|
10911
|
+
var HEAD_BYTES_RATIO = 0.6;
|
|
10912
|
+
function getCommandOutputSpillDir() {
|
|
10913
|
+
return join19(tmpdir5(), COMMAND_OUTPUT_SPILL_DIR_NAME);
|
|
10914
|
+
}
|
|
10915
|
+
function resolveCommandOutputSpillPath(ref) {
|
|
10916
|
+
const normalized = ref.trim().toLowerCase();
|
|
10917
|
+
if (!SPILL_REF_PATTERN.test(normalized)) return null;
|
|
10918
|
+
return join19(getCommandOutputSpillDir(), `${normalized}.txt`);
|
|
10919
|
+
}
|
|
10920
|
+
function truncateUtf8Middle(text, limitBytes) {
|
|
10921
|
+
const buffer = Buffer.from(text, "utf8");
|
|
10922
|
+
const headBytes = Math.floor(limitBytes * HEAD_BYTES_RATIO);
|
|
10923
|
+
const tailBytes = Math.max(0, limitBytes - headBytes);
|
|
10924
|
+
const head = buffer.subarray(0, headBytes).toString("utf8").replace(/\uFFFD+$/u, "");
|
|
10925
|
+
const tail = buffer.subarray(Math.max(0, buffer.length - tailBytes)).toString("utf8").replace(/^\uFFFD+/u, "");
|
|
10926
|
+
const keptBytes = Buffer.byteLength(head, "utf8") + Buffer.byteLength(tail, "utf8");
|
|
10927
|
+
return {
|
|
10928
|
+
text: `${head}
|
|
10929
|
+
\u2026
|
|
10930
|
+
${tail}`,
|
|
10931
|
+
omittedBytes: Math.max(0, buffer.length - keptBytes)
|
|
10932
|
+
};
|
|
10933
|
+
}
|
|
10934
|
+
async function spillCommandOutputToDisk(text) {
|
|
10935
|
+
try {
|
|
10936
|
+
const ref = createHash2("sha1").update(text, "utf8").digest("hex");
|
|
10937
|
+
const spillDir = getCommandOutputSpillDir();
|
|
10938
|
+
await mkdir9(spillDir, { recursive: true });
|
|
10939
|
+
const filePath = join19(spillDir, `${ref}.txt`);
|
|
10940
|
+
try {
|
|
10941
|
+
await stat10(filePath);
|
|
10942
|
+
} catch {
|
|
10943
|
+
await writeFile9(filePath, text, "utf8");
|
|
10944
|
+
}
|
|
10945
|
+
return ref;
|
|
10946
|
+
} catch {
|
|
10947
|
+
return "";
|
|
10948
|
+
}
|
|
10949
|
+
}
|
|
10950
|
+
function slimMcpToolCallItem(item) {
|
|
10951
|
+
if (!("result" in item)) return null;
|
|
10952
|
+
const next = { ...item };
|
|
10953
|
+
delete next.result;
|
|
10954
|
+
return next;
|
|
10955
|
+
}
|
|
10956
|
+
async function slimCommandExecutionItem(item) {
|
|
10957
|
+
const output = item.aggregatedOutput;
|
|
10958
|
+
if (typeof output !== "string" || output.length === 0) return null;
|
|
10959
|
+
const totalBytes = Buffer.byteLength(output, "utf8");
|
|
10960
|
+
if (totalBytes <= COMMAND_OUTPUT_INLINE_LIMIT_BYTES) return null;
|
|
10961
|
+
const { text, omittedBytes } = truncateUtf8Middle(output, COMMAND_OUTPUT_INLINE_LIMIT_BYTES);
|
|
10962
|
+
const ref = totalBytes <= COMMAND_OUTPUT_SPILL_MAX_BYTES ? await spillCommandOutputToDisk(output) : "";
|
|
10963
|
+
return {
|
|
10964
|
+
...item,
|
|
10965
|
+
aggregatedOutput: text,
|
|
10966
|
+
aggregatedOutputSpill: { ref, totalBytes, omittedBytes }
|
|
10967
|
+
};
|
|
10968
|
+
}
|
|
10969
|
+
async function slimThreadTurnsPayload(method, result) {
|
|
10970
|
+
if (!THREAD_METHODS_WITH_TURNS.has(method)) return result;
|
|
10971
|
+
const record = asRecord2(result);
|
|
10972
|
+
const thread = asRecord2(record?.thread);
|
|
10973
|
+
const turns = Array.isArray(thread?.turns) ? thread.turns : null;
|
|
10974
|
+
if (!record || !thread || !turns || turns.length === 0) return result;
|
|
10975
|
+
let changed = false;
|
|
10976
|
+
const nextTurns = [];
|
|
10977
|
+
for (const turn of turns) {
|
|
10978
|
+
const turnRecord = asRecord2(turn);
|
|
10979
|
+
const items = Array.isArray(turnRecord?.items) ? turnRecord.items : null;
|
|
10980
|
+
if (!turnRecord || !items) {
|
|
10981
|
+
nextTurns.push(turn);
|
|
10982
|
+
continue;
|
|
10983
|
+
}
|
|
10984
|
+
let itemChanged = false;
|
|
10985
|
+
const nextItems = [];
|
|
10986
|
+
for (const item of items) {
|
|
10987
|
+
const itemRecord = asRecord2(item);
|
|
10988
|
+
if (!itemRecord) {
|
|
10989
|
+
nextItems.push(item);
|
|
10990
|
+
continue;
|
|
10991
|
+
}
|
|
10992
|
+
const itemType = itemRecord.type;
|
|
10993
|
+
const slimmedItem = itemType === "mcpToolCall" ? slimMcpToolCallItem(itemRecord) : itemType === "commandExecution" ? await slimCommandExecutionItem(itemRecord) : null;
|
|
10994
|
+
if (!slimmedItem) {
|
|
10995
|
+
nextItems.push(item);
|
|
10996
|
+
continue;
|
|
10997
|
+
}
|
|
10998
|
+
itemChanged = true;
|
|
10999
|
+
nextItems.push(slimmedItem);
|
|
11000
|
+
}
|
|
11001
|
+
if (!itemChanged) {
|
|
11002
|
+
nextTurns.push(turn);
|
|
11003
|
+
continue;
|
|
11004
|
+
}
|
|
11005
|
+
changed = true;
|
|
11006
|
+
nextTurns.push({ ...turnRecord, items: nextItems });
|
|
11007
|
+
}
|
|
11008
|
+
if (!changed) return result;
|
|
11009
|
+
return {
|
|
11010
|
+
...record,
|
|
11011
|
+
thread: {
|
|
11012
|
+
...thread,
|
|
11013
|
+
turns: nextTurns
|
|
11014
|
+
}
|
|
11015
|
+
};
|
|
11016
|
+
}
|
|
11017
|
+
|
|
10902
11018
|
// src/server/bridge/threadRoutes.ts
|
|
10903
11019
|
function readStreamTurnId(params) {
|
|
10904
11020
|
const directTurnId = readNonEmptyString(params.turnId) || readNonEmptyString(params.turn_id);
|
|
@@ -11085,7 +11201,7 @@ function handleThreadHttpRequest(req, res, url, deps) {
|
|
|
11085
11201
|
let sessionSize = 0;
|
|
11086
11202
|
if (sessionPath && isAbsolute5(sessionPath)) {
|
|
11087
11203
|
try {
|
|
11088
|
-
const s = await
|
|
11204
|
+
const s = await stat11(sessionPath);
|
|
11089
11205
|
sessionSize = s.size;
|
|
11090
11206
|
} catch {
|
|
11091
11207
|
}
|
|
@@ -11163,6 +11279,22 @@ function handleThreadHttpRequest(req, res, url, deps) {
|
|
|
11163
11279
|
return true;
|
|
11164
11280
|
})();
|
|
11165
11281
|
}
|
|
11282
|
+
if (req.method === "GET" && url.pathname === "/codex-api/command-output") {
|
|
11283
|
+
return (async () => {
|
|
11284
|
+
const ref = url.searchParams.get("ref")?.trim() ?? "";
|
|
11285
|
+
const spillPath = resolveCommandOutputSpillPath(ref);
|
|
11286
|
+
if (!spillPath) {
|
|
11287
|
+
setJson2(res, 400, { error: "Invalid command output reference." });
|
|
11288
|
+
return true;
|
|
11289
|
+
}
|
|
11290
|
+
try {
|
|
11291
|
+
setJson2(res, 200, { text: await readFile10(spillPath, "utf8") });
|
|
11292
|
+
} catch {
|
|
11293
|
+
setJson2(res, 404, { error: "Command output is no longer available." });
|
|
11294
|
+
}
|
|
11295
|
+
return true;
|
|
11296
|
+
})();
|
|
11297
|
+
}
|
|
11166
11298
|
return Promise.resolve(false);
|
|
11167
11299
|
}
|
|
11168
11300
|
|
|
@@ -11273,8 +11405,8 @@ async function runRpcResponsePipeline(deps, method, rpcResult) {
|
|
|
11273
11405
|
}
|
|
11274
11406
|
|
|
11275
11407
|
// src/server/bridge/telegramRoutes.ts
|
|
11276
|
-
import { join as
|
|
11277
|
-
import { readFile as readFile11, writeFile as
|
|
11408
|
+
import { join as join20 } from "path";
|
|
11409
|
+
import { readFile as readFile11, writeFile as writeFile10 } from "fs/promises";
|
|
11278
11410
|
function normalizeTelegramBridgeConfig(value) {
|
|
11279
11411
|
const record = asRecord2(value);
|
|
11280
11412
|
if (!record) return { botToken: "", chatIds: [], allowedUserIds: [] };
|
|
@@ -11297,7 +11429,7 @@ function normalizeTelegramBridgeConfig(value) {
|
|
|
11297
11429
|
return { botToken, chatIds, allowedUserIds };
|
|
11298
11430
|
}
|
|
11299
11431
|
function getTelegramBridgeConfigPath() {
|
|
11300
|
-
return
|
|
11432
|
+
return join20(getCodexHomeDir2(), "telegram-bridge.json");
|
|
11301
11433
|
}
|
|
11302
11434
|
async function readTelegramBridgeConfig() {
|
|
11303
11435
|
const telegramConfigPath = getTelegramBridgeConfigPath();
|
|
@@ -11312,7 +11444,7 @@ async function readTelegramBridgeConfig() {
|
|
|
11312
11444
|
async function writeTelegramBridgeConfig(nextState) {
|
|
11313
11445
|
const normalized = normalizeTelegramBridgeConfig(nextState);
|
|
11314
11446
|
const telegramConfigPath = getTelegramBridgeConfigPath();
|
|
11315
|
-
await
|
|
11447
|
+
await writeFile10(telegramConfigPath, JSON.stringify({
|
|
11316
11448
|
botToken: normalized.botToken,
|
|
11317
11449
|
chatIds: normalized.chatIds,
|
|
11318
11450
|
allowedUserIds: normalized.allowedUserIds
|
|
@@ -11367,8 +11499,8 @@ async function handleTelegramHttpRequest(req, res, url, deps) {
|
|
|
11367
11499
|
|
|
11368
11500
|
// src/server/bridge/threadPreferencesRoutes.ts
|
|
11369
11501
|
import { createReadStream as createReadStream3 } from "fs";
|
|
11370
|
-
import { readFile as readFile12, stat as
|
|
11371
|
-
import { join as
|
|
11502
|
+
import { readFile as readFile12, stat as stat12, writeFile as writeFile11 } from "fs/promises";
|
|
11503
|
+
import { join as join21 } from "path";
|
|
11372
11504
|
import { createInterface } from "readline";
|
|
11373
11505
|
var MAX_THREAD_TITLES = 500;
|
|
11374
11506
|
var MAX_REASONING_MESSAGES_PER_THREAD = 20;
|
|
@@ -11383,7 +11515,7 @@ var sessionIndexThreadTitleCacheState = {
|
|
|
11383
11515
|
cache: EMPTY_THREAD_TITLE_CACHE
|
|
11384
11516
|
};
|
|
11385
11517
|
function getCodexSessionIndexPath() {
|
|
11386
|
-
return
|
|
11518
|
+
return join21(getCodexHomeDir2(), "session_index.jsonl");
|
|
11387
11519
|
}
|
|
11388
11520
|
function normalizeThreadTitleCache(value) {
|
|
11389
11521
|
const record = asRecord2(value);
|
|
@@ -11486,7 +11618,7 @@ async function writeThreadTitleCache(cache) {
|
|
|
11486
11618
|
payload = {};
|
|
11487
11619
|
}
|
|
11488
11620
|
payload["thread-titles"] = cache;
|
|
11489
|
-
await
|
|
11621
|
+
await writeFile11(statePath, JSON.stringify(payload), "utf8");
|
|
11490
11622
|
}
|
|
11491
11623
|
async function readPinnedThreadIds() {
|
|
11492
11624
|
const statePath = getCodexGlobalStatePath();
|
|
@@ -11508,7 +11640,7 @@ async function writePinnedThreadIds(threadIds) {
|
|
|
11508
11640
|
payload = {};
|
|
11509
11641
|
}
|
|
11510
11642
|
payload[PINNED_THREAD_IDS_KEY] = normalizePinnedThreadIds(threadIds);
|
|
11511
|
-
await
|
|
11643
|
+
await writeFile11(statePath, JSON.stringify(payload), "utf8");
|
|
11512
11644
|
}
|
|
11513
11645
|
async function readThreadReasoningArchive() {
|
|
11514
11646
|
const statePath = getCodexGlobalStatePath();
|
|
@@ -11535,7 +11667,7 @@ async function writeThreadReasoningArchive(archive) {
|
|
|
11535
11667
|
} else {
|
|
11536
11668
|
delete payload[THREAD_REASONING_KEY];
|
|
11537
11669
|
}
|
|
11538
|
-
await
|
|
11670
|
+
await writeFile11(statePath, JSON.stringify(payload), "utf8");
|
|
11539
11671
|
}
|
|
11540
11672
|
async function mergeThreadReasoningArchive(threadId, messages) {
|
|
11541
11673
|
const archive = await readThreadReasoningArchive();
|
|
@@ -11592,7 +11724,7 @@ async function writeThreadTurnDurations(next) {
|
|
|
11592
11724
|
} else {
|
|
11593
11725
|
delete payload[THREAD_TURN_DURATIONS_KEY];
|
|
11594
11726
|
}
|
|
11595
|
-
await
|
|
11727
|
+
await writeFile11(statePath, JSON.stringify(payload), "utf8");
|
|
11596
11728
|
}
|
|
11597
11729
|
async function mergeThreadTurnDuration(threadId, turnId, durationMs) {
|
|
11598
11730
|
const store = await readThreadTurnDurations();
|
|
@@ -11636,7 +11768,7 @@ async function writeFirstLaunchPluginsCardDismissed(dismissed) {
|
|
|
11636
11768
|
payload = {};
|
|
11637
11769
|
}
|
|
11638
11770
|
payload[FIRST_LAUNCH_PLUGINS_CARD_DISMISSED_KEY] = dismissed === true;
|
|
11639
|
-
await
|
|
11771
|
+
await writeFile11(statePath, JSON.stringify(payload), "utf8");
|
|
11640
11772
|
}
|
|
11641
11773
|
function getSessionIndexFileSignature(stats) {
|
|
11642
11774
|
return `${String(stats.mtimeMs)}:${String(stats.size)}`;
|
|
@@ -11678,7 +11810,7 @@ async function parseThreadTitlesFromSessionIndex(sessionIndexPath) {
|
|
|
11678
11810
|
async function readThreadTitlesFromSessionIndex() {
|
|
11679
11811
|
const sessionIndexPath = getCodexSessionIndexPath();
|
|
11680
11812
|
try {
|
|
11681
|
-
const stats = await
|
|
11813
|
+
const stats = await stat12(sessionIndexPath);
|
|
11682
11814
|
const fileSignature = getSessionIndexFileSignature(stats);
|
|
11683
11815
|
if (sessionIndexThreadTitleCacheState.fileSignature === fileSignature) {
|
|
11684
11816
|
return sessionIndexThreadTitleCacheState.cache;
|
|
@@ -11859,14 +11991,14 @@ data: ${JSON.stringify({ ok: true })}
|
|
|
11859
11991
|
// src/server/bridge/projectZip.ts
|
|
11860
11992
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
11861
11993
|
import { existsSync as existsSync8 } from "fs";
|
|
11862
|
-
import { mkdir as
|
|
11863
|
-
import { basename as basename7, dirname as dirname6, extname as extname3, isAbsolute as isAbsolute7, join as
|
|
11994
|
+
import { mkdir as mkdir10, readFile as readFile14, realpath as realpath3, utimes, writeFile as writeFile13 } from "fs/promises";
|
|
11995
|
+
import { basename as basename7, dirname as dirname6, extname as extname3, isAbsolute as isAbsolute7, join as join23, relative as relative3, resolve as resolve4, sep as sep3 } from "path";
|
|
11864
11996
|
|
|
11865
11997
|
// src/server/bridge/importedSessions.ts
|
|
11866
11998
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
11867
11999
|
import { existsSync as existsSync7 } from "fs";
|
|
11868
12000
|
import { readdir as readdir8 } from "fs/promises";
|
|
11869
|
-
import { join as
|
|
12001
|
+
import { join as join22 } from "path";
|
|
11870
12002
|
async function* walkFiles(root, current = root) {
|
|
11871
12003
|
let entries;
|
|
11872
12004
|
try {
|
|
@@ -11875,7 +12007,7 @@ async function* walkFiles(root, current = root) {
|
|
|
11875
12007
|
return;
|
|
11876
12008
|
}
|
|
11877
12009
|
for (const entry of entries) {
|
|
11878
|
-
const absolutePath =
|
|
12010
|
+
const absolutePath = join22(current, entry.name);
|
|
11879
12011
|
if (entry.isDirectory()) {
|
|
11880
12012
|
yield* walkFiles(root, absolutePath);
|
|
11881
12013
|
} else if (entry.isFile()) {
|
|
@@ -11908,7 +12040,7 @@ function readSessionMetaId(raw) {
|
|
|
11908
12040
|
}
|
|
11909
12041
|
}
|
|
11910
12042
|
function getCurrentImportedSessionModelDefaults() {
|
|
11911
|
-
const fmState = ensureDefaultFreeModeStateForMissingAuthSync(
|
|
12043
|
+
const fmState = ensureDefaultFreeModeStateForMissingAuthSync(join22(getCodexHomeDir2(), FREE_MODE_STATE_FILE));
|
|
11912
12044
|
if (!fmState?.enabled) return null;
|
|
11913
12045
|
if (fmState.provider === "opencode-zen") {
|
|
11914
12046
|
return {
|
|
@@ -12038,7 +12170,7 @@ function sqlString(value) {
|
|
|
12038
12170
|
return `'${value.replace(/'/g, "''")}'`;
|
|
12039
12171
|
}
|
|
12040
12172
|
function sqliteStateDbPath() {
|
|
12041
|
-
return
|
|
12173
|
+
return join22(getCodexHomeDir2(), "state_5.sqlite");
|
|
12042
12174
|
}
|
|
12043
12175
|
function ensureImportedThreadsStateDbTable(stateDbPath) {
|
|
12044
12176
|
const sql = `
|
|
@@ -12247,7 +12379,7 @@ function mergeImportedThreadsIntoThreadListResult(result) {
|
|
|
12247
12379
|
}
|
|
12248
12380
|
|
|
12249
12381
|
// src/server/bridge/workspaceRoots.ts
|
|
12250
|
-
import { readFile as readFile13, realpath as realpath2, rm as rm7, writeFile as
|
|
12382
|
+
import { readFile as readFile13, realpath as realpath2, rm as rm7, writeFile as writeFile12 } from "fs/promises";
|
|
12251
12383
|
import { isAbsolute as isAbsolute6 } from "path";
|
|
12252
12384
|
function normalizeRemoteProjects(value) {
|
|
12253
12385
|
if (!Array.isArray(value)) return [];
|
|
@@ -12370,7 +12502,7 @@ async function writeWorkspaceRootsState(nextState) {
|
|
|
12370
12502
|
payload["electron-workspace-root-labels"] = normalizeStringRecord(state.labels);
|
|
12371
12503
|
payload["active-workspace-roots"] = normalizeStringArray(state.active);
|
|
12372
12504
|
payload["project-order"] = normalizeStringArray(state.projectOrder);
|
|
12373
|
-
await
|
|
12505
|
+
await writeFile12(statePath, JSON.stringify(payload), "utf8");
|
|
12374
12506
|
}
|
|
12375
12507
|
var workspaceRootsMutation = Promise.resolve();
|
|
12376
12508
|
function queueWorkspaceRootsMutation(mutation) {
|
|
@@ -12431,8 +12563,8 @@ async function collectProjectChatZipEntries(projectRoot) {
|
|
|
12431
12563
|
const exportedTitles = {};
|
|
12432
12564
|
const exportedThreads = {};
|
|
12433
12565
|
const roots = [
|
|
12434
|
-
{ disk:
|
|
12435
|
-
{ disk:
|
|
12566
|
+
{ disk: join23(codexHome, "sessions"), zip: ".codex-project/chats/sessions" },
|
|
12567
|
+
{ disk: join23(codexHome, "archived_sessions"), zip: ".codex-project/chats/archived_sessions" }
|
|
12436
12568
|
];
|
|
12437
12569
|
const entries = [{
|
|
12438
12570
|
path: ".codex-project/manifest.json",
|
|
@@ -12527,14 +12659,14 @@ async function importProjectZip(buffer, destinationParent) {
|
|
|
12527
12659
|
}
|
|
12528
12660
|
}
|
|
12529
12661
|
const parent = await realpath3(destinationParent);
|
|
12530
|
-
let projectPath =
|
|
12662
|
+
let projectPath = join23(parent, projectName);
|
|
12531
12663
|
for (let index = 2; existsSync8(projectPath); index += 1) {
|
|
12532
|
-
projectPath =
|
|
12664
|
+
projectPath = join23(parent, `${projectName}-${index}`);
|
|
12533
12665
|
}
|
|
12534
|
-
await
|
|
12666
|
+
await mkdir10(projectPath, { recursive: true });
|
|
12535
12667
|
let importedSessions = 0;
|
|
12536
12668
|
const importedSessionRecords = [];
|
|
12537
|
-
const importedSessionsRoot =
|
|
12669
|
+
const importedSessionsRoot = join23(getCodexHomeDir2(), "sessions");
|
|
12538
12670
|
const chatEntries = entries.filter((entry) => entry.path.startsWith(".codex-project/chats/") && !entry.isDirectory && extname3(entry.path) === ".jsonl").map((entry) => {
|
|
12539
12671
|
const importedMetadata = importedThreadMetadata.get(entry.path);
|
|
12540
12672
|
const sourceSessionRaw = entry.data.toString("utf8");
|
|
@@ -12544,10 +12676,10 @@ async function importProjectZip(buffer, destinationParent) {
|
|
|
12544
12676
|
}).sort((first, second) => second.updatedAtMs - first.updatedAtMs);
|
|
12545
12677
|
for (const [index, chatEntry] of chatEntries.entries()) {
|
|
12546
12678
|
const importedThreadId = randomUUID2();
|
|
12547
|
-
const target =
|
|
12548
|
-
await
|
|
12679
|
+
const target = join23(importedSessionsRoot, "imported", `${String(index + 1).padStart(6, "0")}-${importedThreadId}.jsonl`);
|
|
12680
|
+
await mkdir10(dirname6(target), { recursive: true });
|
|
12549
12681
|
const importedSessionRaw = rewriteImportedSession(chatEntry.sourceSessionRaw, projectPath, importedThreadId);
|
|
12550
|
-
await
|
|
12682
|
+
await writeFile13(target, importedSessionRaw, "utf8");
|
|
12551
12683
|
const importedRecord = readImportedSessionRecord(importedSessionRaw, target, projectPath, importedThreadId, chatEntry.importedMetadata?.title ?? "");
|
|
12552
12684
|
if (chatEntry.updatedAtMs > 0) {
|
|
12553
12685
|
importedRecord.updatedAtMs = chatEntry.updatedAtMs;
|
|
@@ -12568,13 +12700,13 @@ async function importProjectZip(buffer, destinationParent) {
|
|
|
12568
12700
|
if (entry.path.startsWith(".codex-project/chats/")) {
|
|
12569
12701
|
continue;
|
|
12570
12702
|
}
|
|
12571
|
-
const target =
|
|
12703
|
+
const target = join23(projectPath, entry.path);
|
|
12572
12704
|
if (!isSameOrDescendantPath(target, projectPath)) throw new Error("Project ZIP contains an unsafe path");
|
|
12573
12705
|
if (entry.isDirectory) {
|
|
12574
|
-
await
|
|
12706
|
+
await mkdir10(target, { recursive: true });
|
|
12575
12707
|
} else {
|
|
12576
|
-
await
|
|
12577
|
-
await
|
|
12708
|
+
await mkdir10(dirname6(target), { recursive: true });
|
|
12709
|
+
await writeFile13(target, entry.data);
|
|
12578
12710
|
}
|
|
12579
12711
|
}
|
|
12580
12712
|
await persistWorkspaceRoot(projectPath, projectName);
|
|
@@ -12582,7 +12714,7 @@ async function importProjectZip(buffer, destinationParent) {
|
|
|
12582
12714
|
}
|
|
12583
12715
|
|
|
12584
12716
|
// src/server/bridge/threadQueueState.ts
|
|
12585
|
-
import { readFile as readFile15, writeFile as
|
|
12717
|
+
import { readFile as readFile15, writeFile as writeFile14 } from "fs/promises";
|
|
12586
12718
|
var THREAD_QUEUE_STATE_KEY = "thread-queue-state";
|
|
12587
12719
|
function normalizeStoredQueuedMessage(value) {
|
|
12588
12720
|
const record = asRecord2(value);
|
|
@@ -12662,7 +12794,7 @@ async function writeThreadQueueStateUnlocked(nextState) {
|
|
|
12662
12794
|
} else {
|
|
12663
12795
|
delete payload[THREAD_QUEUE_STATE_KEY];
|
|
12664
12796
|
}
|
|
12665
|
-
await
|
|
12797
|
+
await writeFile14(statePath, JSON.stringify(payload), "utf8");
|
|
12666
12798
|
}
|
|
12667
12799
|
async function withThreadQueueStateUpdate(update) {
|
|
12668
12800
|
const run = threadQueueMutationChain.then(async () => {
|
|
@@ -12938,10 +13070,10 @@ function isTurnCompletedNotification(notification) {
|
|
|
12938
13070
|
|
|
12939
13071
|
// src/server/bridge/approvalPolicy.ts
|
|
12940
13072
|
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "fs";
|
|
12941
|
-
import { mkdir as
|
|
12942
|
-
import { dirname as dirname7, join as
|
|
13073
|
+
import { mkdir as mkdir11, readFile as readFile16, writeFile as writeFile15 } from "fs/promises";
|
|
13074
|
+
import { dirname as dirname7, join as join24 } from "path";
|
|
12943
13075
|
function getCodexConfigPath() {
|
|
12944
|
-
return
|
|
13076
|
+
return join24(getCodexHomeDir2(), "config.toml");
|
|
12945
13077
|
}
|
|
12946
13078
|
async function resolveEffectiveApprovalPolicy() {
|
|
12947
13079
|
const envPolicy = parseApprovalPolicy(process.env.CODEXUI_APPROVAL_POLICY ?? "");
|
|
@@ -12970,10 +13102,10 @@ function readApprovalPolicyFromConfigFile() {
|
|
|
12970
13102
|
var APPROVAL_POLICY_ASSIGNMENT = /^approval_policy\s*=/u;
|
|
12971
13103
|
async function writeApprovalPolicyToConfigFile(policy) {
|
|
12972
13104
|
const configPath = getCodexConfigPath();
|
|
12973
|
-
await
|
|
13105
|
+
await mkdir11(dirname7(configPath), { recursive: true });
|
|
12974
13106
|
const policyLine = `approval_policy = "${policy}"`;
|
|
12975
13107
|
if (!existsSync9(configPath)) {
|
|
12976
|
-
await
|
|
13108
|
+
await writeFile15(configPath, `${policyLine}
|
|
12977
13109
|
`, "utf8");
|
|
12978
13110
|
return;
|
|
12979
13111
|
}
|
|
@@ -12985,14 +13117,14 @@ async function writeApprovalPolicyToConfigFile(policy) {
|
|
|
12985
13117
|
${body}
|
|
12986
13118
|
` : `${policyLine}
|
|
12987
13119
|
`;
|
|
12988
|
-
await
|
|
13120
|
+
await writeFile15(configPath, nextContent, "utf8");
|
|
12989
13121
|
}
|
|
12990
13122
|
|
|
12991
13123
|
// src/server/bridge/inlineImages.ts
|
|
12992
|
-
import { createHash as
|
|
12993
|
-
import { mkdir as
|
|
12994
|
-
import { tmpdir as
|
|
12995
|
-
import { join as
|
|
13124
|
+
import { createHash as createHash3 } from "crypto";
|
|
13125
|
+
import { mkdir as mkdir12, stat as stat13, writeFile as writeFile16 } from "fs/promises";
|
|
13126
|
+
import { tmpdir as tmpdir6 } from "os";
|
|
13127
|
+
import { join as join25 } from "path";
|
|
12996
13128
|
var THREAD_METHODS_WITH_TURNS2 = /* @__PURE__ */ new Set(["thread/read", "thread/resume", "thread/fork", "thread/rollback", "thread/revert"]);
|
|
12997
13129
|
function isInlineDataUrl(value) {
|
|
12998
13130
|
return /^data:/iu.test(value.trim());
|
|
@@ -13081,16 +13213,16 @@ async function persistInlineDataUrlToLocalFile(dataUrl, baseName) {
|
|
|
13081
13213
|
return null;
|
|
13082
13214
|
}
|
|
13083
13215
|
if (bytes.length === 0) return null;
|
|
13084
|
-
const hash =
|
|
13216
|
+
const hash = createHash3("sha1").update(bytes).digest("hex");
|
|
13085
13217
|
const ext = extensionFromMimeType(mimeType);
|
|
13086
|
-
const mediaDir =
|
|
13087
|
-
await
|
|
13218
|
+
const mediaDir = join25(tmpdir6(), "codex-web-inline-media");
|
|
13219
|
+
await mkdir12(mediaDir, { recursive: true });
|
|
13088
13220
|
const fileName = `${baseName}-${hash}${ext}`;
|
|
13089
|
-
const filePath =
|
|
13221
|
+
const filePath = join25(mediaDir, fileName);
|
|
13090
13222
|
try {
|
|
13091
|
-
await
|
|
13223
|
+
await stat13(filePath);
|
|
13092
13224
|
} catch {
|
|
13093
|
-
await
|
|
13225
|
+
await writeFile16(filePath, bytes);
|
|
13094
13226
|
}
|
|
13095
13227
|
return filePath;
|
|
13096
13228
|
}
|
|
@@ -13222,10 +13354,11 @@ async function sanitizeInlinePayloadDeep(value, context) {
|
|
|
13222
13354
|
}
|
|
13223
13355
|
async function sanitizeThreadTurnsInlinePayloads(method, result) {
|
|
13224
13356
|
if (!THREAD_METHODS_WITH_TURNS2.has(method)) return result;
|
|
13225
|
-
const
|
|
13357
|
+
const slimmed = await slimThreadTurnsPayload(method, result);
|
|
13358
|
+
const record = asRecord2(slimmed);
|
|
13226
13359
|
const thread = asRecord2(record?.thread);
|
|
13227
13360
|
const turns = Array.isArray(thread?.turns) ? thread.turns : null;
|
|
13228
|
-
if (!record || !thread || !turns || turns.length === 0) return
|
|
13361
|
+
if (!record || !thread || !turns || turns.length === 0) return slimmed;
|
|
13229
13362
|
let changed = false;
|
|
13230
13363
|
const nextTurns = [];
|
|
13231
13364
|
for (let turnIndex = 0; turnIndex < turns.length; turnIndex += 1) {
|
|
@@ -13269,7 +13402,7 @@ async function sanitizeThreadTurnsInlinePayloads(method, result) {
|
|
|
13269
13402
|
items: nextItems
|
|
13270
13403
|
});
|
|
13271
13404
|
}
|
|
13272
|
-
if (!changed) return
|
|
13405
|
+
if (!changed) return slimmed;
|
|
13273
13406
|
return {
|
|
13274
13407
|
...record,
|
|
13275
13408
|
thread: {
|
|
@@ -13279,6 +13412,70 @@ async function sanitizeThreadTurnsInlinePayloads(method, result) {
|
|
|
13279
13412
|
};
|
|
13280
13413
|
}
|
|
13281
13414
|
|
|
13415
|
+
// src/server/bridge/threadReadCache.ts
|
|
13416
|
+
var THREAD_READ_RESULT_CACHE_TTL_MS = 2e4;
|
|
13417
|
+
var THREAD_READ_RESULT_CACHE_MAX_ENTRIES = 6;
|
|
13418
|
+
var THREAD_READ_INVALIDATING_METHOD_PATTERN = /^(?:turn\/|thread\/(?:start|fork|rollback|revert|resume|archive|unarchive|delete|compact|name\/))/u;
|
|
13419
|
+
function threadReadInvalidatesCache(method) {
|
|
13420
|
+
return THREAD_READ_INVALIDATING_METHOD_PATTERN.test(method);
|
|
13421
|
+
}
|
|
13422
|
+
function buildThreadReadCacheKey(params) {
|
|
13423
|
+
const parts = [];
|
|
13424
|
+
for (const key of Object.keys(params).sort()) {
|
|
13425
|
+
if (key === "threadId") continue;
|
|
13426
|
+
const value = params[key];
|
|
13427
|
+
if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
13428
|
+
parts.push(`${key}=${String(value)}`);
|
|
13429
|
+
continue;
|
|
13430
|
+
}
|
|
13431
|
+
return "";
|
|
13432
|
+
}
|
|
13433
|
+
return parts.join("&");
|
|
13434
|
+
}
|
|
13435
|
+
var ThreadReadResultCache = class {
|
|
13436
|
+
constructor() {
|
|
13437
|
+
this.entries = /* @__PURE__ */ new Map();
|
|
13438
|
+
}
|
|
13439
|
+
get size() {
|
|
13440
|
+
return this.entries.size;
|
|
13441
|
+
}
|
|
13442
|
+
get(threadId, params) {
|
|
13443
|
+
if (!threadId || !params) return null;
|
|
13444
|
+
const cacheKey = buildThreadReadCacheKey(params);
|
|
13445
|
+
if (!cacheKey) return null;
|
|
13446
|
+
const mapKey = `${threadId}\0${cacheKey}`;
|
|
13447
|
+
const entry = this.entries.get(mapKey);
|
|
13448
|
+
if (!entry) return null;
|
|
13449
|
+
if (entry.expiresAt <= Date.now()) {
|
|
13450
|
+
this.entries.delete(mapKey);
|
|
13451
|
+
return null;
|
|
13452
|
+
}
|
|
13453
|
+
return entry.result;
|
|
13454
|
+
}
|
|
13455
|
+
set(threadId, params, result, now = Date.now()) {
|
|
13456
|
+
if (!threadId || !params) return;
|
|
13457
|
+
const cacheKey = buildThreadReadCacheKey(params);
|
|
13458
|
+
if (!cacheKey) return;
|
|
13459
|
+
const mapKey = `${threadId}\0${cacheKey}`;
|
|
13460
|
+
this.entries.delete(mapKey);
|
|
13461
|
+
this.entries.set(mapKey, { threadId, result, expiresAt: now + THREAD_READ_RESULT_CACHE_TTL_MS });
|
|
13462
|
+
while (this.entries.size > THREAD_READ_RESULT_CACHE_MAX_ENTRIES) {
|
|
13463
|
+
const oldestKey = this.entries.keys().next();
|
|
13464
|
+
if (oldestKey.done) break;
|
|
13465
|
+
this.entries.delete(oldestKey.value);
|
|
13466
|
+
}
|
|
13467
|
+
}
|
|
13468
|
+
invalidate(threadId) {
|
|
13469
|
+
if (!threadId) {
|
|
13470
|
+
this.entries.clear();
|
|
13471
|
+
return;
|
|
13472
|
+
}
|
|
13473
|
+
for (const [mapKey, entry] of this.entries) {
|
|
13474
|
+
if (entry.threadId === threadId) this.entries.delete(mapKey);
|
|
13475
|
+
}
|
|
13476
|
+
}
|
|
13477
|
+
};
|
|
13478
|
+
|
|
13282
13479
|
// src/server/bridge/apiPerfLogging.ts
|
|
13283
13480
|
import { readFileSync as readFileSync6 } from "fs";
|
|
13284
13481
|
var API_PERF_LOGGING_ENV_KEY = "CODEXUI_API_PERF_LOGGING";
|
|
@@ -13354,12 +13551,12 @@ function getChunkByteLength(chunk, encoding) {
|
|
|
13354
13551
|
}
|
|
13355
13552
|
|
|
13356
13553
|
// src/server/bridge/terminal.ts
|
|
13357
|
-
import { readdir as readdir9, readFile as readFile17, stat as
|
|
13358
|
-
import { isAbsolute as isAbsolute8, join as
|
|
13554
|
+
import { readdir as readdir9, readFile as readFile17, stat as stat14 } from "fs/promises";
|
|
13555
|
+
import { isAbsolute as isAbsolute8, join as join26, resolve as resolve5 } from "path";
|
|
13359
13556
|
import { existsSync as existsSync10 } from "fs";
|
|
13360
13557
|
async function listTerminalQuickCommands(cwd) {
|
|
13361
13558
|
const normalizedCwd = isAbsolute8(cwd) ? cwd : resolve5(cwd);
|
|
13362
|
-
const info = await
|
|
13559
|
+
const info = await stat14(normalizedCwd);
|
|
13363
13560
|
if (!info.isDirectory()) {
|
|
13364
13561
|
throw new Error("Terminal cwd is not a directory");
|
|
13365
13562
|
}
|
|
@@ -13378,7 +13575,7 @@ async function listTerminalQuickCommands(cwd) {
|
|
|
13378
13575
|
}
|
|
13379
13576
|
async function addPackageJsonCommands(cwd, addCommand) {
|
|
13380
13577
|
try {
|
|
13381
|
-
const raw = await readFile17(
|
|
13578
|
+
const raw = await readFile17(join26(cwd, "package.json"), "utf8");
|
|
13382
13579
|
const parsed = JSON.parse(raw);
|
|
13383
13580
|
const record = asRecord2(parsed);
|
|
13384
13581
|
const scripts = asRecord2(record?.scripts);
|
|
@@ -13397,7 +13594,7 @@ async function addPackageJsonCommands(cwd, addCommand) {
|
|
|
13397
13594
|
}
|
|
13398
13595
|
}
|
|
13399
13596
|
async function addMakefileCommands(cwd, addCommand) {
|
|
13400
|
-
const makefilePath = existsSync10(
|
|
13597
|
+
const makefilePath = existsSync10(join26(cwd, "Makefile")) ? join26(cwd, "Makefile") : existsSync10(join26(cwd, "makefile")) ? join26(cwd, "makefile") : "";
|
|
13401
13598
|
if (!makefilePath) return;
|
|
13402
13599
|
try {
|
|
13403
13600
|
const raw = await readFile17(makefilePath, "utf8");
|
|
@@ -13420,7 +13617,7 @@ async function addRootScriptCommands(cwd, addCommand) {
|
|
|
13420
13617
|
await addScriptFileCommands(cwd, ".", addCommand);
|
|
13421
13618
|
}
|
|
13422
13619
|
async function addScriptsDirectoryCommands(cwd, addCommand) {
|
|
13423
|
-
await addScriptFileCommands(
|
|
13620
|
+
await addScriptFileCommands(join26(cwd, "scripts"), "./scripts", addCommand);
|
|
13424
13621
|
}
|
|
13425
13622
|
async function addScriptFileCommands(directory, commandPrefix, addCommand) {
|
|
13426
13623
|
try {
|
|
@@ -13439,9 +13636,9 @@ async function addScriptFileCommands(directory, commandPrefix, addCommand) {
|
|
|
13439
13636
|
}
|
|
13440
13637
|
}
|
|
13441
13638
|
function resolvePackageManager(cwd) {
|
|
13442
|
-
if (existsSync10(
|
|
13443
|
-
if (existsSync10(
|
|
13444
|
-
if (existsSync10(
|
|
13639
|
+
if (existsSync10(join26(cwd, "pnpm-lock.yaml"))) return "pnpm";
|
|
13640
|
+
if (existsSync10(join26(cwd, "yarn.lock"))) return "yarn";
|
|
13641
|
+
if (existsSync10(join26(cwd, "bun.lock")) || existsSync10(join26(cwd, "bun.lockb"))) return "bun";
|
|
13445
13642
|
return "npm";
|
|
13446
13643
|
}
|
|
13447
13644
|
function formatPackageScriptCommand(packageManager, scriptName) {
|
|
@@ -13454,13 +13651,13 @@ function formatPackageScriptCommand(packageManager, scriptName) {
|
|
|
13454
13651
|
|
|
13455
13652
|
// src/server/bridge/routes.ts
|
|
13456
13653
|
import { randomBytes as randomBytes5 } from "crypto";
|
|
13457
|
-
import { stat as
|
|
13458
|
-
import { basename as basename8, dirname as dirname9, isAbsolute as isAbsolute10, join as
|
|
13654
|
+
import { stat as stat16, mkdir as mkdir14 } from "fs/promises";
|
|
13655
|
+
import { basename as basename8, dirname as dirname9, isAbsolute as isAbsolute10, join as join28, resolve as resolve6 } from "path";
|
|
13459
13656
|
|
|
13460
13657
|
// src/server/bridge/git.ts
|
|
13461
13658
|
import { existsSync as existsSync11 } from "fs";
|
|
13462
|
-
import { mkdir as
|
|
13463
|
-
import { dirname as dirname8, isAbsolute as isAbsolute9, join as
|
|
13659
|
+
import { mkdir as mkdir13, rename as rename3, rm as rm8, stat as stat15, writeFile as writeFile17 } from "fs/promises";
|
|
13660
|
+
import { dirname as dirname8, isAbsolute as isAbsolute9, join as join27 } from "path";
|
|
13464
13661
|
function isMissingHeadError2(error) {
|
|
13465
13662
|
const message = getErrorMessage2(error, "").toLowerCase();
|
|
13466
13663
|
return message.includes("not a valid object name: 'head'") || message.includes("not a valid object name: head") || message.includes("invalid reference: head");
|
|
@@ -13470,11 +13667,11 @@ function isNotGitRepositoryError2(error) {
|
|
|
13470
13667
|
return message.includes("not a git repository") || message.includes("fatal: not a git repository");
|
|
13471
13668
|
}
|
|
13472
13669
|
async function ensureRepoHasInitialCommit(repoRoot) {
|
|
13473
|
-
const agentsPath =
|
|
13670
|
+
const agentsPath = join27(repoRoot, "AGENTS.md");
|
|
13474
13671
|
try {
|
|
13475
|
-
await
|
|
13672
|
+
await stat15(agentsPath);
|
|
13476
13673
|
} catch {
|
|
13477
|
-
await
|
|
13674
|
+
await writeFile17(agentsPath, "", "utf8");
|
|
13478
13675
|
}
|
|
13479
13676
|
await runCommand("git", ["add", "AGENTS.md"], { cwd: repoRoot });
|
|
13480
13677
|
await runCommand(
|
|
@@ -13505,7 +13702,7 @@ function isSafeGitRelativePath2(filePath) {
|
|
|
13505
13702
|
return Boolean(filePath) && !isAbsolute9(filePath) && !filePath.split("/").includes("..");
|
|
13506
13703
|
}
|
|
13507
13704
|
function resolveGitRelativePath(repoRoot, filePath) {
|
|
13508
|
-
return
|
|
13705
|
+
return join27(repoRoot, ...filePath.split("/"));
|
|
13509
13706
|
}
|
|
13510
13707
|
function gitPathsConflict(left, right) {
|
|
13511
13708
|
return left === right || left.startsWith(`${right}/`) || right.startsWith(`${left}/`);
|
|
@@ -13525,7 +13722,7 @@ async function rollbackPreservedUntrackedFiles(entries) {
|
|
|
13525
13722
|
for (const entry of entries.slice().reverse()) {
|
|
13526
13723
|
try {
|
|
13527
13724
|
if (existsSync11(entry.backupPath) && !existsSync11(entry.sourcePath)) {
|
|
13528
|
-
await
|
|
13725
|
+
await mkdir13(dirname8(entry.sourcePath), { recursive: true });
|
|
13529
13726
|
await rename3(entry.backupPath, entry.sourcePath);
|
|
13530
13727
|
}
|
|
13531
13728
|
} catch {
|
|
@@ -13540,12 +13737,12 @@ async function preserveUntrackedFilesForGitTarget(repoRoot, targetRef) {
|
|
|
13540
13737
|
const targetPaths = splitGitPathList2(targetTreeRaw);
|
|
13541
13738
|
const conflictingUntrackedPaths = splitGitPathList2(untrackedRaw).filter((filePath) => isSafeGitRelativePath2(filePath) && targetPaths.some((targetPath) => gitPathsConflict(filePath, targetPath)));
|
|
13542
13739
|
if (conflictingUntrackedPaths.length === 0) return [];
|
|
13543
|
-
const backupRoot =
|
|
13740
|
+
const backupRoot = join27(repoRoot, HEADER_GIT_UNTRACKED_BACKUP_DIR, (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-"));
|
|
13544
13741
|
const movedFiles = [];
|
|
13545
13742
|
for (const filePath of conflictingUntrackedPaths) {
|
|
13546
13743
|
const sourcePath = resolveGitRelativePath(repoRoot, filePath);
|
|
13547
|
-
const backupPath =
|
|
13548
|
-
await
|
|
13744
|
+
const backupPath = join27(backupRoot, ...filePath.split("/"));
|
|
13745
|
+
await mkdir13(dirname8(backupPath), { recursive: true });
|
|
13549
13746
|
await rename3(sourcePath, backupPath);
|
|
13550
13747
|
movedFiles.push({ filePath, sourcePath, backupPath });
|
|
13551
13748
|
await removeEmptyGitRelativeParents(repoRoot, filePath);
|
|
@@ -13704,7 +13901,7 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13704
13901
|
}
|
|
13705
13902
|
const sourceCwd = isAbsolute10(rawSourceCwd) ? rawSourceCwd : resolve6(rawSourceCwd);
|
|
13706
13903
|
try {
|
|
13707
|
-
const sourceInfo = await
|
|
13904
|
+
const sourceInfo = await stat16(sourceCwd);
|
|
13708
13905
|
if (!sourceInfo.isDirectory()) {
|
|
13709
13906
|
setJson2(res, 400, { error: "sourceCwd is not a directory" });
|
|
13710
13907
|
return true;
|
|
@@ -13723,21 +13920,21 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13723
13920
|
gitRoot = await runCommandCapture("git", ["rev-parse", "--show-toplevel"], { cwd: sourceCwd });
|
|
13724
13921
|
}
|
|
13725
13922
|
const repoName = basename8(gitRoot) || "repo";
|
|
13726
|
-
const worktreesRoot =
|
|
13727
|
-
await
|
|
13923
|
+
const worktreesRoot = join28(getCodexHomeDir2(), "worktrees");
|
|
13924
|
+
await mkdir14(worktreesRoot, { recursive: true });
|
|
13728
13925
|
let worktreeId = "";
|
|
13729
13926
|
let worktreeParent = "";
|
|
13730
13927
|
let worktreeCwd = "";
|
|
13731
13928
|
for (let attempt = 0; attempt < 12; attempt += 1) {
|
|
13732
13929
|
const candidate = randomBytes5(2).toString("hex");
|
|
13733
|
-
const parent =
|
|
13930
|
+
const parent = join28(worktreesRoot, candidate);
|
|
13734
13931
|
try {
|
|
13735
|
-
await
|
|
13932
|
+
await stat16(parent);
|
|
13736
13933
|
continue;
|
|
13737
13934
|
} catch {
|
|
13738
13935
|
worktreeId = candidate;
|
|
13739
13936
|
worktreeParent = parent;
|
|
13740
|
-
worktreeCwd =
|
|
13937
|
+
worktreeCwd = join28(parent, repoName);
|
|
13741
13938
|
break;
|
|
13742
13939
|
}
|
|
13743
13940
|
}
|
|
@@ -13745,7 +13942,7 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13745
13942
|
throw new Error("Failed to allocate a unique worktree id");
|
|
13746
13943
|
}
|
|
13747
13944
|
const startPoint = baseBranch || "HEAD";
|
|
13748
|
-
await
|
|
13945
|
+
await mkdir14(worktreeParent, { recursive: true });
|
|
13749
13946
|
try {
|
|
13750
13947
|
await runCommand("git", ["worktree", "add", "--detach", worktreeCwd, startPoint], { cwd: gitRoot });
|
|
13751
13948
|
} catch (error) {
|
|
@@ -13789,7 +13986,7 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13789
13986
|
}
|
|
13790
13987
|
const sourceCwd = isAbsolute10(rawSourceCwd) ? rawSourceCwd : resolve6(rawSourceCwd);
|
|
13791
13988
|
try {
|
|
13792
|
-
const sourceInfo = await
|
|
13989
|
+
const sourceInfo = await stat16(sourceCwd);
|
|
13793
13990
|
if (!sourceInfo.isDirectory()) {
|
|
13794
13991
|
setJson2(res, 400, { error: "sourceCwd is not a directory" });
|
|
13795
13992
|
return true;
|
|
@@ -13807,9 +14004,9 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13807
14004
|
await runCommand("git", ["init"], { cwd: sourceCwd });
|
|
13808
14005
|
gitRoot = await runCommandCapture("git", ["rev-parse", "--show-toplevel"], { cwd: sourceCwd });
|
|
13809
14006
|
}
|
|
13810
|
-
const worktreeCwd =
|
|
14007
|
+
const worktreeCwd = join28(dirname9(gitRoot), rawWorktreeName);
|
|
13811
14008
|
try {
|
|
13812
|
-
await
|
|
14009
|
+
await stat16(worktreeCwd);
|
|
13813
14010
|
setJson2(res, 409, { error: "Worktree folder already exists" });
|
|
13814
14011
|
return true;
|
|
13815
14012
|
} catch {
|
|
@@ -13848,7 +14045,7 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13848
14045
|
}
|
|
13849
14046
|
const sourceCwd = isAbsolute10(rawSourceCwd) ? rawSourceCwd : resolve6(rawSourceCwd);
|
|
13850
14047
|
try {
|
|
13851
|
-
const sourceInfo = await
|
|
14048
|
+
const sourceInfo = await stat16(sourceCwd);
|
|
13852
14049
|
if (!sourceInfo.isDirectory()) {
|
|
13853
14050
|
setJson2(res, 400, { error: "sourceCwd is not a directory" });
|
|
13854
14051
|
return true;
|
|
@@ -13903,7 +14100,7 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13903
14100
|
}
|
|
13904
14101
|
const cwd = isAbsolute10(rawCwd) ? rawCwd : resolve6(rawCwd);
|
|
13905
14102
|
try {
|
|
13906
|
-
const cwdInfo = await
|
|
14103
|
+
const cwdInfo = await stat16(cwd);
|
|
13907
14104
|
if (!cwdInfo.isDirectory()) {
|
|
13908
14105
|
setJson2(res, 400, { error: "cwd is not a directory" });
|
|
13909
14106
|
return true;
|
|
@@ -13979,7 +14176,7 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
13979
14176
|
}
|
|
13980
14177
|
const cwd = isAbsolute10(rawCwd) ? rawCwd : resolve6(rawCwd);
|
|
13981
14178
|
try {
|
|
13982
|
-
const cwdInfo = await
|
|
14179
|
+
const cwdInfo = await stat16(cwd);
|
|
13983
14180
|
if (!cwdInfo.isDirectory()) {
|
|
13984
14181
|
setJson2(res, 400, { error: "cwd is not a directory" });
|
|
13985
14182
|
return true;
|
|
@@ -14029,7 +14226,7 @@ async function handleGitWorktreeHttpRequest(req, res, url, deps) {
|
|
|
14029
14226
|
}
|
|
14030
14227
|
const cwd = isAbsolute10(rawCwd) ? rawCwd : resolve6(rawCwd);
|
|
14031
14228
|
try {
|
|
14032
|
-
const cwdInfo = await
|
|
14229
|
+
const cwdInfo = await stat16(cwd);
|
|
14033
14230
|
if (!cwdInfo.isDirectory()) {
|
|
14034
14231
|
setJson2(res, 400, { error: "cwd is not a directory" });
|
|
14035
14232
|
return true;
|
|
@@ -14251,6 +14448,7 @@ var AppServerProcess = class {
|
|
|
14251
14448
|
this.lastThreadReadSnapshotByThreadId = /* @__PURE__ */ new Map();
|
|
14252
14449
|
this.threadTurnPageReadCacheByThreadId = /* @__PURE__ */ new Map();
|
|
14253
14450
|
this.threadTurnPageReadPromiseByThreadId = /* @__PURE__ */ new Map();
|
|
14451
|
+
this.threadReadResultCache = new ThreadReadResultCache();
|
|
14254
14452
|
this.capturedItemsByThreadId = /* @__PURE__ */ new Map();
|
|
14255
14453
|
this.liveStateCache = /* @__PURE__ */ new Map();
|
|
14256
14454
|
this.chatgptAuthRefreshPromise = null;
|
|
@@ -14268,7 +14466,7 @@ var AppServerProcess = class {
|
|
|
14268
14466
|
let extraEnv = {};
|
|
14269
14467
|
const serverPort = parseInt(process.env.CODEXUI_SERVER_PORT ?? "", 10) || void 0;
|
|
14270
14468
|
args.push(...getProviderCompatibilityConfigArgs(serverPort));
|
|
14271
|
-
const statePath =
|
|
14469
|
+
const statePath = join29(getCodexHomeDir2(), FREE_MODE_STATE_FILE);
|
|
14272
14470
|
try {
|
|
14273
14471
|
const state = ensureDefaultFreeModeStateForMissingAuthSync(statePath);
|
|
14274
14472
|
if (state) {
|
|
@@ -14331,6 +14529,7 @@ var AppServerProcess = class {
|
|
|
14331
14529
|
this.initialized = false;
|
|
14332
14530
|
this.initializePromise = null;
|
|
14333
14531
|
this.readBuffer = "";
|
|
14532
|
+
this.invalidateThreadReadResultCache();
|
|
14334
14533
|
});
|
|
14335
14534
|
}
|
|
14336
14535
|
sendLine(payload) {
|
|
@@ -14376,6 +14575,7 @@ var AppServerProcess = class {
|
|
|
14376
14575
|
if (nThreadId) {
|
|
14377
14576
|
this.invalidateLiveStateCache(nThreadId);
|
|
14378
14577
|
this.threadTurnPageReadCacheByThreadId.delete(nThreadId);
|
|
14578
|
+
this.invalidateThreadReadResultCache(nThreadId);
|
|
14379
14579
|
}
|
|
14380
14580
|
for (const listener of this.notificationListeners) {
|
|
14381
14581
|
listener(notification);
|
|
@@ -14450,6 +14650,20 @@ var AppServerProcess = class {
|
|
|
14450
14650
|
cacheLiveState(threadId, data, turnCount, sessionSize) {
|
|
14451
14651
|
this.liveStateCache.set(threadId, { data, turnCount, sessionSize });
|
|
14452
14652
|
}
|
|
14653
|
+
/**
|
|
14654
|
+
* Cache a post-pipeline `thread/read` result. Called by the RPC handler with
|
|
14655
|
+
* exactly what was sent to the client, so a hit skips the app-server call, the
|
|
14656
|
+
* 10-turn trim, the session-log command merge and the payload slimming.
|
|
14657
|
+
*/
|
|
14658
|
+
cacheThreadReadResult(threadId, params, result) {
|
|
14659
|
+
this.threadReadResultCache.set(threadId, asRecord2(params), result);
|
|
14660
|
+
}
|
|
14661
|
+
getCachedThreadReadResult(threadId, params) {
|
|
14662
|
+
return this.threadReadResultCache.get(threadId, asRecord2(params));
|
|
14663
|
+
}
|
|
14664
|
+
invalidateThreadReadResultCache(threadId) {
|
|
14665
|
+
this.threadReadResultCache.invalidate(threadId);
|
|
14666
|
+
}
|
|
14453
14667
|
getCachedLiveState(threadId, turnCount, sessionSize) {
|
|
14454
14668
|
const cached = this.liveStateCache.get(threadId);
|
|
14455
14669
|
if (!cached) return null;
|
|
@@ -14645,9 +14859,45 @@ var AppServerProcess = class {
|
|
|
14645
14859
|
}
|
|
14646
14860
|
async rpc(method, params) {
|
|
14647
14861
|
this.disposeIfConfigChanged();
|
|
14862
|
+
if (threadReadInvalidatesCache(method)) {
|
|
14863
|
+
const paramsRecord = asRecord2(params);
|
|
14864
|
+
const threadId = readNonEmptyString(paramsRecord?.threadId);
|
|
14865
|
+
this.invalidateThreadReadResultCache(threadId || void 0);
|
|
14866
|
+
}
|
|
14648
14867
|
await this.ensureInitialized();
|
|
14649
14868
|
return this.call(method, params);
|
|
14650
14869
|
}
|
|
14870
|
+
/**
|
|
14871
|
+
* Spawn + initialize the app-server ahead of the first user request.
|
|
14872
|
+
*
|
|
14873
|
+
* Measured cold start: the first page load fires ~20 startup RPCs that all
|
|
14874
|
+
* queue behind one just-spawned app-server (head-of-line blocking —
|
|
14875
|
+
* free-mode/status 2619ms, meta/methods 1306ms, thread/queue-state 1287ms), so
|
|
14876
|
+
* `thread/list` costs 934ms cold vs 63ms warm.
|
|
14877
|
+
*
|
|
14878
|
+
* Honest scope of the win (measured 2026-09-11, dev server): the frontend's
|
|
14879
|
+
* burst arrives ~1-3s after the Vite dev server reports ready, while
|
|
14880
|
+
* `ensureInitialized()` here needs ~2.1s. So warm-up only *overlaps* part of
|
|
14881
|
+
* the init — the first request still waits for the remainder (observed first
|
|
14882
|
+
* GET 1401ms, meta/methods 2118ms). It does not remove the cold start from
|
|
14883
|
+
* first paint; it removes it from every *later* load, and it is what makes the
|
|
14884
|
+
* warm path (all endpoints <=70ms) reachable without an artificial first
|
|
14885
|
+
* request. Treat this as queue-shifting, not as a first-paint fix.
|
|
14886
|
+
*
|
|
14887
|
+
* Side effect to be aware of: this spawns the app-server eagerly whenever the
|
|
14888
|
+
* bridge is created (dev server and packaged `createServer` alike) instead of
|
|
14889
|
+
* lazily on the first request.
|
|
14890
|
+
*
|
|
14891
|
+
* Best-effort: a failure here must not break the bridge, because the next
|
|
14892
|
+
* request retries through the normal ensureInitialized path.
|
|
14893
|
+
*/
|
|
14894
|
+
async warmUp() {
|
|
14895
|
+
try {
|
|
14896
|
+
this.disposeIfConfigChanged();
|
|
14897
|
+
await this.ensureInitialized();
|
|
14898
|
+
} catch {
|
|
14899
|
+
}
|
|
14900
|
+
}
|
|
14651
14901
|
onNotification(listener) {
|
|
14652
14902
|
this.notificationListeners.add(listener);
|
|
14653
14903
|
return () => {
|
|
@@ -14688,6 +14938,7 @@ var AppServerProcess = class {
|
|
|
14688
14938
|
this.initializePromise = null;
|
|
14689
14939
|
this.activeConfigSignature = "";
|
|
14690
14940
|
this.readBuffer = "";
|
|
14941
|
+
this.invalidateThreadReadResultCache();
|
|
14691
14942
|
const failure = new Error("codex app-server stopped");
|
|
14692
14943
|
for (const request of this.pending.values()) {
|
|
14693
14944
|
request.reject(failure);
|
|
@@ -14995,9 +15246,9 @@ var MethodCatalog = class {
|
|
|
14995
15246
|
if (this.methodCache) {
|
|
14996
15247
|
return this.methodCache;
|
|
14997
15248
|
}
|
|
14998
|
-
const outDir = await mkdtemp4(
|
|
15249
|
+
const outDir = await mkdtemp4(join29(tmpdir7(), "codex-web-local-schema-"));
|
|
14999
15250
|
await this.runGenerateSchemaCommand(outDir);
|
|
15000
|
-
const clientRequestPath =
|
|
15251
|
+
const clientRequestPath = join29(outDir, "ClientRequest.json");
|
|
15001
15252
|
const raw = await readFile18(clientRequestPath, "utf8");
|
|
15002
15253
|
const parsed = JSON.parse(raw);
|
|
15003
15254
|
const methods = this.extractMethodsFromClientRequest(parsed);
|
|
@@ -15008,9 +15259,9 @@ var MethodCatalog = class {
|
|
|
15008
15259
|
if (this.notificationCache) {
|
|
15009
15260
|
return this.notificationCache;
|
|
15010
15261
|
}
|
|
15011
|
-
const outDir = await mkdtemp4(
|
|
15262
|
+
const outDir = await mkdtemp4(join29(tmpdir7(), "codex-web-local-schema-"));
|
|
15012
15263
|
await this.runGenerateSchemaCommand(outDir);
|
|
15013
|
-
const serverNotificationPath =
|
|
15264
|
+
const serverNotificationPath = join29(outDir, "ServerNotification.json");
|
|
15014
15265
|
const raw = await readFile18(serverNotificationPath, "utf8");
|
|
15015
15266
|
const parsed = JSON.parse(raw);
|
|
15016
15267
|
const methods = this.extractMethodsFromServerNotification(parsed);
|
|
@@ -15019,7 +15270,7 @@ var MethodCatalog = class {
|
|
|
15019
15270
|
}
|
|
15020
15271
|
};
|
|
15021
15272
|
var SHARED_BRIDGE_KEY = "__codexRemoteSharedBridge__";
|
|
15022
|
-
var SHARED_BRIDGE_VERSION = "experimental-api-
|
|
15273
|
+
var SHARED_BRIDGE_VERSION = "experimental-api-v3";
|
|
15023
15274
|
function getSharedBridgeState() {
|
|
15024
15275
|
const globalScope = globalThis;
|
|
15025
15276
|
const existing = globalScope[SHARED_BRIDGE_KEY];
|
|
@@ -15068,6 +15319,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15068
15319
|
}
|
|
15069
15320
|
return threadSearchIndexPromise;
|
|
15070
15321
|
}
|
|
15322
|
+
void appServer.warmUp();
|
|
15071
15323
|
void initializeSkillsSyncOnStartup(appServer);
|
|
15072
15324
|
void readTelegramBridgeConfig().then((config) => {
|
|
15073
15325
|
if (!config.botToken) return;
|
|
@@ -15128,7 +15380,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15128
15380
|
setJson(res, 403, { error: "Zen proxy is only available from localhost" });
|
|
15129
15381
|
return;
|
|
15130
15382
|
}
|
|
15131
|
-
const statePath =
|
|
15383
|
+
const statePath = join29(getCodexHomeDir2(), FREE_MODE_STATE_FILE);
|
|
15132
15384
|
let bearerToken = "";
|
|
15133
15385
|
let wireApi = "responses";
|
|
15134
15386
|
try {
|
|
@@ -15143,7 +15395,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15143
15395
|
return;
|
|
15144
15396
|
}
|
|
15145
15397
|
if (url.pathname === "/codex-api/openrouter-proxy/v1/responses" && req.method === "POST") {
|
|
15146
|
-
const statePath =
|
|
15398
|
+
const statePath = join29(getCodexHomeDir2(), FREE_MODE_STATE_FILE);
|
|
15147
15399
|
let bearerToken = "";
|
|
15148
15400
|
let wireApi = "responses";
|
|
15149
15401
|
try {
|
|
@@ -15156,7 +15408,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15156
15408
|
return;
|
|
15157
15409
|
}
|
|
15158
15410
|
if (url.pathname === "/codex-api/custom-proxy/v1/responses" && req.method === "POST") {
|
|
15159
|
-
const statePath =
|
|
15411
|
+
const statePath = join29(getCodexHomeDir2(), FREE_MODE_STATE_FILE);
|
|
15160
15412
|
let bearerToken = "";
|
|
15161
15413
|
let wireApi = "responses";
|
|
15162
15414
|
let baseUrl = "";
|
|
@@ -15310,6 +15562,14 @@ function createCodexBridgeMiddleware() {
|
|
|
15310
15562
|
setJson(res, 200, { result: null });
|
|
15311
15563
|
return;
|
|
15312
15564
|
}
|
|
15565
|
+
const cacheableThreadReadId = body.method === "thread/read" ? readNonEmptyString(asRecord2(body.params)?.threadId) : "";
|
|
15566
|
+
if (cacheableThreadReadId) {
|
|
15567
|
+
const cachedThreadRead = appServer.getCachedThreadReadResult(cacheableThreadReadId, body.params ?? null);
|
|
15568
|
+
if (cachedThreadRead !== null) {
|
|
15569
|
+
setJson(res, 200, { result: cachedThreadRead });
|
|
15570
|
+
return;
|
|
15571
|
+
}
|
|
15572
|
+
}
|
|
15313
15573
|
let rpcResult;
|
|
15314
15574
|
try {
|
|
15315
15575
|
rpcResult = await callRpcWithArchiveRecovery(appServer, body.method, body.params ?? null);
|
|
@@ -15354,6 +15614,9 @@ function createCodexBridgeMiddleware() {
|
|
|
15354
15614
|
sanitizeThreadTurnsInlinePayloads,
|
|
15355
15615
|
mergeImportedThreadsIntoThreadListResult
|
|
15356
15616
|
}, body.method, rpcResult);
|
|
15617
|
+
if (cacheableThreadReadId) {
|
|
15618
|
+
appServer.cacheThreadReadResult(cacheableThreadReadId, body.params ?? null, pipelineResult);
|
|
15619
|
+
}
|
|
15357
15620
|
setJson(res, 200, { result: pipelineResult });
|
|
15358
15621
|
return;
|
|
15359
15622
|
}
|
|
@@ -15373,7 +15636,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15373
15636
|
const action = readNonEmptyString(body?.action) === "redo" ? "redo" : "undo";
|
|
15374
15637
|
const scope = readNonEmptyString(body?.scope) === "single_turn" ? "single_turn" : "turn_and_later";
|
|
15375
15638
|
const patchIds = Array.isArray(body?.patchIds) ? new Set(body.patchIds.filter((value) => typeof value === "string" && value.length > 0)) : void 0;
|
|
15376
|
-
const filePaths = Array.isArray(body?.filePaths) ? new Set(body.filePaths.filter((value) => typeof value === "string" && value.length > 0).map((value) => isAbsolute11(value) ? value :
|
|
15639
|
+
const filePaths = Array.isArray(body?.filePaths) ? new Set(body.filePaths.filter((value) => typeof value === "string" && value.length > 0).map((value) => isAbsolute11(value) ? value : join29(cwd, value))) : void 0;
|
|
15377
15640
|
if (!threadId || !turnId || !cwd) {
|
|
15378
15641
|
setJson(res, 400, { error: "Missing threadId, turnId, or cwd" });
|
|
15379
15642
|
return;
|
|
@@ -15459,7 +15722,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15459
15722
|
const requestedProvider = url.searchParams.get("provider")?.trim() ?? "";
|
|
15460
15723
|
if (requestedProvider) {
|
|
15461
15724
|
const normalizedRequestedProvider = requestedProvider.replace(/_/g, "-");
|
|
15462
|
-
const fmState2 = ensureDefaultFreeModeStateForMissingAuthSync(
|
|
15725
|
+
const fmState2 = ensureDefaultFreeModeStateForMissingAuthSync(join29(getCodexHomeDir2(), FREE_MODE_STATE_FILE));
|
|
15463
15726
|
if (fmState2?.enabled && normalizedRequestedProvider === CUSTOM_RUNTIME_PROVIDER_ID.replace(/_/g, "-") && fmState2.provider === "custom" && fmState2.customBaseUrl) {
|
|
15464
15727
|
setJson(res, 200, {
|
|
15465
15728
|
data: await fetchCustomEndpointModelIds(fmState2.customBaseUrl, fmState2.apiKey ?? ""),
|
|
@@ -15489,7 +15752,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15489
15752
|
});
|
|
15490
15753
|
return;
|
|
15491
15754
|
}
|
|
15492
|
-
const fmState = ensureDefaultFreeModeStateForMissingAuthSync(
|
|
15755
|
+
const fmState = ensureDefaultFreeModeStateForMissingAuthSync(join29(getCodexHomeDir2(), FREE_MODE_STATE_FILE));
|
|
15493
15756
|
if (fmState?.enabled) {
|
|
15494
15757
|
if (fmState.provider === "opencode-zen") {
|
|
15495
15758
|
try {
|
|
@@ -15664,7 +15927,7 @@ function createCodexBridgeMiddleware() {
|
|
|
15664
15927
|
import { randomBytes as randomBytes6, timingSafeEqual } from "crypto";
|
|
15665
15928
|
import { existsSync as existsSync12, mkdirSync, readFileSync as readFileSync7, renameSync, writeFileSync as writeFileSync2 } from "fs";
|
|
15666
15929
|
import { homedir as homedir10 } from "os";
|
|
15667
|
-
import { dirname as dirname11, join as
|
|
15930
|
+
import { dirname as dirname11, join as join30 } from "path";
|
|
15668
15931
|
var TOKEN_COOKIE = "portal_session";
|
|
15669
15932
|
var SESSION_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
15670
15933
|
var SESSION_STORE_FILE = "webui-auth-sessions.json";
|
|
@@ -15718,10 +15981,10 @@ function isTrustedTailscaleRemote(remote) {
|
|
|
15718
15981
|
}
|
|
15719
15982
|
function getCodexHomeDir6() {
|
|
15720
15983
|
const codexHome = process.env.CODEX_HOME?.trim();
|
|
15721
|
-
return codexHome && codexHome.length > 0 ? codexHome :
|
|
15984
|
+
return codexHome && codexHome.length > 0 ? codexHome : join30(homedir10(), ".codex");
|
|
15722
15985
|
}
|
|
15723
15986
|
function getSessionStorePath() {
|
|
15724
|
-
return
|
|
15987
|
+
return join30(getCodexHomeDir6(), SESSION_STORE_FILE);
|
|
15725
15988
|
}
|
|
15726
15989
|
function readPersistedSessions() {
|
|
15727
15990
|
const sessionStorePath = getSessionStorePath();
|
|
@@ -15903,8 +16166,8 @@ function createAuthSession(password) {
|
|
|
15903
16166
|
// src/server/httpServer.ts
|
|
15904
16167
|
import { WebSocketServer } from "ws";
|
|
15905
16168
|
var __dirname = dirname12(fileURLToPath(import.meta.url));
|
|
15906
|
-
var distDir =
|
|
15907
|
-
var spaEntryFile =
|
|
16169
|
+
var distDir = join31(__dirname, "..", "dist");
|
|
16170
|
+
var spaEntryFile = join31(distDir, "index.html");
|
|
15908
16171
|
var IMAGE_CONTENT_TYPES = {
|
|
15909
16172
|
".avif": "image/avif",
|
|
15910
16173
|
".bmp": "image/bmp",
|
|
@@ -16008,7 +16271,7 @@ function createServer(options = {}) {
|
|
|
16008
16271
|
return;
|
|
16009
16272
|
}
|
|
16010
16273
|
try {
|
|
16011
|
-
const fileStat = await
|
|
16274
|
+
const fileStat = await stat18(localPath);
|
|
16012
16275
|
if (!fileStat.isDirectory()) {
|
|
16013
16276
|
res.status(400).json({ error: "Expected directory path." });
|
|
16014
16277
|
return;
|
|
@@ -16027,7 +16290,7 @@ function createServer(options = {}) {
|
|
|
16027
16290
|
return;
|
|
16028
16291
|
}
|
|
16029
16292
|
try {
|
|
16030
|
-
const fileStat = await
|
|
16293
|
+
const fileStat = await stat18(localPath);
|
|
16031
16294
|
if (!fileStat.isDirectory()) {
|
|
16032
16295
|
res.status(400).json({ error: "Expected directory path." });
|
|
16033
16296
|
return;
|
|
@@ -16046,7 +16309,7 @@ function createServer(options = {}) {
|
|
|
16046
16309
|
return;
|
|
16047
16310
|
}
|
|
16048
16311
|
try {
|
|
16049
|
-
const fileStat = await
|
|
16312
|
+
const fileStat = await stat18(localPath);
|
|
16050
16313
|
if (!fileStat.isFile()) {
|
|
16051
16314
|
res.status(400).json({ error: "Expected file path." });
|
|
16052
16315
|
return;
|
|
@@ -16066,7 +16329,7 @@ function createServer(options = {}) {
|
|
|
16066
16329
|
return;
|
|
16067
16330
|
}
|
|
16068
16331
|
try {
|
|
16069
|
-
const fileStat = await
|
|
16332
|
+
const fileStat = await stat18(localPath);
|
|
16070
16333
|
res.setHeader("Cache-Control", "private, no-store");
|
|
16071
16334
|
if (fileStat.isDirectory()) {
|
|
16072
16335
|
const html = await createDirectoryListingHtml(localPath, { newProjectName });
|
|
@@ -16089,7 +16352,7 @@ function createServer(options = {}) {
|
|
|
16089
16352
|
return;
|
|
16090
16353
|
}
|
|
16091
16354
|
try {
|
|
16092
|
-
const fileStat = await
|
|
16355
|
+
const fileStat = await stat18(localPath);
|
|
16093
16356
|
if (!fileStat.isFile()) {
|
|
16094
16357
|
res.status(400).json({ error: "Expected file path." });
|
|
16095
16358
|
return;
|
|
@@ -16113,7 +16376,7 @@ function createServer(options = {}) {
|
|
|
16113
16376
|
}
|
|
16114
16377
|
const body = typeof req.body === "string" ? req.body : "";
|
|
16115
16378
|
try {
|
|
16116
|
-
await
|
|
16379
|
+
await writeFile18(localPath, body, "utf8");
|
|
16117
16380
|
res.status(200).json({ ok: true });
|
|
16118
16381
|
} catch {
|
|
16119
16382
|
res.status(404).json({ error: "File not found." });
|
|
@@ -16192,10 +16455,10 @@ var program = new Command().name("codex-mobile-re").description("Web interface f
|
|
|
16192
16455
|
var __dirname2 = dirname13(fileURLToPath2(import.meta.url));
|
|
16193
16456
|
var hasPromptedCloudflaredInstall = false;
|
|
16194
16457
|
function getCodexHomePath() {
|
|
16195
|
-
return process.env.CODEX_HOME?.trim() ||
|
|
16458
|
+
return process.env.CODEX_HOME?.trim() || join32(homedir11(), ".codex");
|
|
16196
16459
|
}
|
|
16197
16460
|
function getCloudflaredPromptMarkerPath() {
|
|
16198
|
-
return
|
|
16461
|
+
return join32(getCodexHomePath(), ".cloudflared-install-prompted");
|
|
16199
16462
|
}
|
|
16200
16463
|
function hasPromptedCloudflaredInstallPersisted() {
|
|
16201
16464
|
return existsSync14(getCloudflaredPromptMarkerPath());
|
|
@@ -16203,12 +16466,12 @@ function hasPromptedCloudflaredInstallPersisted() {
|
|
|
16203
16466
|
async function persistCloudflaredInstallPrompted() {
|
|
16204
16467
|
const codexHome = getCodexHomePath();
|
|
16205
16468
|
mkdirSync2(codexHome, { recursive: true });
|
|
16206
|
-
await
|
|
16469
|
+
await writeFile19(getCloudflaredPromptMarkerPath(), `${Date.now()}
|
|
16207
16470
|
`, "utf8");
|
|
16208
16471
|
}
|
|
16209
16472
|
async function readCliVersion() {
|
|
16210
16473
|
try {
|
|
16211
|
-
const packageJsonPath =
|
|
16474
|
+
const packageJsonPath = join32(__dirname2, "..", "package.json");
|
|
16212
16475
|
const raw = await readFile19(packageJsonPath, "utf8");
|
|
16213
16476
|
const parsed = JSON.parse(raw);
|
|
16214
16477
|
return typeof parsed.version === "string" ? parsed.version : "unknown";
|
|
@@ -16233,7 +16496,7 @@ function resolveCloudflaredCommand() {
|
|
|
16233
16496
|
if (canRunCommand("cloudflared", ["--version"])) {
|
|
16234
16497
|
return "cloudflared";
|
|
16235
16498
|
}
|
|
16236
|
-
const localCandidate =
|
|
16499
|
+
const localCandidate = join32(homedir11(), ".local", "bin", "cloudflared");
|
|
16237
16500
|
if (existsSync14(localCandidate) && canRunCommand(localCandidate, ["--version"])) {
|
|
16238
16501
|
return localCandidate;
|
|
16239
16502
|
}
|
|
@@ -16287,9 +16550,9 @@ async function ensureCloudflaredInstalledLinux() {
|
|
|
16287
16550
|
if (!mappedArch) {
|
|
16288
16551
|
throw new Error(`cloudflared auto-install is not supported for Linux architecture: ${process.arch}`);
|
|
16289
16552
|
}
|
|
16290
|
-
const userBinDir =
|
|
16553
|
+
const userBinDir = join32(homedir11(), ".local", "bin");
|
|
16291
16554
|
mkdirSync2(userBinDir, { recursive: true });
|
|
16292
|
-
const destination =
|
|
16555
|
+
const destination = join32(userBinDir, "cloudflared");
|
|
16293
16556
|
const downloadUrl = `https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-${mappedArch}`;
|
|
16294
16557
|
console.log("\ncloudflared not found. Installing to ~/.local/bin...\n");
|
|
16295
16558
|
await downloadFile(downloadUrl, destination);
|
|
@@ -16340,7 +16603,7 @@ async function resolveCloudflaredForTunnel() {
|
|
|
16340
16603
|
}
|
|
16341
16604
|
function hasCodexAuth() {
|
|
16342
16605
|
const codexHome = getCodexHomePath();
|
|
16343
|
-
return existsSync14(
|
|
16606
|
+
return existsSync14(join32(codexHome, "auth.json"));
|
|
16344
16607
|
}
|
|
16345
16608
|
function ensureCodexInstalled() {
|
|
16346
16609
|
let codexCommand = resolveCodexCommand();
|
|
@@ -16396,13 +16659,13 @@ function resolvePassword(input) {
|
|
|
16396
16659
|
return { password: generatePassword(), generated: true };
|
|
16397
16660
|
}
|
|
16398
16661
|
function getGeneratedPasswordPath() {
|
|
16399
|
-
return
|
|
16662
|
+
return join32(getCodexHomePath(), "codexui-password");
|
|
16400
16663
|
}
|
|
16401
16664
|
async function persistGeneratedPassword(password) {
|
|
16402
16665
|
const codexHome = getCodexHomePath();
|
|
16403
16666
|
mkdirSync2(codexHome, { recursive: true });
|
|
16404
16667
|
const passwordPath = getGeneratedPasswordPath();
|
|
16405
|
-
await
|
|
16668
|
+
await writeFile19(passwordPath, `${password}
|
|
16406
16669
|
`, { encoding: "utf8", mode: 384 });
|
|
16407
16670
|
chmodSync2(passwordPath, 384);
|
|
16408
16671
|
return passwordPath;
|
|
@@ -16541,7 +16804,7 @@ function listenWithFallback(server, startPort) {
|
|
|
16541
16804
|
}
|
|
16542
16805
|
function getCodexGlobalStatePath2() {
|
|
16543
16806
|
const codexHome = getCodexHomePath();
|
|
16544
|
-
return
|
|
16807
|
+
return join32(codexHome, ".codex-global-state.json");
|
|
16545
16808
|
}
|
|
16546
16809
|
function normalizeUniqueStrings(value) {
|
|
16547
16810
|
if (!Array.isArray(value)) return [];
|
|
@@ -16558,7 +16821,7 @@ async function persistLaunchProject(projectPath) {
|
|
|
16558
16821
|
const trimmed = projectPath.trim();
|
|
16559
16822
|
if (!trimmed) return;
|
|
16560
16823
|
const normalizedPath = isAbsolute13(trimmed) ? trimmed : resolve8(trimmed);
|
|
16561
|
-
const directoryInfo = await
|
|
16824
|
+
const directoryInfo = await stat19(normalizedPath);
|
|
16562
16825
|
if (!directoryInfo.isDirectory()) {
|
|
16563
16826
|
throw new Error(`Not a directory: ${normalizedPath}`);
|
|
16564
16827
|
}
|
|
@@ -16583,7 +16846,7 @@ async function persistLaunchProject(projectPath) {
|
|
|
16583
16846
|
normalizedPath,
|
|
16584
16847
|
...activeRoots.filter((value) => value !== normalizedPath)
|
|
16585
16848
|
];
|
|
16586
|
-
await
|
|
16849
|
+
await writeFile19(statePath, JSON.stringify(payload), "utf8");
|
|
16587
16850
|
}
|
|
16588
16851
|
async function addProjectOnly(projectPath) {
|
|
16589
16852
|
const trimmed = projectPath.trim();
|