@oh-my-pi/omp-stats 18.1.22 → 18.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/THIRD-PARTY-NOTICES.txt +54 -25
- package/dist/client/index.css +1 -1
- package/dist/client/styles.css +20 -0
- package/dist/types/db.d.ts +9 -1
- package/dist/types/parser.d.ts +18 -17
- package/dist/types/sync-worker.d.ts +4 -2
- package/package.json +4 -4
- package/src/aggregator.ts +51 -30
- package/src/client/styles.css +30 -0
- package/src/db.ts +98 -12
- package/src/parser.ts +137 -31
- package/src/port-conflict.ts +18 -7
- package/src/sync-worker.ts +11 -4
- package/src/trace.ts +49 -13
package/src/trace.ts
CHANGED
|
@@ -854,22 +854,54 @@ export async function getTraceEntry(fileParam: string, entryId: string): Promise
|
|
|
854
854
|
// ---------------------------------------------------------------------------
|
|
855
855
|
// Session list
|
|
856
856
|
|
|
857
|
-
|
|
857
|
+
interface SessionMetadata {
|
|
858
|
+
title: string | null;
|
|
859
|
+
cwd: string | null;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const metadataCache = new Map<string, SessionMetadata & { mtimeMs: number }>();
|
|
858
863
|
|
|
859
|
-
/** Read
|
|
860
|
-
async function
|
|
864
|
+
/** Read enough decompressed transcript bytes to cover the title slot and session header. */
|
|
865
|
+
async function readSessionMetadataHead(file: string): Promise<string> {
|
|
866
|
+
if (!file.endsWith(".gz")) return Bun.file(file).slice(0, TITLE_SCAN_BYTES).text();
|
|
867
|
+
|
|
868
|
+
const reader = Bun.file(file).stream().pipeThrough(new DecompressionStream("gzip")).getReader();
|
|
869
|
+
const decoder = new TextDecoder();
|
|
870
|
+
let remaining = TITLE_SCAN_BYTES;
|
|
871
|
+
let head = "";
|
|
872
|
+
try {
|
|
873
|
+
while (remaining > 0) {
|
|
874
|
+
const { done, value } = await reader.read();
|
|
875
|
+
if (done) {
|
|
876
|
+
head += decoder.decode();
|
|
877
|
+
break;
|
|
878
|
+
}
|
|
879
|
+
const chunk = value.subarray(0, remaining);
|
|
880
|
+
remaining -= chunk.byteLength;
|
|
881
|
+
head += decoder.decode(chunk, { stream: remaining > 0 && chunk.byteLength === value.byteLength });
|
|
882
|
+
if (chunk.byteLength < value.byteLength) break;
|
|
883
|
+
}
|
|
884
|
+
} finally {
|
|
885
|
+
await reader.cancel();
|
|
886
|
+
}
|
|
887
|
+
return head;
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/** Read list metadata from the fixed title slot and session header. */
|
|
891
|
+
async function readSessionMetadata(file: string): Promise<SessionMetadata> {
|
|
861
892
|
let mtimeMs: number;
|
|
862
893
|
try {
|
|
863
894
|
mtimeMs = (await fs.stat(file)).mtimeMs;
|
|
864
895
|
} catch {
|
|
865
|
-
return null;
|
|
896
|
+
return { title: null, cwd: null };
|
|
866
897
|
}
|
|
867
|
-
const cached =
|
|
868
|
-
if (cached && cached.mtimeMs === mtimeMs) return cached
|
|
898
|
+
const cached = metadataCache.get(file);
|
|
899
|
+
if (cached && cached.mtimeMs === mtimeMs) return cached;
|
|
869
900
|
|
|
870
901
|
let title: string | null = null;
|
|
902
|
+
let cwd: string | null = null;
|
|
871
903
|
try {
|
|
872
|
-
const head = await
|
|
904
|
+
const head = await readSessionMetadataHead(file);
|
|
873
905
|
for (const line of head.split("\n")) {
|
|
874
906
|
if (!line.trim()) continue;
|
|
875
907
|
let parsed: EntryView;
|
|
@@ -881,18 +913,20 @@ async function readSessionTitle(file: string): Promise<string | null> {
|
|
|
881
913
|
}
|
|
882
914
|
if (parsed.type === "title" && typeof parsed.title === "string") {
|
|
883
915
|
title = parsed.title;
|
|
884
|
-
|
|
916
|
+
continue;
|
|
885
917
|
}
|
|
886
918
|
if (parsed.type === "session") {
|
|
887
|
-
if (typeof parsed.title === "string") title = parsed.title;
|
|
919
|
+
if (title === null && typeof parsed.title === "string") title = parsed.title;
|
|
920
|
+
if (typeof parsed.cwd === "string") cwd = parsed.cwd;
|
|
888
921
|
break;
|
|
889
922
|
}
|
|
890
923
|
}
|
|
891
924
|
} catch {
|
|
892
|
-
// Unreadable head (gc'd, gz, permission) → keep the row
|
|
925
|
+
// Unreadable head (gc'd, gz, permission) → keep the row with path-derived metadata.
|
|
893
926
|
}
|
|
894
|
-
|
|
895
|
-
|
|
927
|
+
const metadata = { mtimeMs, title, cwd };
|
|
928
|
+
metadataCache.set(file, metadata);
|
|
929
|
+
return metadata;
|
|
896
930
|
}
|
|
897
931
|
|
|
898
932
|
interface SummaryFold extends SessionSummary {
|
|
@@ -1027,7 +1061,9 @@ export async function listSessionSummaries(limit = 100, q?: string): Promise<Ses
|
|
|
1027
1061
|
const folds = [...byRoot.values()].sort((a, b) => b.endedAt - a.endedAt).slice(0, LIST_FOLD_LIMIT);
|
|
1028
1062
|
await Promise.all(
|
|
1029
1063
|
folds.map(async fold => {
|
|
1030
|
-
|
|
1064
|
+
const metadata = await readSessionMetadata(fold.file);
|
|
1065
|
+
fold.title = metadata.title;
|
|
1066
|
+
fold.folder = metadata.cwd ?? extractFolderFromPath(fold.file);
|
|
1031
1067
|
fold.models = [...fold.modelSet].sort();
|
|
1032
1068
|
}),
|
|
1033
1069
|
);
|