codex-mobile-re 0.1.136 → 0.1.138
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-BrxpYd3F.js → AutomationsPanel-CDEKwt6K.js} +1 -1
- package/dist/assets/{DirectoryHub-CbWz81wr.js → DirectoryHub-DVmDATpS.js} +1 -1
- package/dist/assets/{QueuedMessages-BYByp2ES.js → QueuedMessages-ELc1xjI5.js} +1 -1
- package/dist/assets/{ReviewPane-BzcuGGZz.js → ReviewPane-27Mn7pUF.js} +1 -1
- package/dist/assets/{RightFilePreview-BRXbHofi.js → RightFilePreview-7I6S8IKp.js} +1 -1
- package/dist/assets/{RightFilesPanel-mG5aaeVI.js → RightFilesPanel-BiloZijH.js} +1 -1
- package/dist/assets/{RightGitPanel-BsVCibLZ.js → RightGitPanel-Cx6ye-CJ.js} +2 -2
- package/dist/assets/{SettingsDialog-D34lKAxd.js → SettingsDialog-CidE2T-1.js} +3 -3
- package/dist/assets/ThreadConversation-BwVKGC9K.js +25 -0
- package/dist/assets/{ThreadConversation-DynLmaDw.css → ThreadConversation-C63h9DGI.css} +1 -1
- package/dist/assets/{ThreadPendingRequestPanel-Bg_3qi-T.js → ThreadPendingRequestPanel-64Dcj9Ku.js} +1 -1
- package/dist/assets/{ThreadTerminalPanel-BlisDnyv.js → ThreadTerminalPanel-D2OFYu0C.js} +1 -1
- package/dist/assets/index-B7NhUyvj.js +98 -0
- package/dist/assets/{index-CuSbagu4.css → index-CKSFHnPK.css} +1 -1
- package/dist/index.html +2 -2
- package/dist/sw.js +34 -8
- package/dist-cli/index.js +147 -38
- package/dist-cli/index.js.map +1 -1
- package/package.json +79 -81
- package/dist/assets/ThreadConversation-BiyKia_7.js +0 -25
- package/dist/assets/index-ChD_mFyW.js +0 -98
package/dist/index.html
CHANGED
|
@@ -36,8 +36,8 @@
|
|
|
36
36
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/ibm-plex-sans-400.woff2" crossorigin />
|
|
37
37
|
<link rel="preload" as="font" type="font/woff2" href="/fonts/ibm-plex-mono-400.woff2" crossorigin />
|
|
38
38
|
<title>Codex Web</title>
|
|
39
|
-
<script type="module" crossorigin src="/assets/index-
|
|
40
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
39
|
+
<script type="module" crossorigin src="/assets/index-B7NhUyvj.js"></script>
|
|
40
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CKSFHnPK.css">
|
|
41
41
|
</head>
|
|
42
42
|
<body>
|
|
43
43
|
<div id="app"></div>
|
package/dist/sw.js
CHANGED
|
@@ -1,26 +1,49 @@
|
|
|
1
|
-
|
|
1
|
+
// round-183:把缓存名绑到构建产物上,并让安装过程不会因为一次瞬时失败而卡死。
|
|
2
|
+
//
|
|
3
|
+
// 背景(线上现象):重部署后有些标签页会「坏一阵子,清掉应用缓存就好了」。
|
|
4
|
+
// 两条叠加的原因:
|
|
5
|
+
// 1) CACHE_NAME 写死成固定字符串(旧值以 `v2` 结尾)⇒ sw.js 的字节在两次构建之间**完全
|
|
6
|
+
// 相同**,浏览器不会判定 SW 需要更新,所以既不会重新 install,也不会在
|
|
7
|
+
// activate 里清掉旧缓存 ⇒ 旧 shell 与旧 chunk 可以一直留着(旧标签页请求
|
|
8
|
+
// 已下线的 chunk 时,networkFirstStatic 还会从缓存里把旧文件喂回去,
|
|
9
|
+
// 于是整页继续跑旧构建,而服务端早已是新版本)。
|
|
10
|
+
// 2) install 里用了 cache.addAll:任一资源失败就整体 reject ⇒ 新 SW 装不上、
|
|
11
|
+
// 旧 SW 继续服务。重部署瞬间最容易踩这个。
|
|
12
|
+
//
|
|
13
|
+
// 修法:BUILD_ID 由构建期注入(见 vite.config.ts 的 codex-sw-version 插件),
|
|
14
|
+
// 每次产物变化 ⇒ sw.js 字节变化 ⇒ 走标准更新流程 ⇒ activate 清掉所有非当前
|
|
15
|
+
// 版本的缓存;install 改成逐个 add 且各自吞错。
|
|
16
|
+
const BUILD_ID = 'cc0efc82b5'
|
|
17
|
+
const CACHE_NAME = `codexweb-shell-${BUILD_ID}`
|
|
2
18
|
const APP_SHELL_PATHS = ['/', '/manifest.webmanifest']
|
|
3
19
|
const STATIC_DESTINATIONS = new Set(['document', 'script', 'style', 'image', 'font'])
|
|
4
20
|
const BYPASS_PREFIXES = ['/codex-api/', '/codex-local-image', '/codex-local-file', '/codex-local-browse/', '/codex-local-edit/']
|
|
5
21
|
|
|
6
22
|
self.addEventListener('install', (event) => {
|
|
7
23
|
event.waitUntil(
|
|
8
|
-
|
|
24
|
+
(async () => {
|
|
25
|
+
const cache = await caches.open(CACHE_NAME)
|
|
26
|
+
// 逐个装入:单个资源失败不该让新版本装不上(旧行为是 addAll 整体 reject)。
|
|
27
|
+
await Promise.all(
|
|
28
|
+
APP_SHELL_PATHS.map((path) => cache.add(path).catch(() => undefined)),
|
|
29
|
+
)
|
|
30
|
+
})(),
|
|
9
31
|
)
|
|
10
32
|
self.skipWaiting()
|
|
11
33
|
})
|
|
12
34
|
|
|
13
35
|
self.addEventListener('activate', (event) => {
|
|
14
36
|
event.waitUntil(
|
|
15
|
-
|
|
16
|
-
|
|
37
|
+
(async () => {
|
|
38
|
+
const keys = await caches.keys()
|
|
39
|
+
await Promise.all(
|
|
17
40
|
keys
|
|
18
41
|
.filter((key) => key !== CACHE_NAME)
|
|
19
42
|
.map((key) => caches.delete(key)),
|
|
20
|
-
)
|
|
21
|
-
|
|
43
|
+
)
|
|
44
|
+
await self.clients.claim()
|
|
45
|
+
})(),
|
|
22
46
|
)
|
|
23
|
-
self.clients.claim()
|
|
24
47
|
})
|
|
25
48
|
|
|
26
49
|
self.addEventListener('fetch', (event) => {
|
|
@@ -50,9 +73,12 @@ async function networkFirstNavigation(request) {
|
|
|
50
73
|
const cache = await caches.open(CACHE_NAME)
|
|
51
74
|
try {
|
|
52
75
|
const response = await fetch(request)
|
|
53
|
-
|
|
76
|
+
if (response.ok) {
|
|
77
|
+
cache.put('/', response.clone())
|
|
78
|
+
}
|
|
54
79
|
return response
|
|
55
80
|
} catch {
|
|
81
|
+
// 仅真正的离线才回退缓存;此时缓存一定是本构建写入的(缓存名带 BUILD_ID)。
|
|
56
82
|
return (await cache.match('/')) || Response.error()
|
|
57
83
|
}
|
|
58
84
|
}
|
package/dist-cli/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { readFile as readFile20, stat as stat19, writeFile as writeFile20 } from
|
|
|
7
7
|
import { homedir as homedir11, networkInterfaces } from "os";
|
|
8
8
|
import { isAbsolute as isAbsolute13, join as join32, resolve as resolve8 } from "path";
|
|
9
9
|
import { spawn as spawn9 } from "child_process";
|
|
10
|
-
import { createInterface as
|
|
10
|
+
import { createInterface as createInterface3 } from "readline/promises";
|
|
11
11
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
12
12
|
import { dirname as dirname12 } from "path";
|
|
13
13
|
import { get as httpsGet } from "https";
|
|
@@ -10712,10 +10712,36 @@ import { readFile as readFile10, stat as stat11 } from "fs/promises";
|
|
|
10712
10712
|
// src/server/bridge/session.ts
|
|
10713
10713
|
import { mkdir as mkdir8, readFile as readFile9, rename as rename2, rm as rm6, stat as stat9, writeFile as writeFile8 } from "fs/promises";
|
|
10714
10714
|
import { dirname as dirname4, isAbsolute as isAbsolute4, join as join17 } from "path";
|
|
10715
|
+
|
|
10716
|
+
// src/server/bridge/sessionLogScan.ts
|
|
10717
|
+
import { createReadStream as createReadStream3 } from "fs";
|
|
10718
|
+
import { createInterface } from "readline";
|
|
10719
|
+
var SESSION_LOG_READ_CHUNK_BYTES = 64 * 1024;
|
|
10720
|
+
async function* iterateSessionLogLines(sessionPath) {
|
|
10721
|
+
const stream = createReadStream3(sessionPath, {
|
|
10722
|
+
encoding: "utf8",
|
|
10723
|
+
highWaterMark: SESSION_LOG_READ_CHUNK_BYTES
|
|
10724
|
+
});
|
|
10725
|
+
const reader = createInterface({ input: stream, crlfDelay: Infinity });
|
|
10726
|
+
try {
|
|
10727
|
+
for await (const line of reader) yield line;
|
|
10728
|
+
} finally {
|
|
10729
|
+
reader.close();
|
|
10730
|
+
stream.destroy();
|
|
10731
|
+
}
|
|
10732
|
+
}
|
|
10733
|
+
async function readSessionLogFirstLine(sessionPath) {
|
|
10734
|
+
for await (const line of iterateSessionLogLines(sessionPath)) return line;
|
|
10735
|
+
return "";
|
|
10736
|
+
}
|
|
10737
|
+
|
|
10738
|
+
// src/server/bridge/session.ts
|
|
10715
10739
|
var SESSION_SKILL_INPUT_CACHE_LIMIT = 64;
|
|
10716
10740
|
var sessionSkillInputCache = /* @__PURE__ */ new Map();
|
|
10741
|
+
var sessionSkillInputScanInFlight = /* @__PURE__ */ new Map();
|
|
10717
10742
|
var SESSION_LOG_RECOVERY_CACHE_LIMIT = 64;
|
|
10718
10743
|
var sessionLogRecoveryCache = /* @__PURE__ */ new Map();
|
|
10744
|
+
var sessionLogRecoveryScanInFlight = /* @__PURE__ */ new Map();
|
|
10719
10745
|
function rememberSessionLogRecovery(sessionPath, size, mtimeMs, recoverable) {
|
|
10720
10746
|
sessionLogRecoveryCache.delete(sessionPath);
|
|
10721
10747
|
sessionLogRecoveryCache.set(sessionPath, { size, mtimeMs, recoverable });
|
|
@@ -10733,10 +10759,10 @@ function parseSessionSkillText(value) {
|
|
|
10733
10759
|
if (!name || !path) return null;
|
|
10734
10760
|
return { name, path };
|
|
10735
10761
|
}
|
|
10736
|
-
function buildSessionSkillInputsByTurn(
|
|
10762
|
+
async function buildSessionSkillInputsByTurn(lines) {
|
|
10737
10763
|
let currentTurnId = "";
|
|
10738
10764
|
const skillsByTurnId = /* @__PURE__ */ new Map();
|
|
10739
|
-
for (const line of
|
|
10765
|
+
for await (const line of lines) {
|
|
10740
10766
|
if (!line.trim()) continue;
|
|
10741
10767
|
let row = null;
|
|
10742
10768
|
try {
|
|
@@ -10780,18 +10806,30 @@ async function readCachedSessionSkillInputsByTurn(sessionPath) {
|
|
|
10780
10806
|
if (cached && cached.size === sessionStat.size && cached.mtimeMs === sessionStat.mtimeMs) {
|
|
10781
10807
|
return cached.skillsByTurnId;
|
|
10782
10808
|
}
|
|
10783
|
-
const
|
|
10784
|
-
const
|
|
10785
|
-
|
|
10786
|
-
|
|
10787
|
-
|
|
10788
|
-
|
|
10789
|
-
|
|
10790
|
-
|
|
10791
|
-
|
|
10792
|
-
|
|
10809
|
+
const scanKey = `${sessionPath}\0${sessionStat.size}\0${sessionStat.mtimeMs}`;
|
|
10810
|
+
const inFlight = sessionSkillInputScanInFlight.get(scanKey);
|
|
10811
|
+
if (inFlight) return await inFlight;
|
|
10812
|
+
const scan = (async () => {
|
|
10813
|
+
const skillsByTurnId = await buildSessionSkillInputsByTurn(iterateSessionLogLines(sessionPath));
|
|
10814
|
+
sessionSkillInputCache.delete(sessionPath);
|
|
10815
|
+
sessionSkillInputCache.set(sessionPath, {
|
|
10816
|
+
size: sessionStat.size,
|
|
10817
|
+
mtimeMs: sessionStat.mtimeMs,
|
|
10818
|
+
skillsByTurnId
|
|
10819
|
+
});
|
|
10820
|
+
while (sessionSkillInputCache.size > SESSION_SKILL_INPUT_CACHE_LIMIT) {
|
|
10821
|
+
const oldestKey = sessionSkillInputCache.keys().next().value;
|
|
10822
|
+
if (!oldestKey) break;
|
|
10823
|
+
sessionSkillInputCache.delete(oldestKey);
|
|
10824
|
+
}
|
|
10825
|
+
return skillsByTurnId;
|
|
10826
|
+
})();
|
|
10827
|
+
sessionSkillInputScanInFlight.set(scanKey, scan);
|
|
10828
|
+
try {
|
|
10829
|
+
return await scan;
|
|
10830
|
+
} finally {
|
|
10831
|
+
sessionSkillInputScanInFlight.delete(scanKey);
|
|
10793
10832
|
}
|
|
10794
|
-
return skillsByTurnId;
|
|
10795
10833
|
}
|
|
10796
10834
|
function mergeSessionSkillInputsIntoTurnsFromMap(turns, skillsByTurnId) {
|
|
10797
10835
|
const turnIds = /* @__PURE__ */ new Set();
|
|
@@ -10988,7 +11026,7 @@ function parseApplyPatchInput(input) {
|
|
|
10988
11026
|
}
|
|
10989
11027
|
return changes;
|
|
10990
11028
|
}
|
|
10991
|
-
function buildSessionFileChangeFallback(threadReadPayload,
|
|
11029
|
+
async function buildSessionFileChangeFallback(threadReadPayload, lines) {
|
|
10992
11030
|
const payload = asRecord(threadReadPayload);
|
|
10993
11031
|
const thread = asRecord(payload?.thread);
|
|
10994
11032
|
const turns = Array.isArray(thread?.turns) ? thread.turns : [];
|
|
@@ -11002,7 +11040,7 @@ function buildSessionFileChangeFallback(threadReadPayload, sessionLogRaw) {
|
|
|
11002
11040
|
}
|
|
11003
11041
|
const collectedByTurnId = /* @__PURE__ */ new Map();
|
|
11004
11042
|
let currentTurnId = "";
|
|
11005
|
-
for (const line of
|
|
11043
|
+
for await (const line of lines) {
|
|
11006
11044
|
if (!line.trim()) continue;
|
|
11007
11045
|
let row = null;
|
|
11008
11046
|
try {
|
|
@@ -11082,12 +11120,12 @@ function isRecoverableToolPayload(payload) {
|
|
|
11082
11120
|
if (payload.type === "function_call") return name === "exec_command" || name === "shell_command";
|
|
11083
11121
|
return payload.type === "custom_tool_call" && name === "apply_patch";
|
|
11084
11122
|
}
|
|
11085
|
-
function buildSessionItemOrder(
|
|
11123
|
+
async function buildSessionItemOrder(lines, turnIds) {
|
|
11086
11124
|
let currentTurnId = "";
|
|
11087
11125
|
const orderByTurnId = /* @__PURE__ */ new Map();
|
|
11088
11126
|
const callIdToCommand = /* @__PURE__ */ new Map();
|
|
11089
11127
|
let sawRecoverableToolRow = false;
|
|
11090
|
-
for (const line of
|
|
11128
|
+
for await (const line of lines) {
|
|
11091
11129
|
if (!line.trim()) continue;
|
|
11092
11130
|
let row = null;
|
|
11093
11131
|
try {
|
|
@@ -11201,10 +11239,10 @@ function extractFilePathsFromCommand(cmd, cwd) {
|
|
|
11201
11239
|
}
|
|
11202
11240
|
return [...new Set(paths)];
|
|
11203
11241
|
}
|
|
11204
|
-
function collectFileChangesForTurns(
|
|
11242
|
+
async function collectFileChangesForTurns(lines, turnIdsToRevert, cwd) {
|
|
11205
11243
|
let currentTurnId = "";
|
|
11206
11244
|
const infoByTurnId = /* @__PURE__ */ new Map();
|
|
11207
|
-
for (const line of
|
|
11245
|
+
for await (const line of lines) {
|
|
11208
11246
|
if (!line.trim()) continue;
|
|
11209
11247
|
let row = null;
|
|
11210
11248
|
try {
|
|
@@ -11703,9 +11741,18 @@ async function mergeSessionCommandsIntoTurnsFromPath(turns, sessionPath) {
|
|
|
11703
11741
|
if (cached && cached.size === sessionStat.size && cached.mtimeMs === sessionStat.mtimeMs && !cached.recoverable) {
|
|
11704
11742
|
return turns;
|
|
11705
11743
|
}
|
|
11706
|
-
const
|
|
11707
|
-
|
|
11708
|
-
|
|
11744
|
+
const scanKey = `${sessionPath}\0${sessionStat.size}\0${sessionStat.mtimeMs}\0${[...turnIds].sort().join(",")}`;
|
|
11745
|
+
let scan = sessionLogRecoveryScanInFlight.get(scanKey);
|
|
11746
|
+
if (!scan) {
|
|
11747
|
+
scan = (async () => {
|
|
11748
|
+
const { orderByTurnId: orderByTurnId2, sawRecoverableToolRow } = await buildSessionItemOrder(iterateSessionLogLines(sessionPath), turnIds);
|
|
11749
|
+
rememberSessionLogRecovery(sessionPath, sessionStat.size, sessionStat.mtimeMs, sawRecoverableToolRow);
|
|
11750
|
+
return orderByTurnId2;
|
|
11751
|
+
})();
|
|
11752
|
+
sessionLogRecoveryScanInFlight.set(scanKey, scan);
|
|
11753
|
+
void scan.catch(() => void 0).finally(() => sessionLogRecoveryScanInFlight.delete(scanKey));
|
|
11754
|
+
}
|
|
11755
|
+
const orderByTurnId = await scan;
|
|
11709
11756
|
return mergeSessionCommandsIntoTurnsFromOrder(turns, orderByTurnId);
|
|
11710
11757
|
}
|
|
11711
11758
|
async function mergeSessionCommandsIntoThreadResult(result) {
|
|
@@ -12001,8 +12048,8 @@ function handleThreadHttpRequest(req, res, url, deps) {
|
|
|
12001
12048
|
return true;
|
|
12002
12049
|
}
|
|
12003
12050
|
try {
|
|
12004
|
-
const
|
|
12005
|
-
setJson2(res, 200, { data
|
|
12051
|
+
const data = await buildSessionFileChangeFallback(threadReadResult, iterateSessionLogLines(sessionPath));
|
|
12052
|
+
setJson2(res, 200, { data });
|
|
12006
12053
|
} catch {
|
|
12007
12054
|
setJson2(res, 200, { data: [] });
|
|
12008
12055
|
}
|
|
@@ -12415,10 +12462,10 @@ async function handleTelegramHttpRequest(req, res, url, deps) {
|
|
|
12415
12462
|
}
|
|
12416
12463
|
|
|
12417
12464
|
// src/server/bridge/threadPreferencesRoutes.ts
|
|
12418
|
-
import { createReadStream as
|
|
12465
|
+
import { createReadStream as createReadStream4 } from "fs";
|
|
12419
12466
|
import { readFile as readFile13, stat as stat12, writeFile as writeFile12 } from "fs/promises";
|
|
12420
12467
|
import { join as join21 } from "path";
|
|
12421
|
-
import { createInterface } from "readline";
|
|
12468
|
+
import { createInterface as createInterface2 } from "readline";
|
|
12422
12469
|
var MAX_THREAD_TITLES = 500;
|
|
12423
12470
|
var MAX_REASONING_MESSAGES_PER_THREAD = 20;
|
|
12424
12471
|
var MAX_TURN_DURATIONS_PER_THREAD = 200;
|
|
@@ -12692,8 +12739,8 @@ function getSessionIndexFileSignature(stats) {
|
|
|
12692
12739
|
}
|
|
12693
12740
|
async function parseThreadTitlesFromSessionIndex(sessionIndexPath) {
|
|
12694
12741
|
const latestById = /* @__PURE__ */ new Map();
|
|
12695
|
-
const input =
|
|
12696
|
-
const lines =
|
|
12742
|
+
const input = createReadStream4(sessionIndexPath, { encoding: "utf8" });
|
|
12743
|
+
const lines = createInterface2({
|
|
12697
12744
|
input,
|
|
12698
12745
|
crlfDelay: Infinity
|
|
12699
12746
|
});
|
|
@@ -12908,7 +12955,7 @@ data: ${JSON.stringify({ ok: true })}
|
|
|
12908
12955
|
// src/server/bridge/projectZip.ts
|
|
12909
12956
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
12910
12957
|
import { existsSync as existsSync8 } from "fs";
|
|
12911
|
-
import { mkdir as mkdir10,
|
|
12958
|
+
import { mkdir as mkdir10, realpath as realpath3, utimes, writeFile as writeFile14 } from "fs/promises";
|
|
12912
12959
|
import { basename as basename7, dirname as dirname5, extname as extname3, isAbsolute as isAbsolute7, join as join23, relative as relative3, resolve as resolve4, sep as sep3 } from "path";
|
|
12913
12960
|
|
|
12914
12961
|
// src/server/bridge/importedSessions.ts
|
|
@@ -13495,13 +13542,13 @@ async function collectProjectChatZipEntries(projectRoot) {
|
|
|
13495
13542
|
for (const root of roots) {
|
|
13496
13543
|
for await (const sessionPath of walkFiles(root.disk)) {
|
|
13497
13544
|
if (extname3(sessionPath) !== ".jsonl") continue;
|
|
13498
|
-
let
|
|
13545
|
+
let metaLine = "";
|
|
13499
13546
|
try {
|
|
13500
|
-
|
|
13547
|
+
metaLine = await readSessionLogFirstLine(sessionPath);
|
|
13501
13548
|
} catch {
|
|
13502
13549
|
continue;
|
|
13503
13550
|
}
|
|
13504
|
-
const sessionCwd = readSessionMetaCwd(
|
|
13551
|
+
const sessionCwd = readSessionMetaCwd(metaLine);
|
|
13505
13552
|
if (!sessionCwd) continue;
|
|
13506
13553
|
let canonicalSessionCwd = "";
|
|
13507
13554
|
try {
|
|
@@ -13512,7 +13559,7 @@ async function collectProjectChatZipEntries(projectRoot) {
|
|
|
13512
13559
|
if (!isSameOrDescendantPath(canonicalSessionCwd, canonicalProjectRoot)) continue;
|
|
13513
13560
|
const rel = relative3(root.disk, sessionPath).split(sep3).join("/");
|
|
13514
13561
|
const zipPath = `${root.zip}/${rel}`;
|
|
13515
|
-
const sessionId = readSessionMetaId(
|
|
13562
|
+
const sessionId = readSessionMetaId(metaLine);
|
|
13516
13563
|
const stateMetadata = sessionId ? stateDbThreadMetadata.get(sessionId) : void 0;
|
|
13517
13564
|
const title = readNonEmptyString(stateMetadata?.title) || (sessionId ? readNonEmptyString(threadTitles.titles[sessionId]) : "");
|
|
13518
13565
|
if (title) exportedTitles[zipPath] = title;
|
|
@@ -15521,6 +15568,7 @@ var MERGEABLE_ITEM_TYPES = /* @__PURE__ */ new Set([
|
|
|
15521
15568
|
"commandExecution",
|
|
15522
15569
|
"fileChange"
|
|
15523
15570
|
]);
|
|
15571
|
+
var THREAD_READ_SNAPSHOT_LIMIT = 8;
|
|
15524
15572
|
var AppServerProcess = class {
|
|
15525
15573
|
constructor() {
|
|
15526
15574
|
this.process = null;
|
|
@@ -15722,7 +15770,13 @@ var AppServerProcess = class {
|
|
|
15722
15770
|
return buffer.slice(-limit);
|
|
15723
15771
|
}
|
|
15724
15772
|
storeThreadReadSnapshot(threadId, snapshot) {
|
|
15773
|
+
this.lastThreadReadSnapshotByThreadId.delete(threadId);
|
|
15725
15774
|
this.lastThreadReadSnapshotByThreadId.set(threadId, snapshot);
|
|
15775
|
+
while (this.lastThreadReadSnapshotByThreadId.size > THREAD_READ_SNAPSHOT_LIMIT) {
|
|
15776
|
+
const oldestThreadId = this.lastThreadReadSnapshotByThreadId.keys().next().value;
|
|
15777
|
+
if (!oldestThreadId) break;
|
|
15778
|
+
this.lastThreadReadSnapshotByThreadId.delete(oldestThreadId);
|
|
15779
|
+
}
|
|
15726
15780
|
this.invalidateBoundedThreadTurnPageCache(threadId);
|
|
15727
15781
|
}
|
|
15728
15782
|
getLastThreadReadSnapshot(threadId) {
|
|
@@ -16959,14 +17013,17 @@ function createCodexBridgeMiddleware() {
|
|
|
16959
17013
|
setJson(res, 200, { reverted: 0, errors: [], message: "No turns to revert" });
|
|
16960
17014
|
return;
|
|
16961
17015
|
}
|
|
16962
|
-
let
|
|
17016
|
+
let turnInfos;
|
|
16963
17017
|
try {
|
|
16964
|
-
|
|
17018
|
+
turnInfos = await collectFileChangesForTurns(
|
|
17019
|
+
iterateSessionLogLines(sessionPath),
|
|
17020
|
+
turnIdsToRevert,
|
|
17021
|
+
cwd
|
|
17022
|
+
);
|
|
16965
17023
|
} catch {
|
|
16966
17024
|
setJson(res, 200, { reverted: 0, errors: ["Could not read session log"], message: "Session log unreadable" });
|
|
16967
17025
|
return;
|
|
16968
17026
|
}
|
|
16969
|
-
const turnInfos = collectFileChangesForTurns(sessionLogRaw, turnIdsToRevert, cwd);
|
|
16970
17027
|
if (turnInfos.size === 0) {
|
|
16971
17028
|
setJson(res, 200, { changed: 0, errors: [], message: action === "redo" ? "No file changes to redo" : "No file changes to revert" });
|
|
16972
17029
|
return;
|
|
@@ -17458,6 +17515,57 @@ function createAuthSession(password) {
|
|
|
17458
17515
|
|
|
17459
17516
|
// src/server/httpServer.ts
|
|
17460
17517
|
import { WebSocketServer } from "ws";
|
|
17518
|
+
|
|
17519
|
+
// src/server/bridge/wsHeartbeat.ts
|
|
17520
|
+
var WS_HEARTBEAT_INTERVAL_MS = 25e3;
|
|
17521
|
+
function attachWebSocketHeartbeat(socket, options = {}) {
|
|
17522
|
+
const intervalMs = options.intervalMs ?? WS_HEARTBEAT_INTERVAL_MS;
|
|
17523
|
+
const onDead = options.onDead ?? (() => {
|
|
17524
|
+
try {
|
|
17525
|
+
socket.terminate();
|
|
17526
|
+
} catch {
|
|
17527
|
+
}
|
|
17528
|
+
});
|
|
17529
|
+
let alive = true;
|
|
17530
|
+
let stopped = false;
|
|
17531
|
+
const handlePong = () => {
|
|
17532
|
+
alive = true;
|
|
17533
|
+
};
|
|
17534
|
+
const stop = () => {
|
|
17535
|
+
if (stopped) return;
|
|
17536
|
+
stopped = true;
|
|
17537
|
+
clearInterval(timer);
|
|
17538
|
+
socket.off?.("pong", handlePong);
|
|
17539
|
+
socket.off?.("close", stop);
|
|
17540
|
+
socket.off?.("error", stop);
|
|
17541
|
+
};
|
|
17542
|
+
const timer = setInterval(() => {
|
|
17543
|
+
if (stopped) return;
|
|
17544
|
+
if (!alive) {
|
|
17545
|
+
stop();
|
|
17546
|
+
onDead();
|
|
17547
|
+
return;
|
|
17548
|
+
}
|
|
17549
|
+
alive = false;
|
|
17550
|
+
try {
|
|
17551
|
+
socket.ping();
|
|
17552
|
+
} catch {
|
|
17553
|
+
stop();
|
|
17554
|
+
onDead();
|
|
17555
|
+
}
|
|
17556
|
+
}, intervalMs);
|
|
17557
|
+
const unrefable = timer;
|
|
17558
|
+
if (typeof unrefable.unref === "function") unrefable.unref();
|
|
17559
|
+
socket.on("pong", handlePong);
|
|
17560
|
+
socket.on("close", stop);
|
|
17561
|
+
socket.on("error", stop);
|
|
17562
|
+
return stop;
|
|
17563
|
+
}
|
|
17564
|
+
function attachHeartbeatToWebSocket(ws) {
|
|
17565
|
+
return attachWebSocketHeartbeat(ws);
|
|
17566
|
+
}
|
|
17567
|
+
|
|
17568
|
+
// src/server/httpServer.ts
|
|
17461
17569
|
var __dirname = dirname11(fileURLToPath(import.meta.url));
|
|
17462
17570
|
var distDir = join31(__dirname, "..", "dist");
|
|
17463
17571
|
var spaEntryFile = join31(distDir, "index.html");
|
|
@@ -17723,6 +17831,7 @@ function createServer(options = {}) {
|
|
|
17723
17831
|
});
|
|
17724
17832
|
});
|
|
17725
17833
|
wss.on("connection", (ws) => {
|
|
17834
|
+
attachHeartbeatToWebSocket(ws);
|
|
17726
17835
|
ws.send(JSON.stringify({ method: "ready", params: { ok: true }, atIso: (/* @__PURE__ */ new Date()).toISOString() }));
|
|
17727
17836
|
const unsubscribe = bridge.subscribeNotifications((notification) => {
|
|
17728
17837
|
if (ws.readyState !== 1) return;
|
|
@@ -17878,7 +17987,7 @@ async function shouldInstallCloudflaredInteractively() {
|
|
|
17878
17987
|
console.warn("\n[cloudflared] cloudflared is missing and terminal is non-interactive, skipping install.");
|
|
17879
17988
|
return false;
|
|
17880
17989
|
}
|
|
17881
|
-
const prompt =
|
|
17990
|
+
const prompt = createInterface3({ input: process.stdin, output: process.stdout });
|
|
17882
17991
|
try {
|
|
17883
17992
|
const answer = await prompt.question("cloudflared is not installed. Install it now to ~/.local/bin? [y/N] ");
|
|
17884
17993
|
const normalized = answer.trim().toLowerCase();
|