@dev-anywhere/proxy 0.0.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/{chunk-OO64L35C.js → chunk-7PXDRNLY.js} +2 -2
- package/dist/{chunk-2Q3Z3ICU.js → chunk-JPJMOVQ5.js} +2 -2
- package/dist/{chunk-QJ5CQDK7.js → chunk-QWPI6YON.js} +38 -9
- package/dist/chunk-QWPI6YON.js.map +1 -0
- package/dist/{chunk-UGFYGF3Y.js → chunk-WXWH6L7J.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/serve.js +387 -74
- package/dist/serve.js.map +1 -1
- package/dist/session-worker.js +2 -2
- package/dist/session-worker.js.map +1 -1
- package/dist/{terminal-TTRA2VJY.js → terminal-ZPPKNAL4.js} +4 -4
- package/package.json +4 -4
- package/dist/chunk-QJ5CQDK7.js.map +0 -1
- /package/dist/{chunk-OO64L35C.js.map → chunk-7PXDRNLY.js.map} +0 -0
- /package/dist/{chunk-2Q3Z3ICU.js.map → chunk-JPJMOVQ5.js.map} +0 -0
- /package/dist/{chunk-UGFYGF3Y.js.map → chunk-WXWH6L7J.js.map} +0 -0
- /package/dist/{terminal-TTRA2VJY.js.map → terminal-ZPPKNAL4.js.map} +0 -0
package/dist/serve.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
KnownContentBlockSchema,
|
|
6
6
|
SeqCounter,
|
|
7
7
|
StreamJsonEventSchema
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-JPJMOVQ5.js";
|
|
9
9
|
import {
|
|
10
10
|
createFSM,
|
|
11
11
|
defineFSM,
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
serviceLogger,
|
|
15
15
|
shouldReleaseApprovalWait,
|
|
16
16
|
stateAfterApprovalRelease
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WXWH6L7J.js";
|
|
18
18
|
import {
|
|
19
19
|
spawnScript
|
|
20
20
|
} from "./chunk-ZUWAB67J.js";
|
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
CONFIG_PATH,
|
|
28
28
|
ControlErrorCode,
|
|
29
29
|
DATA_DIR,
|
|
30
|
+
HOOK_REGISTRY_PATH,
|
|
31
|
+
MessageEnvelopeSchema,
|
|
30
32
|
PID_PATH,
|
|
31
33
|
SESSIONS_PATH,
|
|
32
34
|
SOCK_PATH,
|
|
@@ -39,11 +41,11 @@ import {
|
|
|
39
41
|
serializeWorkerMsg,
|
|
40
42
|
sessionPaths,
|
|
41
43
|
tildify
|
|
42
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-QWPI6YON.js";
|
|
43
45
|
|
|
44
46
|
// src/serve.ts
|
|
45
47
|
import { createServer as createServer2 } from "net";
|
|
46
|
-
import { unlinkSync as unlinkSync3, writeFileSync as
|
|
48
|
+
import { unlinkSync as unlinkSync3, writeFileSync as writeFileSync6, chmodSync, rmSync as rmSync2 } from "fs";
|
|
47
49
|
|
|
48
50
|
// src/serve/session-manager.ts
|
|
49
51
|
import { mkdirSync, readFileSync, renameSync, writeFileSync, existsSync } from "fs";
|
|
@@ -181,6 +183,14 @@ var SessionManager = class {
|
|
|
181
183
|
serviceLogger.info({ sessionId: id, from: oldState, to: newState }, "Session state changed");
|
|
182
184
|
return true;
|
|
183
185
|
}
|
|
186
|
+
touchSession(id, now = Date.now(), minIntervalMs = 0) {
|
|
187
|
+
const session = this.sessions.get(id);
|
|
188
|
+
if (!session) return false;
|
|
189
|
+
if (now - session.updatedAt < minIntervalMs) return false;
|
|
190
|
+
session.updatedAt = now;
|
|
191
|
+
this.save();
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
184
194
|
terminateSession(id, context) {
|
|
185
195
|
const session = this.sessions.get(id);
|
|
186
196
|
if (!session) {
|
|
@@ -764,7 +774,7 @@ import { readdir as readdir2, mkdir } from "fs/promises";
|
|
|
764
774
|
import { join as join4, isAbsolute as isAbsolute2, normalize } from "path";
|
|
765
775
|
|
|
766
776
|
// src/serve/session-history.ts
|
|
767
|
-
import { readdir, stat, access } from "fs/promises";
|
|
777
|
+
import { readdir, stat, access, open } from "fs/promises";
|
|
768
778
|
import { createReadStream } from "fs";
|
|
769
779
|
import { join as join2 } from "path";
|
|
770
780
|
import { homedir as homedir2 } from "os";
|
|
@@ -864,49 +874,123 @@ async function scanCodexSessionHistory() {
|
|
|
864
874
|
}
|
|
865
875
|
return entries;
|
|
866
876
|
}
|
|
867
|
-
|
|
877
|
+
var DEFAULT_HISTORY_PAGE_LIMIT = 50;
|
|
878
|
+
var MAX_HISTORY_PAGE_LIMIT = 200;
|
|
879
|
+
var HISTORY_READ_CHUNK_BYTES = 64 * 1024;
|
|
880
|
+
var HISTORY_CURSOR_PREFIX = "b:";
|
|
881
|
+
function normalizeHistoryPageLimit(limit) {
|
|
882
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) return DEFAULT_HISTORY_PAGE_LIMIT;
|
|
883
|
+
return Math.max(1, Math.min(MAX_HISTORY_PAGE_LIMIT, Math.floor(limit)));
|
|
884
|
+
}
|
|
885
|
+
function encodeHistoryCursor(offset) {
|
|
886
|
+
return `${HISTORY_CURSOR_PREFIX}${Math.max(0, Math.floor(offset))}`;
|
|
887
|
+
}
|
|
888
|
+
function decodeHistoryCursor(cursor, fileSize) {
|
|
889
|
+
if (!cursor) return fileSize;
|
|
890
|
+
const raw = cursor.startsWith(HISTORY_CURSOR_PREFIX) ? cursor.slice(HISTORY_CURSOR_PREFIX.length) : cursor;
|
|
891
|
+
const parsed = Number(raw);
|
|
892
|
+
if (!Number.isInteger(parsed) || parsed < 0) return fileSize;
|
|
893
|
+
return Math.min(parsed, fileSize);
|
|
894
|
+
}
|
|
895
|
+
async function findClaudeSessionFile(claudeSessionId) {
|
|
868
896
|
let projectDirs;
|
|
869
897
|
try {
|
|
870
898
|
projectDirs = await readdir(claudeProjectsDir());
|
|
871
899
|
} catch {
|
|
872
|
-
return
|
|
900
|
+
return null;
|
|
873
901
|
}
|
|
874
902
|
for (const encodedDir of projectDirs) {
|
|
875
903
|
const filePath = join2(claudeProjectsDir(), encodedDir, `${claudeSessionId}.jsonl`);
|
|
876
904
|
try {
|
|
877
905
|
await access(filePath);
|
|
906
|
+
return filePath;
|
|
878
907
|
} catch {
|
|
879
908
|
continue;
|
|
880
909
|
}
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
910
|
+
}
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
function extractConversationMessageFromJson(obj) {
|
|
914
|
+
if (!obj || typeof obj !== "object") return null;
|
|
915
|
+
const record = obj;
|
|
916
|
+
if (record.type === "user") {
|
|
917
|
+
if (record.isMeta) return null;
|
|
918
|
+
const text = extractConversationText(record.message);
|
|
919
|
+
if (!text) return null;
|
|
920
|
+
const ts = typeof record.timestamp === "string" ? new Date(record.timestamp).getTime() : void 0;
|
|
921
|
+
return { role: "user", text, timestamp: ts };
|
|
922
|
+
}
|
|
923
|
+
if (record.type === "assistant") {
|
|
924
|
+
const text = extractConversationText(record.message);
|
|
925
|
+
if (!text) return null;
|
|
926
|
+
const ts = typeof record.timestamp === "string" ? new Date(record.timestamp).getTime() : void 0;
|
|
927
|
+
return { role: "assistant", text, timestamp: ts };
|
|
928
|
+
}
|
|
929
|
+
return null;
|
|
930
|
+
}
|
|
931
|
+
function splitLineSegments(block, blockStart) {
|
|
932
|
+
const segments = [];
|
|
933
|
+
let start = 0;
|
|
934
|
+
for (let i = 0; i < block.length; i += 1) {
|
|
935
|
+
if (block[i] !== 10) continue;
|
|
936
|
+
segments.push({ start: blockStart + start, line: block.subarray(start, i) });
|
|
937
|
+
start = i + 1;
|
|
938
|
+
}
|
|
939
|
+
segments.push({ start: blockStart + start, line: block.subarray(start) });
|
|
940
|
+
return segments;
|
|
941
|
+
}
|
|
942
|
+
function stripCarriageReturn(line) {
|
|
943
|
+
return line.length > 0 && line[line.length - 1] === 13 ? line.subarray(0, -1) : line;
|
|
944
|
+
}
|
|
945
|
+
async function readSessionMessagesPageFromFile(filePath, options = {}) {
|
|
946
|
+
const limit = normalizeHistoryPageLimit(options.limit);
|
|
947
|
+
const file = await open(filePath, "r");
|
|
948
|
+
try {
|
|
949
|
+
const fileStat = await file.stat();
|
|
950
|
+
const endOffset = decodeHistoryCursor(options.before, fileStat.size);
|
|
951
|
+
if (endOffset <= 0) return { messages: [], hasMore: false };
|
|
952
|
+
let position = endOffset;
|
|
953
|
+
let carry = Buffer.alloc(0);
|
|
954
|
+
const collected = [];
|
|
955
|
+
while (position > 0 && collected.length <= limit) {
|
|
956
|
+
const readSize = Math.min(HISTORY_READ_CHUNK_BYTES, position);
|
|
957
|
+
position -= readSize;
|
|
958
|
+
const chunk = Buffer.alloc(readSize);
|
|
959
|
+
await file.read(chunk, 0, readSize, position);
|
|
960
|
+
const block = carry.length > 0 ? Buffer.concat([chunk, carry]) : chunk;
|
|
961
|
+
const segments = splitLineSegments(block, position);
|
|
962
|
+
const firstCompleteIndex = position > 0 ? 1 : 0;
|
|
963
|
+
carry = position > 0 ? segments[0]?.line ?? Buffer.alloc(0) : Buffer.alloc(0);
|
|
964
|
+
for (let i = segments.length - 1; i >= firstCompleteIndex; i -= 1) {
|
|
965
|
+
const segment = segments[i];
|
|
966
|
+
if (!segment) continue;
|
|
967
|
+
const line = stripCarriageReturn(segment.line);
|
|
968
|
+
if (line.length === 0) continue;
|
|
889
969
|
try {
|
|
890
|
-
const
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
const ts = typeof obj.timestamp === "string" ? new Date(obj.timestamp).getTime() : void 0;
|
|
896
|
-
messages.push({ role: "user", text, timestamp: ts });
|
|
897
|
-
} else if (obj.type === "assistant") {
|
|
898
|
-
const text = extractConversationText(obj.message);
|
|
899
|
-
const ts = typeof obj.timestamp === "string" ? new Date(obj.timestamp).getTime() : void 0;
|
|
900
|
-
if (text) messages.push({ role: "assistant", text, timestamp: ts });
|
|
901
|
-
}
|
|
970
|
+
const parsed = JSON.parse(line.toString("utf-8"));
|
|
971
|
+
const message = extractConversationMessageFromJson(parsed);
|
|
972
|
+
if (!message) continue;
|
|
973
|
+
collected.push({ ...message, cursor: encodeHistoryCursor(segment.start) });
|
|
974
|
+
if (collected.length > limit) break;
|
|
902
975
|
} catch {
|
|
903
976
|
}
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
const page = collected.slice(0, limit).reverse();
|
|
980
|
+
const hasMore = collected.length > limit;
|
|
981
|
+
return {
|
|
982
|
+
messages: page,
|
|
983
|
+
hasMore,
|
|
984
|
+
...hasMore && page[0]?.cursor ? { nextBefore: page[0].cursor } : {}
|
|
985
|
+
};
|
|
986
|
+
} finally {
|
|
987
|
+
await file.close();
|
|
908
988
|
}
|
|
909
|
-
|
|
989
|
+
}
|
|
990
|
+
async function readSessionMessagesPage(claudeSessionId, options = {}) {
|
|
991
|
+
const filePath = await findClaudeSessionFile(claudeSessionId);
|
|
992
|
+
if (!filePath) return { messages: [], hasMore: false };
|
|
993
|
+
return readSessionMessagesPageFromFile(filePath, options);
|
|
910
994
|
}
|
|
911
995
|
function collapseWhitespace(text) {
|
|
912
996
|
return text.replace(/\s+/g, " ").trim();
|
|
@@ -1002,7 +1086,7 @@ function extractConversationText(msg) {
|
|
|
1002
1086
|
return null;
|
|
1003
1087
|
}
|
|
1004
1088
|
async function extractTitleAndCwd(filePath) {
|
|
1005
|
-
return new Promise((
|
|
1089
|
+
return new Promise((resolve2) => {
|
|
1006
1090
|
const rl = createInterface({
|
|
1007
1091
|
input: createReadStream(filePath, { encoding: "utf-8" }),
|
|
1008
1092
|
crlfDelay: Infinity
|
|
@@ -1030,10 +1114,10 @@ async function extractTitleAndCwd(filePath) {
|
|
|
1030
1114
|
}
|
|
1031
1115
|
});
|
|
1032
1116
|
rl.on("close", () => {
|
|
1033
|
-
if (!resolved)
|
|
1034
|
-
else
|
|
1117
|
+
if (!resolved) resolve2({ title, cwd });
|
|
1118
|
+
else resolve2({ title, cwd });
|
|
1035
1119
|
});
|
|
1036
|
-
rl.on("error", () =>
|
|
1120
|
+
rl.on("error", () => resolve2({ title, cwd }));
|
|
1037
1121
|
});
|
|
1038
1122
|
}
|
|
1039
1123
|
async function collectJsonlFiles(root) {
|
|
@@ -1055,7 +1139,7 @@ async function collectJsonlFiles(root) {
|
|
|
1055
1139
|
return files;
|
|
1056
1140
|
}
|
|
1057
1141
|
async function extractCodexTitleAndCwd(filePath) {
|
|
1058
|
-
return new Promise((
|
|
1142
|
+
return new Promise((resolve2) => {
|
|
1059
1143
|
const rl = createInterface({
|
|
1060
1144
|
input: createReadStream(filePath, { encoding: "utf-8" }),
|
|
1061
1145
|
crlfDelay: Infinity
|
|
@@ -1079,8 +1163,8 @@ async function extractCodexTitleAndCwd(filePath) {
|
|
|
1079
1163
|
} catch {
|
|
1080
1164
|
}
|
|
1081
1165
|
});
|
|
1082
|
-
rl.on("close", () =>
|
|
1083
|
-
rl.on("error", () =>
|
|
1166
|
+
rl.on("close", () => resolve2({ id, title, cwd }));
|
|
1167
|
+
rl.on("error", () => resolve2({ id, title, cwd }));
|
|
1084
1168
|
});
|
|
1085
1169
|
}
|
|
1086
1170
|
function extractCodexUserText(payload) {
|
|
@@ -1645,16 +1729,16 @@ var WorkerRegistry = class {
|
|
|
1645
1729
|
return workerPid;
|
|
1646
1730
|
}
|
|
1647
1731
|
connect(sessionId, sockPath) {
|
|
1648
|
-
return new Promise((
|
|
1732
|
+
return new Promise((resolve2) => {
|
|
1649
1733
|
const sock = connect(sockPath);
|
|
1650
1734
|
sock.on("connect", () => {
|
|
1651
1735
|
this.sockets.set(sessionId, sock);
|
|
1652
1736
|
createWorkerReader(sock, (msg) => this.handleWorkerMessage(sessionId, msg));
|
|
1653
1737
|
sock.on("close", () => this.onDisconnect(sessionId));
|
|
1654
1738
|
sock.on("error", () => this.onDisconnect(sessionId));
|
|
1655
|
-
|
|
1739
|
+
resolve2(sock);
|
|
1656
1740
|
});
|
|
1657
|
-
sock.on("error", () =>
|
|
1741
|
+
sock.on("error", () => resolve2(null));
|
|
1658
1742
|
});
|
|
1659
1743
|
}
|
|
1660
1744
|
// 枚举 DATA_DIR 下所有 session 目录,尝试连接存活的 worker.sock;失败则清理 stale socket。
|
|
@@ -1780,6 +1864,7 @@ var WorkerRegistry = class {
|
|
|
1780
1864
|
return;
|
|
1781
1865
|
}
|
|
1782
1866
|
const ev = parsed.data;
|
|
1867
|
+
this.deps.touchSessionActivity?.(sessionId);
|
|
1783
1868
|
const isStreamDeltaSession = this.streamDeltaSessions.has(sessionId);
|
|
1784
1869
|
if (ev.type === "stream_event") {
|
|
1785
1870
|
const delta = ContentBlockDeltaSchema.safeParse(ev.event);
|
|
@@ -1977,6 +2062,80 @@ function terminateSessionByOwnership(deps, sessionId) {
|
|
|
1977
2062
|
return { success: false, action: "not_found" };
|
|
1978
2063
|
}
|
|
1979
2064
|
|
|
2065
|
+
// src/serve/clipboard-image-upload.ts
|
|
2066
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
2067
|
+
import { isAbsolute as isAbsolute3, join as join5, relative, resolve } from "path";
|
|
2068
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
2069
|
+
var MAX_CLIPBOARD_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
2070
|
+
var MAX_CLIPBOARD_IMAGE_BASE64_LENGTH = Math.ceil(MAX_CLIPBOARD_IMAGE_BYTES / 3) * 4;
|
|
2071
|
+
var IMAGE_EXTENSIONS = /* @__PURE__ */ new Map([
|
|
2072
|
+
["image/png", "png"],
|
|
2073
|
+
["image/jpeg", "jpg"],
|
|
2074
|
+
["image/webp", "webp"],
|
|
2075
|
+
["image/gif", "gif"]
|
|
2076
|
+
]);
|
|
2077
|
+
function formatTimestamp(ms) {
|
|
2078
|
+
const [date, time = "000000"] = new Date(ms).toISOString().replace(/\.\d{3}Z$/, "").split("T");
|
|
2079
|
+
return `${date.replace(/-/g, "")}-${time.replace(/:/g, "")}`;
|
|
2080
|
+
}
|
|
2081
|
+
function normalizeBase64(input) {
|
|
2082
|
+
return input.replace(/^data:[^;]+;base64,/i, "").replace(/\s/g, "");
|
|
2083
|
+
}
|
|
2084
|
+
function decodeBase64Image(dataBase64) {
|
|
2085
|
+
const normalized = normalizeBase64(dataBase64);
|
|
2086
|
+
if (normalized.length > MAX_CLIPBOARD_IMAGE_BASE64_LENGTH) {
|
|
2087
|
+
throw new Error("\u56FE\u7247\u8D85\u8FC7 10MB \u9650\u5236");
|
|
2088
|
+
}
|
|
2089
|
+
if (!normalized || !/^[A-Za-z0-9+/]*={0,2}$/.test(normalized)) {
|
|
2090
|
+
throw new Error("\u56FE\u7247\u6570\u636E\u4E0D\u662F\u6709\u6548\u7684 base64");
|
|
2091
|
+
}
|
|
2092
|
+
const buffer = Buffer.from(normalized, "base64");
|
|
2093
|
+
if (buffer.length === 0) throw new Error("\u56FE\u7247\u6570\u636E\u4E3A\u7A7A");
|
|
2094
|
+
if (buffer.length > MAX_CLIPBOARD_IMAGE_BYTES) {
|
|
2095
|
+
throw new Error("\u56FE\u7247\u8D85\u8FC7 10MB \u9650\u5236");
|
|
2096
|
+
}
|
|
2097
|
+
return buffer;
|
|
2098
|
+
}
|
|
2099
|
+
function resolveSessionClipboardDir(dataDir, sessionId) {
|
|
2100
|
+
const root = resolve(dataDir);
|
|
2101
|
+
const uploadDir = resolve(root, sessionId, "clipboard");
|
|
2102
|
+
const relativePath = relative(root, uploadDir);
|
|
2103
|
+
if (!relativePath || relativePath.startsWith("..") || isAbsolute3(relativePath)) {
|
|
2104
|
+
throw new Error("\u4F1A\u8BDD\u8DEF\u5F84\u65E0\u6548");
|
|
2105
|
+
}
|
|
2106
|
+
return uploadDir;
|
|
2107
|
+
}
|
|
2108
|
+
function saveClipboardImageUpload(request, options = {}) {
|
|
2109
|
+
const extension = IMAGE_EXTENSIONS.get(request.mimeType);
|
|
2110
|
+
if (!extension) {
|
|
2111
|
+
return {
|
|
2112
|
+
success: false,
|
|
2113
|
+
path: "",
|
|
2114
|
+
error: "\u4E0D\u652F\u6301\u8FD9\u79CD\u56FE\u7247\u683C\u5F0F",
|
|
2115
|
+
errorCode: ControlErrorCode.UNKNOWN
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
try {
|
|
2119
|
+
const dataDir = options.dataDir ?? DATA_DIR;
|
|
2120
|
+
const uploadDir = resolveSessionClipboardDir(dataDir, request.sessionId);
|
|
2121
|
+
const buffer = decodeBase64Image(request.dataBase64);
|
|
2122
|
+
const now = options.now ?? Date.now;
|
|
2123
|
+
const suffix = options.randomSuffix?.() ?? nanoid3(6);
|
|
2124
|
+
const fileName = `pasted-${formatTimestamp(now())}-${suffix}.${extension}`;
|
|
2125
|
+
const path = join5(uploadDir, fileName);
|
|
2126
|
+
mkdirSync4(uploadDir, { recursive: true });
|
|
2127
|
+
writeFileSync4(path, buffer, { mode: 384 });
|
|
2128
|
+
return { success: true, path };
|
|
2129
|
+
} catch (err) {
|
|
2130
|
+
return {
|
|
2131
|
+
success: false,
|
|
2132
|
+
path: "",
|
|
2133
|
+
error: err instanceof Error ? err.message : String(err),
|
|
2134
|
+
errorCode: ControlErrorCode.UNKNOWN
|
|
2135
|
+
};
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
|
|
1980
2139
|
// src/serve/pty-input.ts
|
|
1981
2140
|
function serializeRawPtyInput(sessionId, data) {
|
|
1982
2141
|
return serializeIpc({ type: "pty_input", sessionId, data });
|
|
@@ -2008,6 +2167,21 @@ var RelayInputHandlers = class {
|
|
|
2008
2167
|
serviceLogger.warn({ sessionId }, "Remote input dropped: JSON worker socket not available");
|
|
2009
2168
|
return;
|
|
2010
2169
|
}
|
|
2170
|
+
const timestamp = typeof msg.timestamp === "number" && Number.isFinite(msg.timestamp) ? msg.timestamp : Date.now();
|
|
2171
|
+
const seq = typeof msg.seq === "number" && Number.isInteger(msg.seq) && msg.seq >= 0 ? msg.seq : 0;
|
|
2172
|
+
const version = typeof msg.version === "string" ? msg.version : "1";
|
|
2173
|
+
const messageId = typeof payload?.messageId === "string" && payload.messageId.length > 0 ? payload.messageId : `${sessionId}-user-${timestamp}`;
|
|
2174
|
+
this.deps.relayConnection.sendEnvelope(
|
|
2175
|
+
MessageEnvelopeSchema.parse({
|
|
2176
|
+
type: "user_input",
|
|
2177
|
+
sessionId,
|
|
2178
|
+
seq,
|
|
2179
|
+
timestamp,
|
|
2180
|
+
source: "proxy",
|
|
2181
|
+
version,
|
|
2182
|
+
payload: { text, messageId }
|
|
2183
|
+
})
|
|
2184
|
+
);
|
|
2011
2185
|
serviceLogger.info({ sessionId }, "Remote input forwarded to JSON worker");
|
|
2012
2186
|
return;
|
|
2013
2187
|
}
|
|
@@ -2035,6 +2209,42 @@ var RelayInputHandlers = class {
|
|
|
2035
2209
|
ts.write(serializeRawPtyInput(sessionId, data));
|
|
2036
2210
|
serviceLogger.info({ sessionId, bytes: data.length }, "Raw PTY input forwarded");
|
|
2037
2211
|
}
|
|
2212
|
+
onClipboardImageUpload(msg) {
|
|
2213
|
+
const sessionId = msg.sessionId;
|
|
2214
|
+
const requestId = msg.requestId;
|
|
2215
|
+
if (!sessionId) return;
|
|
2216
|
+
const session = this.deps.sessionManager.getSession(sessionId);
|
|
2217
|
+
if (!session) {
|
|
2218
|
+
this.deps.relayConnection.sendRaw(
|
|
2219
|
+
JSON.stringify({
|
|
2220
|
+
type: "clipboard_image_upload_response",
|
|
2221
|
+
requestId,
|
|
2222
|
+
sessionId,
|
|
2223
|
+
success: false,
|
|
2224
|
+
path: "",
|
|
2225
|
+
error: "\u4F1A\u8BDD\u4E0D\u5B58\u5728",
|
|
2226
|
+
errorCode: ControlErrorCode.SESSION_NOT_FOUND
|
|
2227
|
+
})
|
|
2228
|
+
);
|
|
2229
|
+
serviceLogger.warn({ sessionId }, "Clipboard image upload rejected: session not found");
|
|
2230
|
+
return;
|
|
2231
|
+
}
|
|
2232
|
+
const result = saveClipboardImageUpload({
|
|
2233
|
+
sessionId,
|
|
2234
|
+
mimeType: typeof msg.mimeType === "string" ? msg.mimeType : "",
|
|
2235
|
+
dataBase64: typeof msg.dataBase64 === "string" ? msg.dataBase64 : "",
|
|
2236
|
+
fileName: typeof msg.fileName === "string" ? msg.fileName : void 0
|
|
2237
|
+
});
|
|
2238
|
+
this.deps.relayConnection.sendRaw(
|
|
2239
|
+
JSON.stringify({
|
|
2240
|
+
type: "clipboard_image_upload_response",
|
|
2241
|
+
requestId,
|
|
2242
|
+
sessionId,
|
|
2243
|
+
...result
|
|
2244
|
+
})
|
|
2245
|
+
);
|
|
2246
|
+
serviceLogger.info({ sessionId, success: result.success }, "Clipboard image upload handled");
|
|
2247
|
+
}
|
|
2038
2248
|
};
|
|
2039
2249
|
|
|
2040
2250
|
// src/serve/relay-history-handlers.ts
|
|
@@ -2047,32 +2257,45 @@ var RelayHistoryHandlers = class {
|
|
|
2047
2257
|
const sid = msg.sessionId;
|
|
2048
2258
|
if (!sid) return;
|
|
2049
2259
|
const requestId = msg.requestId;
|
|
2260
|
+
const before = msg.before;
|
|
2261
|
+
const limit = msg.limit;
|
|
2050
2262
|
const session = this.deps.sessionManager.getSession(sid);
|
|
2051
2263
|
if (session?.claudeSessionId) {
|
|
2052
|
-
|
|
2264
|
+
readSessionMessagesPage(session.claudeSessionId, { before, limit }).then((page) => {
|
|
2053
2265
|
this.deps.relaySend(
|
|
2054
2266
|
JSON.stringify({
|
|
2055
2267
|
type: "session_history_messages",
|
|
2056
2268
|
requestId,
|
|
2057
2269
|
sessionId: sid,
|
|
2058
|
-
|
|
2270
|
+
...before !== void 0 ? { before } : {},
|
|
2271
|
+
messages: page.messages,
|
|
2272
|
+
hasMore: page.hasMore,
|
|
2273
|
+
...page.nextBefore !== void 0 ? { nextBefore: page.nextBefore } : {}
|
|
2059
2274
|
})
|
|
2060
2275
|
);
|
|
2061
2276
|
serviceLogger.info(
|
|
2062
|
-
{
|
|
2063
|
-
|
|
2277
|
+
{
|
|
2278
|
+
sessionId: sid,
|
|
2279
|
+
before,
|
|
2280
|
+
hasMore: page.hasMore,
|
|
2281
|
+
nextBefore: page.nextBefore,
|
|
2282
|
+
messageCount: page.messages.length
|
|
2283
|
+
},
|
|
2284
|
+
"History message page sent on request"
|
|
2064
2285
|
);
|
|
2065
2286
|
}).catch((err) => {
|
|
2066
2287
|
serviceLogger.warn(
|
|
2067
2288
|
{ sessionId: sid, error: String(err) },
|
|
2068
|
-
"Failed to read session history
|
|
2289
|
+
"Failed to read session history page on request"
|
|
2069
2290
|
);
|
|
2070
2291
|
this.deps.relaySend(
|
|
2071
2292
|
JSON.stringify({
|
|
2072
2293
|
type: "session_history_messages",
|
|
2073
2294
|
requestId,
|
|
2074
2295
|
sessionId: sid,
|
|
2075
|
-
|
|
2296
|
+
...before !== void 0 ? { before } : {},
|
|
2297
|
+
messages: [],
|
|
2298
|
+
hasMore: false
|
|
2076
2299
|
})
|
|
2077
2300
|
);
|
|
2078
2301
|
});
|
|
@@ -2082,7 +2305,9 @@ var RelayHistoryHandlers = class {
|
|
|
2082
2305
|
type: "session_history_messages",
|
|
2083
2306
|
requestId,
|
|
2084
2307
|
sessionId: sid,
|
|
2085
|
-
|
|
2308
|
+
...before !== void 0 ? { before } : {},
|
|
2309
|
+
messages: [],
|
|
2310
|
+
hasMore: false
|
|
2086
2311
|
})
|
|
2087
2312
|
);
|
|
2088
2313
|
}
|
|
@@ -2336,8 +2561,8 @@ var RelayResourceHandlers = class {
|
|
|
2336
2561
|
|
|
2337
2562
|
// src/serve/relay-session-create-handler.ts
|
|
2338
2563
|
import { rmSync, statSync as statSync2 } from "fs";
|
|
2339
|
-
import { isAbsolute as
|
|
2340
|
-
import { nanoid as
|
|
2564
|
+
import { isAbsolute as isAbsolute4 } from "path";
|
|
2565
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
2341
2566
|
|
|
2342
2567
|
// src/serve/hosted-pty-registry.ts
|
|
2343
2568
|
import * as pty from "node-pty";
|
|
@@ -2489,6 +2714,7 @@ var HostedPtyRegistry = class {
|
|
|
2489
2714
|
hosted.lastOutputTime = Date.now();
|
|
2490
2715
|
hosted.outputSeq += 1;
|
|
2491
2716
|
hosted.terminal.write(data);
|
|
2717
|
+
this.deps.touchSessionActivity(sessionId);
|
|
2492
2718
|
this.sendBinary(sessionId, Buffer.from(data, "utf-8"), hosted.outputSeq);
|
|
2493
2719
|
const oscSequences = extractOscSequences(data);
|
|
2494
2720
|
const session = this.deps.sessionManager.getSession(sessionId);
|
|
@@ -2625,7 +2851,7 @@ function validateSessionCwd(cwd) {
|
|
|
2625
2851
|
return { message: "\u8BF7\u8F93\u5165\u5DE5\u4F5C\u76EE\u5F55", code: ControlErrorCode.INVALID_PATH };
|
|
2626
2852
|
}
|
|
2627
2853
|
const trimmed = cwd.trim();
|
|
2628
|
-
if (!
|
|
2854
|
+
if (!isAbsolute4(trimmed)) {
|
|
2629
2855
|
return { message: "\u5DE5\u4F5C\u76EE\u5F55\u5FC5\u987B\u662F\u7EDD\u5BF9\u8DEF\u5F84", code: ControlErrorCode.INVALID_PATH };
|
|
2630
2856
|
}
|
|
2631
2857
|
try {
|
|
@@ -2684,7 +2910,7 @@ var RelaySessionCreateHandler = class {
|
|
|
2684
2910
|
const resumeSessionId = msg.resumeSessionId;
|
|
2685
2911
|
const streamDelta = msg.streamDelta === true;
|
|
2686
2912
|
const name = tildify(sessionCwd);
|
|
2687
|
-
const pendingId =
|
|
2913
|
+
const pendingId = nanoid4();
|
|
2688
2914
|
const hook = this.deps.createHookContext(pendingId, provider);
|
|
2689
2915
|
const workerPid = this.deps.workerRegistry.spawn(pendingId, {
|
|
2690
2916
|
cwd: sessionCwd,
|
|
@@ -2769,7 +2995,7 @@ var RelaySessionCreateHandler = class {
|
|
|
2769
2995
|
return;
|
|
2770
2996
|
}
|
|
2771
2997
|
const resumeSessionId = msg.resumeSessionId;
|
|
2772
|
-
const pendingId =
|
|
2998
|
+
const pendingId = nanoid4();
|
|
2773
2999
|
const name = tildify(cwd);
|
|
2774
3000
|
const hook = this.deps.createHookContext(pendingId, provider);
|
|
2775
3001
|
try {
|
|
@@ -2834,19 +3060,30 @@ var RelaySessionCreateHandler = class {
|
|
|
2834
3060
|
}
|
|
2835
3061
|
}
|
|
2836
3062
|
pushHistoryMessages(sessionId, resumeSessionId) {
|
|
2837
|
-
|
|
2838
|
-
if (messages.length === 0) return;
|
|
3063
|
+
readSessionMessagesPage(resumeSessionId).then((page) => {
|
|
3064
|
+
if (page.messages.length === 0) return;
|
|
2839
3065
|
this.deps.relaySend(
|
|
2840
|
-
JSON.stringify({
|
|
3066
|
+
JSON.stringify({
|
|
3067
|
+
type: "session_history_messages",
|
|
3068
|
+
sessionId,
|
|
3069
|
+
messages: page.messages,
|
|
3070
|
+
hasMore: page.hasMore,
|
|
3071
|
+
...page.nextBefore !== void 0 ? { nextBefore: page.nextBefore } : {}
|
|
3072
|
+
})
|
|
2841
3073
|
);
|
|
2842
3074
|
serviceLogger.info(
|
|
2843
|
-
{
|
|
2844
|
-
|
|
3075
|
+
{
|
|
3076
|
+
sessionId,
|
|
3077
|
+
resumeSessionId,
|
|
3078
|
+
messageCount: page.messages.length,
|
|
3079
|
+
hasMore: page.hasMore
|
|
3080
|
+
},
|
|
3081
|
+
"History message page sent for resumed session"
|
|
2845
3082
|
);
|
|
2846
3083
|
}).catch((err) => {
|
|
2847
3084
|
serviceLogger.warn(
|
|
2848
3085
|
{ sessionId, error: String(err) },
|
|
2849
|
-
"Failed to read session history
|
|
3086
|
+
"Failed to read session history page"
|
|
2850
3087
|
);
|
|
2851
3088
|
});
|
|
2852
3089
|
}
|
|
@@ -2864,6 +3101,7 @@ var RelayRouter = class {
|
|
|
2864
3101
|
this.inputHandlers = new RelayInputHandlers({
|
|
2865
3102
|
sessionManager: deps.sessionManager,
|
|
2866
3103
|
workerRegistry: deps.workerRegistry,
|
|
3104
|
+
relayConnection: deps.relayConnection,
|
|
2867
3105
|
terminalSockets: deps.terminalSockets,
|
|
2868
3106
|
hostedPtyRegistry: deps.hostedPtyRegistry,
|
|
2869
3107
|
jsonObserver: deps.jsonObserver
|
|
@@ -2924,6 +3162,7 @@ var RelayRouter = class {
|
|
|
2924
3162
|
handlers = {
|
|
2925
3163
|
user_input: (msg) => this.inputHandlers.onUserInput(msg),
|
|
2926
3164
|
remote_input_raw: (msg) => this.inputHandlers.onRemoteInputRaw(msg),
|
|
3165
|
+
clipboard_image_upload: (msg) => this.inputHandlers.onClipboardImageUpload(msg),
|
|
2927
3166
|
tool_approve: (msg) => this.permissionHandlers.onToolApprove(msg),
|
|
2928
3167
|
tool_deny: (msg) => this.permissionHandlers.onToolDeny(msg),
|
|
2929
3168
|
proxy_info_request: (msg) => this.resourceHandlers.onProxyInfoRequest(msg),
|
|
@@ -3114,11 +3353,11 @@ var PermissionBroker = class {
|
|
|
3114
3353
|
message: "Duplicate permission request id."
|
|
3115
3354
|
});
|
|
3116
3355
|
}
|
|
3117
|
-
return new Promise((
|
|
3356
|
+
return new Promise((resolve2) => {
|
|
3118
3357
|
this.pending.set(request.requestId, {
|
|
3119
3358
|
...request,
|
|
3120
3359
|
source: "hook",
|
|
3121
|
-
resolve,
|
|
3360
|
+
resolve: resolve2,
|
|
3122
3361
|
createdAt: Date.now()
|
|
3123
3362
|
});
|
|
3124
3363
|
});
|
|
@@ -3342,6 +3581,7 @@ var AgentStatusRegistry = class {
|
|
|
3342
3581
|
};
|
|
3343
3582
|
|
|
3344
3583
|
// src/serve/session-broadcast.ts
|
|
3584
|
+
var ACTIVITY_STATUS_PUSH_INTERVAL_MS = 15e3;
|
|
3345
3585
|
function toSessionListPayload(s) {
|
|
3346
3586
|
return {
|
|
3347
3587
|
sessionId: s.id,
|
|
@@ -3404,6 +3644,11 @@ function changeSessionState(sessionManager, relay, sessionId, next) {
|
|
|
3404
3644
|
if (changed) pushSessionStatus(relay, sessionManager, sessionId);
|
|
3405
3645
|
return changed;
|
|
3406
3646
|
}
|
|
3647
|
+
function touchSessionActivity(sessionManager, relay, sessionId, now = Date.now()) {
|
|
3648
|
+
const touched = sessionManager.touchSession(sessionId, now, ACTIVITY_STATUS_PUSH_INTERVAL_MS);
|
|
3649
|
+
if (touched) pushSessionStatus(relay, sessionManager, sessionId);
|
|
3650
|
+
return touched;
|
|
3651
|
+
}
|
|
3407
3652
|
|
|
3408
3653
|
// src/serve/service-files.ts
|
|
3409
3654
|
import { execSync } from "child_process";
|
|
@@ -3411,10 +3656,10 @@ import { existsSync as existsSync5, readFileSync as readFileSync5, unlinkSync as
|
|
|
3411
3656
|
import { hostname } from "os";
|
|
3412
3657
|
import { connect as connect2 } from "net";
|
|
3413
3658
|
function tryConnectSocket(sockPath) {
|
|
3414
|
-
return new Promise((
|
|
3659
|
+
return new Promise((resolve2) => {
|
|
3415
3660
|
const s = connect2(sockPath);
|
|
3416
|
-
s.on("connect", () =>
|
|
3417
|
-
s.on("error", () =>
|
|
3661
|
+
s.on("connect", () => resolve2(s));
|
|
3662
|
+
s.on("error", () => resolve2(null));
|
|
3418
3663
|
});
|
|
3419
3664
|
}
|
|
3420
3665
|
function isProcessAlive(pid) {
|
|
@@ -3758,6 +4003,7 @@ function handleTerminalConnection(socket, deps) {
|
|
|
3758
4003
|
},
|
|
3759
4004
|
(sessionId, data, outputSeq) => {
|
|
3760
4005
|
if (!sessionManager.getSession(sessionId)) return;
|
|
4006
|
+
touchSessionActivity(sessionManager, relayConnection, sessionId);
|
|
3761
4007
|
const sessionIdBuf = Buffer.from(sessionId, "utf-8");
|
|
3762
4008
|
const wsFrame = Buffer.alloc(1 + sessionIdBuf.length + 4 + data.length);
|
|
3763
4009
|
wsFrame[0] = sessionIdBuf.length;
|
|
@@ -3808,6 +4054,21 @@ function handleTerminalConnection(socket, deps) {
|
|
|
3808
4054
|
|
|
3809
4055
|
// src/serve/hook-registry.ts
|
|
3810
4056
|
import { createHash, randomBytes } from "crypto";
|
|
4057
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync5, readFileSync as readFileSync6, renameSync as renameSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
4058
|
+
import { dirname as dirname4 } from "path";
|
|
4059
|
+
import { z } from "zod";
|
|
4060
|
+
var PersistedHookSessionBindingSchema = z.object({
|
|
4061
|
+
sessionId: z.string(),
|
|
4062
|
+
provider: z.enum(["claude", "codex"]),
|
|
4063
|
+
marker: z.string(),
|
|
4064
|
+
tokenHash: z.string(),
|
|
4065
|
+
createdAt: z.number(),
|
|
4066
|
+
expiresAt: z.number().optional()
|
|
4067
|
+
});
|
|
4068
|
+
var PersistedHookRegistrySchema = z.object({
|
|
4069
|
+
version: z.literal(1),
|
|
4070
|
+
bindings: z.array(PersistedHookSessionBindingSchema)
|
|
4071
|
+
});
|
|
3811
4072
|
function hashToken(token) {
|
|
3812
4073
|
return createHash("sha256").update(token).digest("hex");
|
|
3813
4074
|
}
|
|
@@ -3816,6 +4077,11 @@ function randomSecret() {
|
|
|
3816
4077
|
}
|
|
3817
4078
|
var HookRegistry = class {
|
|
3818
4079
|
bindingsBySession = /* @__PURE__ */ new Map();
|
|
4080
|
+
persistPath;
|
|
4081
|
+
constructor(options = {}) {
|
|
4082
|
+
this.persistPath = options.persistPath;
|
|
4083
|
+
this.load();
|
|
4084
|
+
}
|
|
3819
4085
|
registerSession(sessionId, provider, options = {}) {
|
|
3820
4086
|
const now = options.now ?? Date.now();
|
|
3821
4087
|
const token = randomSecret();
|
|
@@ -3828,6 +4094,7 @@ var HookRegistry = class {
|
|
|
3828
4094
|
createdAt: now,
|
|
3829
4095
|
...options.ttlMs ? { expiresAt: now + options.ttlMs } : {}
|
|
3830
4096
|
});
|
|
4097
|
+
this.save();
|
|
3831
4098
|
return { sessionId, provider, marker, token };
|
|
3832
4099
|
}
|
|
3833
4100
|
verify(options) {
|
|
@@ -3843,7 +4110,50 @@ var HookRegistry = class {
|
|
|
3843
4110
|
return this.bindingsBySession.get(sessionId) ?? null;
|
|
3844
4111
|
}
|
|
3845
4112
|
unregisterSession(sessionId) {
|
|
3846
|
-
this.bindingsBySession.delete(sessionId)
|
|
4113
|
+
if (this.bindingsBySession.delete(sessionId)) {
|
|
4114
|
+
this.save();
|
|
4115
|
+
}
|
|
4116
|
+
}
|
|
4117
|
+
load() {
|
|
4118
|
+
if (!this.persistPath || !existsSync6(this.persistPath)) return;
|
|
4119
|
+
try {
|
|
4120
|
+
const parsed = PersistedHookRegistrySchema.parse(
|
|
4121
|
+
JSON.parse(readFileSync6(this.persistPath, "utf8"))
|
|
4122
|
+
);
|
|
4123
|
+
this.bindingsBySession.clear();
|
|
4124
|
+
for (const binding of parsed.bindings) {
|
|
4125
|
+
this.bindingsBySession.set(binding.sessionId, binding);
|
|
4126
|
+
}
|
|
4127
|
+
} catch (err) {
|
|
4128
|
+
serviceLogger.warn(
|
|
4129
|
+
{ path: this.persistPath, error: String(err) },
|
|
4130
|
+
"Failed to load hook registry state"
|
|
4131
|
+
);
|
|
4132
|
+
}
|
|
4133
|
+
}
|
|
4134
|
+
save() {
|
|
4135
|
+
if (!this.persistPath) return;
|
|
4136
|
+
try {
|
|
4137
|
+
mkdirSync5(dirname4(this.persistPath), { recursive: true });
|
|
4138
|
+
const tmpPath = `${this.persistPath}.${process.pid}.${Date.now()}.tmp`;
|
|
4139
|
+
writeFileSync5(
|
|
4140
|
+
tmpPath,
|
|
4141
|
+
JSON.stringify(
|
|
4142
|
+
{
|
|
4143
|
+
version: 1,
|
|
4144
|
+
bindings: Array.from(this.bindingsBySession.values())
|
|
4145
|
+
},
|
|
4146
|
+
null,
|
|
4147
|
+
2
|
|
4148
|
+
)
|
|
4149
|
+
);
|
|
4150
|
+
renameSync2(tmpPath, this.persistPath);
|
|
4151
|
+
} catch (err) {
|
|
4152
|
+
serviceLogger.warn(
|
|
4153
|
+
{ path: this.persistPath, error: String(err) },
|
|
4154
|
+
"Failed to persist hook registry state"
|
|
4155
|
+
);
|
|
4156
|
+
}
|
|
3847
4157
|
}
|
|
3848
4158
|
};
|
|
3849
4159
|
|
|
@@ -3878,7 +4188,7 @@ var HookServer = class {
|
|
|
3878
4188
|
this.writeJson(res, 500, { error: "internal_error" });
|
|
3879
4189
|
});
|
|
3880
4190
|
});
|
|
3881
|
-
return new Promise((
|
|
4191
|
+
return new Promise((resolve2, reject) => {
|
|
3882
4192
|
const onError = (err) => {
|
|
3883
4193
|
this.server?.off("listening", onListening);
|
|
3884
4194
|
reject(err);
|
|
@@ -3886,7 +4196,7 @@ var HookServer = class {
|
|
|
3886
4196
|
const onListening = () => {
|
|
3887
4197
|
this.server?.off("error", onError);
|
|
3888
4198
|
serviceLogger.info({ host: this.host, port: this.options.port }, "Hook server listening");
|
|
3889
|
-
|
|
4199
|
+
resolve2();
|
|
3890
4200
|
};
|
|
3891
4201
|
this.server.once("error", onError);
|
|
3892
4202
|
this.server.once("listening", onListening);
|
|
@@ -3897,8 +4207,8 @@ var HookServer = class {
|
|
|
3897
4207
|
if (!this.server) return Promise.resolve();
|
|
3898
4208
|
const server = this.server;
|
|
3899
4209
|
this.server = null;
|
|
3900
|
-
return new Promise((
|
|
3901
|
-
server.close((err) => err ? reject(err) :
|
|
4210
|
+
return new Promise((resolve2, reject) => {
|
|
4211
|
+
server.close((err) => err ? reject(err) : resolve2());
|
|
3902
4212
|
});
|
|
3903
4213
|
}
|
|
3904
4214
|
getListeningPort() {
|
|
@@ -4012,7 +4322,7 @@ var HookServer = class {
|
|
|
4012
4322
|
this.writeJson(res, 200, payload);
|
|
4013
4323
|
}
|
|
4014
4324
|
readBody(req) {
|
|
4015
|
-
return new Promise((
|
|
4325
|
+
return new Promise((resolve2, reject) => {
|
|
4016
4326
|
let body = "";
|
|
4017
4327
|
let size = 0;
|
|
4018
4328
|
req.setEncoding("utf8");
|
|
@@ -4025,7 +4335,7 @@ var HookServer = class {
|
|
|
4025
4335
|
}
|
|
4026
4336
|
body += chunk;
|
|
4027
4337
|
});
|
|
4028
|
-
req.on("end", () =>
|
|
4338
|
+
req.on("end", () => resolve2(body));
|
|
4029
4339
|
req.on("error", reject);
|
|
4030
4340
|
});
|
|
4031
4341
|
}
|
|
@@ -4038,7 +4348,7 @@ var HookServer = class {
|
|
|
4038
4348
|
|
|
4039
4349
|
// src/serve/provider-hook-runtime.ts
|
|
4040
4350
|
async function createProviderHookRuntime(options) {
|
|
4041
|
-
const hookRegistry = new HookRegistry();
|
|
4351
|
+
const hookRegistry = new HookRegistry({ persistPath: HOOK_REGISTRY_PATH });
|
|
4042
4352
|
const hookEventRouter = new HookEventRouter({
|
|
4043
4353
|
relayConnection: options.relayConnection,
|
|
4044
4354
|
agentStatusRegistry: options.agentStatusRegistry,
|
|
@@ -4209,6 +4519,7 @@ async function startService(options) {
|
|
|
4209
4519
|
const relaySend = (data) => relayConnection.sendRaw(data);
|
|
4210
4520
|
const controlHandlers = createControlMessageHandlers(relaySend, sessionManager);
|
|
4211
4521
|
const observerChangeState = (sessionId, next) => changeSessionState(sessionManager, relayConnection, sessionId, next);
|
|
4522
|
+
const observerTouchActivity = (sessionId) => touchSessionActivity(sessionManager, relayConnection, sessionId);
|
|
4212
4523
|
const emitAgentStatus = (sessionId, phase) => {
|
|
4213
4524
|
const session = sessionManager.getSession(sessionId);
|
|
4214
4525
|
if (!session) return;
|
|
@@ -4239,6 +4550,7 @@ async function startService(options) {
|
|
|
4239
4550
|
permissionBroker,
|
|
4240
4551
|
relayConnection,
|
|
4241
4552
|
jsonObserver,
|
|
4553
|
+
touchSessionActivity: observerTouchActivity,
|
|
4242
4554
|
getProviderEnv
|
|
4243
4555
|
});
|
|
4244
4556
|
const hostedPtyRegistry = new HostedPtyRegistry({
|
|
@@ -4246,6 +4558,7 @@ async function startService(options) {
|
|
|
4246
4558
|
relayConnection,
|
|
4247
4559
|
getProviderEnv,
|
|
4248
4560
|
changeSessionState: observerChangeState,
|
|
4561
|
+
touchSessionActivity: observerTouchActivity,
|
|
4249
4562
|
onTurnComplete: (sessionId) => {
|
|
4250
4563
|
resolveInterruptedApprovals(
|
|
4251
4564
|
permissionBroker,
|
|
@@ -4331,7 +4644,7 @@ async function startService(options) {
|
|
|
4331
4644
|
});
|
|
4332
4645
|
});
|
|
4333
4646
|
server.listen(SOCK_PATH, () => {
|
|
4334
|
-
|
|
4647
|
+
writeFileSync6(PID_PATH, String(process.pid));
|
|
4335
4648
|
chmodSync(SOCK_PATH, 384);
|
|
4336
4649
|
serviceLogger.info({ pid: process.pid, sock: SOCK_PATH }, "Service started");
|
|
4337
4650
|
});
|