dsh-phone-bridge 0.2.6 → 0.2.8
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/index.js +98 -18
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -244,6 +244,32 @@ const REQUIRED_CONTROLLER_METHODS = [
|
|
|
244
244
|
"selectModel",
|
|
245
245
|
];
|
|
246
246
|
|
|
247
|
+
// The DSH version this build was last exercised against. Bumped by hand when the
|
|
248
|
+
// plugin is re-tested on a newer DSH. It is not a compatibility claim, only the
|
|
249
|
+
// value /health compares the running version to, so that a mismatch shows up as a
|
|
250
|
+
// sentence rather than as a mystery.
|
|
251
|
+
const TESTED_WITH_DSH = "0.2.0-rc.2";
|
|
252
|
+
|
|
253
|
+
// Best effort. The plugin runs inside the DSH process, and Electron patches fs so
|
|
254
|
+
// an app.asar reads like a directory, which is what makes this possible at all.
|
|
255
|
+
// Returns null when it cannot be read; /health says so rather than guessing.
|
|
256
|
+
function readDshVersion() {
|
|
257
|
+
const candidates = [];
|
|
258
|
+
if (process.resourcesPath) {
|
|
259
|
+
candidates.push(join(process.resourcesPath, "app.asar", "package.json"));
|
|
260
|
+
candidates.push(join(process.resourcesPath, "app", "package.json"));
|
|
261
|
+
}
|
|
262
|
+
for (const candidate of candidates) {
|
|
263
|
+
try {
|
|
264
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
265
|
+
if (parsed?.version) return { name: parsed.name ?? null, version: parsed.version };
|
|
266
|
+
} catch {
|
|
267
|
+
// try the next candidate
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
|
|
247
273
|
// A session lives at sessions/<slug-of-cwd>/<sessionId>/, where the slug is a
|
|
248
274
|
// lossy encoding of the working directory. Locate it by scanning instead of
|
|
249
275
|
// trying to re-derive that encoding.
|
|
@@ -943,29 +969,74 @@ export function apply(ctx, config = {}) {
|
|
|
943
969
|
}
|
|
944
970
|
const sessionController = ctx.get("sessionController");
|
|
945
971
|
if (!sessionController) throw new Error("sessionController is unavailable");
|
|
946
|
-
if (typeof sessionController.page !== "function") {
|
|
947
|
-
throw new Error("this DSH build does not expose sessionController.page");
|
|
948
|
-
}
|
|
949
972
|
|
|
950
973
|
const signal = new AbortController().signal;
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
974
|
+
const traces = {};
|
|
975
|
+
|
|
976
|
+
// Two ways in, tried in order.
|
|
977
|
+
//
|
|
978
|
+
// inspect() hands back the event list outright (SessionInspection.events),
|
|
979
|
+
// so it needs no cursor. page() does need one: throughSeq is an absolute
|
|
980
|
+
// sequence, and -1 is not "newest" but "nothing", since paginate starts at
|
|
981
|
+
// end = throughSeq + 1 = 0 and walks backwards from -1. That misreading is
|
|
982
|
+
// what made the first version answer ok with zero messages.
|
|
983
|
+
let events = [];
|
|
984
|
+
let used = null;
|
|
985
|
+
|
|
986
|
+
if (typeof sessionController.inspect === "function") {
|
|
987
|
+
try {
|
|
988
|
+
const inspection = await sessionController.inspect(sessionId, signal);
|
|
989
|
+
const list = Array.isArray(inspection?.events) ? inspection.events : [];
|
|
990
|
+
traces.inspect = {
|
|
991
|
+
eventCount: list.length,
|
|
992
|
+
firstSeq: list[0]?.seq ?? null,
|
|
993
|
+
lastSeq: list[list.length - 1]?.seq ?? null,
|
|
994
|
+
};
|
|
995
|
+
if (list.length) {
|
|
996
|
+
events = list;
|
|
997
|
+
used = "inspect";
|
|
998
|
+
}
|
|
999
|
+
} catch (error) {
|
|
1000
|
+
traces.inspect = { error: String(error?.message ?? error) };
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
if (!events.length && typeof sessionController.page === "function") {
|
|
1005
|
+
try {
|
|
1006
|
+
// Page from whatever cursor inspect gave us, else from the very start.
|
|
1007
|
+
const cursor = Number(traces.inspect?.lastSeq ?? 0);
|
|
1008
|
+
const page = await sessionController.page({
|
|
1009
|
+
address: { kind: "session", sessionId },
|
|
1010
|
+
throughSeq: Number.isSafeInteger(cursor) && cursor >= 0 ? cursor : 0,
|
|
1011
|
+
maxMessages: 400,
|
|
1012
|
+
}, signal);
|
|
1013
|
+
const list = (page?.records ?? []).map((record) => record?.event).filter(Boolean);
|
|
1014
|
+
traces.page = {
|
|
1015
|
+
cursor,
|
|
1016
|
+
recordCount: list.length,
|
|
1017
|
+
hasMore: page?.hasMore ?? null,
|
|
1018
|
+
firstSeq: list[0]?.seq ?? null,
|
|
1019
|
+
lastSeq: list[list.length - 1]?.seq ?? null,
|
|
1020
|
+
};
|
|
1021
|
+
if (list.length) {
|
|
1022
|
+
events = list;
|
|
1023
|
+
used = "page";
|
|
1024
|
+
}
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
traces.page = { error: String(error?.message ?? error) };
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
958
1029
|
|
|
959
1030
|
const texts = [];
|
|
960
|
-
const allRecords = page?.records ?? [];
|
|
961
1031
|
const typeTally = {};
|
|
962
1032
|
let firstUserEvent = null;
|
|
963
|
-
|
|
964
|
-
|
|
1033
|
+
let userEventCount = 0;
|
|
1034
|
+
for (const event of events) {
|
|
965
1035
|
if (!event) continue;
|
|
966
1036
|
const key = event.type ?? "(none)";
|
|
967
1037
|
typeTally[key] = (typeTally[key] ?? 0) + 1;
|
|
968
1038
|
if (event.type !== "user/message") continue;
|
|
1039
|
+
userEventCount += 1;
|
|
969
1040
|
if (!firstUserEvent) firstUserEvent = event;
|
|
970
1041
|
if (event?.data?.source?.kind !== "user") continue;
|
|
971
1042
|
const text = textFromContentBlocks(event.data.content);
|
|
@@ -976,6 +1047,7 @@ export function apply(ctx, config = {}) {
|
|
|
976
1047
|
const payload = {
|
|
977
1048
|
ok: true,
|
|
978
1049
|
sessionId,
|
|
1050
|
+
source: used,
|
|
979
1051
|
total: texts.length,
|
|
980
1052
|
// Newest last, so the caller can slice from the end.
|
|
981
1053
|
messages: texts.slice(-limit),
|
|
@@ -986,12 +1058,13 @@ export function apply(ctx, config = {}) {
|
|
|
986
1058
|
// why is to look at what actually arrived. Loopback-only, and truncated.
|
|
987
1059
|
if ((url.searchParams.get("debug") ?? "") === "1") {
|
|
988
1060
|
payload.diagnostics = {
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1061
|
+
used,
|
|
1062
|
+
eventCount: events.length,
|
|
1063
|
+
userEventCount,
|
|
992
1064
|
eventTypes: typeTally,
|
|
993
|
-
|
|
994
|
-
|
|
1065
|
+
traces,
|
|
1066
|
+
firstEventKeys: events[0] ? Object.keys(events[0]) : [],
|
|
1067
|
+
firstUserEventSample: firstUserEvent ? JSON.stringify(firstUserEvent).slice(0, 1500) : null,
|
|
995
1068
|
};
|
|
996
1069
|
}
|
|
997
1070
|
|
|
@@ -1011,11 +1084,18 @@ export function apply(ctx, config = {}) {
|
|
|
1011
1084
|
const missing = sessionController
|
|
1012
1085
|
? REQUIRED_CONTROLLER_METHODS.filter((name) => typeof sessionController[name] !== "function")
|
|
1013
1086
|
: REQUIRED_CONTROLLER_METHODS.slice();
|
|
1087
|
+
const dsh = readDshVersion();
|
|
1014
1088
|
sendJson(res, 200, {
|
|
1015
1089
|
ok: missing.length === 0,
|
|
1016
1090
|
controllerAvailable: Boolean(sessionController),
|
|
1017
1091
|
required: REQUIRED_CONTROLLER_METHODS,
|
|
1018
1092
|
missing,
|
|
1093
|
+
// The method list above is the real compatibility signal. The version is
|
|
1094
|
+
// context: a newer DSH can keep every method and still change what one of
|
|
1095
|
+
// them means, and that is the one failure nothing here can detect.
|
|
1096
|
+
testedWith: TESTED_WITH_DSH,
|
|
1097
|
+
dshVersion: dsh?.version ?? null,
|
|
1098
|
+
versionMatchesTested: dsh?.version ? dsh.version === TESTED_WITH_DSH : null,
|
|
1019
1099
|
});
|
|
1020
1100
|
} catch (error) {
|
|
1021
1101
|
sendJson(res, 500, { ok: false, error: String(error?.message ?? error) });
|