@juspay/shooter 1.17.0 → 1.18.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/build/client/_app/immutable/assets/{0.B0O0vCnX.css → 0.NV8k8wxG.css} +1 -1
- package/build/client/_app/immutable/assets/0.NV8k8wxG.css.br +0 -0
- package/build/client/_app/immutable/assets/{0.B0O0vCnX.css.gz → 0.NV8k8wxG.css.gz} +0 -0
- package/build/client/_app/immutable/chunks/{BctvtE4d.js → 8lO1IL7u.js} +1 -1
- package/build/client/_app/immutable/chunks/8lO1IL7u.js.br +0 -0
- package/build/client/_app/immutable/chunks/{BctvtE4d.js.gz → 8lO1IL7u.js.gz} +0 -0
- package/build/client/_app/immutable/chunks/B9WQy_3X.js +1 -0
- package/build/client/_app/immutable/chunks/B9WQy_3X.js.br +0 -0
- package/build/client/_app/immutable/chunks/B9WQy_3X.js.gz +0 -0
- package/build/client/_app/immutable/chunks/BdtLzPpO.js +1 -0
- package/build/client/_app/immutable/chunks/BdtLzPpO.js.br +0 -0
- package/build/client/_app/immutable/chunks/BdtLzPpO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/{CjfxuHdN.js → DJvX78LW.js} +1 -1
- package/build/client/_app/immutable/chunks/DJvX78LW.js.br +0 -0
- package/build/client/_app/immutable/chunks/DJvX78LW.js.gz +0 -0
- package/build/client/_app/immutable/chunks/nWG9RHyB.js +3 -0
- package/build/client/_app/immutable/chunks/nWG9RHyB.js.br +0 -0
- package/build/client/_app/immutable/chunks/nWG9RHyB.js.gz +0 -0
- package/build/client/_app/immutable/entry/{app.CNaTe-zm.js → app.f46Ko1hu.js} +2 -2
- package/build/client/_app/immutable/entry/app.f46Ko1hu.js.br +0 -0
- package/build/client/_app/immutable/entry/app.f46Ko1hu.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.BVDjNnXt.js +1 -0
- package/build/client/_app/immutable/entry/start.BVDjNnXt.js.br +2 -0
- package/build/client/_app/immutable/entry/start.BVDjNnXt.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{0.C3ELOf4c.js → 0.D_9EwVmq.js} +1 -1
- package/build/client/_app/immutable/nodes/0.D_9EwVmq.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.D_9EwVmq.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{1.Fqso94b3.js → 1.C4eFlqSB.js} +1 -1
- package/build/client/_app/immutable/nodes/1.C4eFlqSB.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.C4eFlqSB.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{2.BusCVJWk.js → 2.CdC092Za.js} +1 -1
- package/build/client/_app/immutable/nodes/2.CdC092Za.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.CdC092Za.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{3.DUlpocIc.js → 3.Dhf4ZWW0.js} +1 -1
- package/build/client/_app/immutable/nodes/3.Dhf4ZWW0.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.Dhf4ZWW0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{6.CG4eKRH0.js → 6.B3SEB_li.js} +1 -1
- package/build/client/_app/immutable/nodes/6.B3SEB_li.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.B3SEB_li.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{7.DHilxD1o.js → 7.DV8cJ1lX.js} +1 -1
- package/build/client/_app/immutable/nodes/7.DV8cJ1lX.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.DV8cJ1lX.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{8.BjKgvSie.js → 8.Bs362gyb.js} +2 -2
- package/build/client/_app/immutable/nodes/8.Bs362gyb.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.Bs362gyb.js.gz +0 -0
- package/build/client/_app/immutable/nodes/{9.BRT6HOXB.js → 9.Cf7_3uqT.js} +1 -1
- package/build/client/_app/immutable/nodes/9.Cf7_3uqT.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.Cf7_3uqT.js.gz +0 -0
- package/build/client/_app/version.json +1 -1
- package/build/client/_app/version.json.br +0 -0
- package/build/client/_app/version.json.gz +0 -0
- package/build/server/chunks/{0-BWFSL107.js → 0-Cd7jY0a7.js} +3 -3
- package/build/server/chunks/{0-BWFSL107.js.map → 0-Cd7jY0a7.js.map} +1 -1
- package/build/server/chunks/{1-Bw5KlAjL.js → 1-C4BOGoJY.js} +2 -2
- package/build/server/chunks/{1-Bw5KlAjL.js.map → 1-C4BOGoJY.js.map} +1 -1
- package/build/server/chunks/{2-CQ3yYSVK.js → 2-Ba0mNwJ6.js} +2 -2
- package/build/server/chunks/{2-CQ3yYSVK.js.map → 2-Ba0mNwJ6.js.map} +1 -1
- package/build/server/chunks/{3-DZ4H9hPs.js → 3-Pg8t1uJU.js} +2 -2
- package/build/server/chunks/{3-DZ4H9hPs.js.map → 3-Pg8t1uJU.js.map} +1 -1
- package/build/server/chunks/{6-BZ0enR6b.js → 6-D8xbnTSo.js} +2 -2
- package/build/server/chunks/{6-BZ0enR6b.js.map → 6-D8xbnTSo.js.map} +1 -1
- package/build/server/chunks/{7-Lg8imTZn.js → 7-CkVK06S0.js} +2 -2
- package/build/server/chunks/{7-Lg8imTZn.js.map → 7-CkVK06S0.js.map} +1 -1
- package/build/server/chunks/{8-DKs4yOL7.js → 8-C8qVhrds.js} +2 -2
- package/build/server/chunks/{8-DKs4yOL7.js.map → 8-C8qVhrds.js.map} +1 -1
- package/build/server/chunks/{9-UNmpUWDY.js → 9-fL5zqN0T.js} +2 -2
- package/build/server/chunks/{9-UNmpUWDY.js.map → 9-fL5zqN0T.js.map} +1 -1
- package/build/server/chunks/{_server.ts-B1z0q6qZ.js → _server.ts-BA_uWcPw.js} +4 -5
- package/build/server/chunks/_server.ts-BA_uWcPw.js.map +1 -0
- package/build/server/chunks/{_server.ts-5wx4ZppI.js → _server.ts-Bu3s5hfv.js} +3 -3
- package/build/server/chunks/{_server.ts-5wx4ZppI.js.map → _server.ts-Bu3s5hfv.js.map} +1 -1
- package/build/server/chunks/{_server.ts-CKXVBbwb.js → _server.ts-CwAjt91u.js} +8 -8
- package/build/server/chunks/_server.ts-CwAjt91u.js.map +1 -0
- package/build/server/chunks/{_server.ts-CgHc1Zpx.js → _server.ts-DZP2lhaY.js} +3 -3
- package/build/server/chunks/{_server.ts-CgHc1Zpx.js.map → _server.ts-DZP2lhaY.js.map} +1 -1
- package/build/server/chunks/{_server.ts-BMMTS86y.js → _server.ts-DZgfQKiH.js} +3 -4
- package/build/server/chunks/{_server.ts-BMMTS86y.js.map → _server.ts-DZgfQKiH.js.map} +1 -1
- package/build/server/chunks/{_server.ts-Bt7EAfjo.js → _server.ts-MbnroWEF.js} +25 -48
- package/build/server/chunks/_server.ts-MbnroWEF.js.map +1 -0
- package/build/server/chunks/{pty-manager-RmhVe2Ez.js → pty-manager-DmNSCKAr.js} +99 -2
- package/build/server/chunks/pty-manager-DmNSCKAr.js.map +1 -0
- package/build/server/chunks/qwen-reader-DGfUbKaJ.js +2112 -0
- package/build/server/chunks/qwen-reader-DGfUbKaJ.js.map +1 -0
- package/build/server/chunks/{registry-DzJj2E6I.js → registry-Kcw2UCMv.js} +55 -23
- package/build/server/chunks/registry-Kcw2UCMv.js.map +1 -0
- package/build/server/index.js +1 -1
- package/build/server/index.js.map +1 -1
- package/build/server/manifest.js +15 -15
- package/build/server/manifest.js.map +1 -1
- package/package.json +2 -2
- package/scripts/e2e-all-features.sh +165 -0
- package/scripts/e2e-cross-terminal.sh +168 -0
- package/server.ts +12 -0
- package/src/lib/modules/client/common/provider.ts +0 -2
- package/src/lib/modules/client/terminal/ChatView.svelte +9 -2
- package/src/lib/modules/client/terminal/LaunchSheet.svelte +3 -0
- package/src/lib/modules/server/sessions/amp-reader.ts +439 -0
- package/src/lib/modules/server/sessions/copilot-reader.ts +542 -0
- package/src/lib/modules/server/sessions/cursor-reader.ts +634 -0
- package/src/lib/modules/server/sessions/gemini-reader.ts +48 -25
- package/src/lib/modules/server/sessions/opencode-reader.ts +13 -12
- package/src/lib/modules/server/sessions/process-detector.ts +37 -60
- package/src/lib/modules/server/sessions/provider-paths.ts +173 -0
- package/src/lib/modules/server/sessions/qwen-reader.ts +41 -15
- package/src/lib/modules/server/sessions/registry.ts +55 -14
- package/src/lib/modules/server/terminal/generic-session-watcher.ts +163 -0
- package/src/lib/modules/server/terminal/pty-manager.ts +51 -0
- package/src/lib/modules/server/ws/session-handler.ts +11 -1
- package/src/lib/theme.css +1 -2
- package/src/lib/types/generated/Sessions.ts +1 -4
- package/src/lib/types/server.ts +23 -6
- package/src/lib/types/sessions.ts +1 -10
- package/src/routes/api/sessions/connect/+server.ts +7 -3
- package/build/client/_app/immutable/assets/0.B0O0vCnX.css.br +0 -0
- package/build/client/_app/immutable/chunks/BctvtE4d.js.br +0 -0
- package/build/client/_app/immutable/chunks/BxFShcQO.js +0 -1
- package/build/client/_app/immutable/chunks/BxFShcQO.js.br +0 -0
- package/build/client/_app/immutable/chunks/BxFShcQO.js.gz +0 -0
- package/build/client/_app/immutable/chunks/ByzqAuXw.js +0 -3
- package/build/client/_app/immutable/chunks/ByzqAuXw.js.br +0 -0
- package/build/client/_app/immutable/chunks/ByzqAuXw.js.gz +0 -0
- package/build/client/_app/immutable/chunks/CjfxuHdN.js.br +0 -0
- package/build/client/_app/immutable/chunks/CjfxuHdN.js.gz +0 -0
- package/build/client/_app/immutable/chunks/Pw0jDB7M.js +0 -1
- package/build/client/_app/immutable/chunks/Pw0jDB7M.js.br +0 -0
- package/build/client/_app/immutable/chunks/Pw0jDB7M.js.gz +0 -0
- package/build/client/_app/immutable/entry/app.CNaTe-zm.js.br +0 -0
- package/build/client/_app/immutable/entry/app.CNaTe-zm.js.gz +0 -0
- package/build/client/_app/immutable/entry/start.hxYnjcDu.js +0 -1
- package/build/client/_app/immutable/entry/start.hxYnjcDu.js.br +0 -0
- package/build/client/_app/immutable/entry/start.hxYnjcDu.js.gz +0 -0
- package/build/client/_app/immutable/nodes/0.C3ELOf4c.js.br +0 -0
- package/build/client/_app/immutable/nodes/0.C3ELOf4c.js.gz +0 -0
- package/build/client/_app/immutable/nodes/1.Fqso94b3.js.br +0 -0
- package/build/client/_app/immutable/nodes/1.Fqso94b3.js.gz +0 -0
- package/build/client/_app/immutable/nodes/2.BusCVJWk.js.br +0 -0
- package/build/client/_app/immutable/nodes/2.BusCVJWk.js.gz +0 -0
- package/build/client/_app/immutable/nodes/3.DUlpocIc.js.br +0 -0
- package/build/client/_app/immutable/nodes/3.DUlpocIc.js.gz +0 -0
- package/build/client/_app/immutable/nodes/6.CG4eKRH0.js.br +0 -0
- package/build/client/_app/immutable/nodes/6.CG4eKRH0.js.gz +0 -0
- package/build/client/_app/immutable/nodes/7.DHilxD1o.js.br +0 -0
- package/build/client/_app/immutable/nodes/7.DHilxD1o.js.gz +0 -0
- package/build/client/_app/immutable/nodes/8.BjKgvSie.js.br +0 -0
- package/build/client/_app/immutable/nodes/8.BjKgvSie.js.gz +0 -0
- package/build/client/_app/immutable/nodes/9.BRT6HOXB.js.br +0 -0
- package/build/client/_app/immutable/nodes/9.BRT6HOXB.js.gz +0 -0
- package/build/server/chunks/_server.ts-B1z0q6qZ.js.map +0 -1
- package/build/server/chunks/_server.ts-Bt7EAfjo.js.map +0 -1
- package/build/server/chunks/_server.ts-CKXVBbwb.js.map +0 -1
- package/build/server/chunks/opencode-db-path-BwaPufWf.js +0 -411
- package/build/server/chunks/opencode-db-path-BwaPufWf.js.map +0 -1
- package/build/server/chunks/pty-manager-RmhVe2Ez.js.map +0 -1
- package/build/server/chunks/qwen-reader-2fTFuC_D.js +0 -622
- package/build/server/chunks/qwen-reader-2fTFuC_D.js.map +0 -1
- package/build/server/chunks/registry-DzJj2E6I.js.map +0 -1
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
import * as crypto from 'crypto';
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import { homedir } from 'os';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
|
|
6
|
-
const MAX_GEMINI_FILE_BYTES = 64 * 1024 * 1024;
|
|
7
|
-
const GEMINI_TMP = path.join(homedir(), ".gemini", "tmp");
|
|
8
|
-
const GEMINI_PROJECTS_JSON = path.join(homedir(), ".gemini", "projects.json");
|
|
9
|
-
function detectActiveGeminiSessions(thresholdMs) {
|
|
10
|
-
const cutoff = Date.now() - thresholdMs;
|
|
11
|
-
const projectHashToCwd = buildHashToCwdMap();
|
|
12
|
-
const projectDirs = collectProjectHashDirs();
|
|
13
|
-
const out = [];
|
|
14
|
-
for (const hashDir of projectDirs) {
|
|
15
|
-
const hash = path.basename(hashDir);
|
|
16
|
-
const cwd = projectHashToCwd.get(hash) ?? hash;
|
|
17
|
-
const chatFiles = collectChatFiles(hashDir);
|
|
18
|
-
for (const chatFile of chatFiles) {
|
|
19
|
-
try {
|
|
20
|
-
const stat = fs.statSync(chatFile);
|
|
21
|
-
if (stat.mtimeMs < cutoff) {
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
const record = readChatFile(chatFile);
|
|
25
|
-
if (record) {
|
|
26
|
-
out.push({
|
|
27
|
-
cwd,
|
|
28
|
-
id: record.sessionId,
|
|
29
|
-
startedAt: new Date(record.startTime).getTime()
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
} catch {
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
const logsPath = path.join(hashDir, "logs.json");
|
|
36
|
-
try {
|
|
37
|
-
const stat = fs.statSync(logsPath);
|
|
38
|
-
if (stat.mtimeMs < cutoff) {
|
|
39
|
-
continue;
|
|
40
|
-
}
|
|
41
|
-
const entries = readLogsJson(logsPath);
|
|
42
|
-
if (entries.length > 0) {
|
|
43
|
-
const last = entries[entries.length - 1];
|
|
44
|
-
if (last) {
|
|
45
|
-
const alreadyAdded = out.some((o) => o.id === last.sessionId && o.cwd === cwd);
|
|
46
|
-
if (!alreadyAdded) {
|
|
47
|
-
out.push({
|
|
48
|
-
cwd,
|
|
49
|
-
id: last.sessionId,
|
|
50
|
-
startedAt: new Date(entries[0]?.timestamp ?? last.timestamp).getTime()
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
return out;
|
|
59
|
-
}
|
|
60
|
-
function getGeminiConversation(sessionId, offset = 0, limit = 200) {
|
|
61
|
-
const projectDirs = collectProjectHashDirs();
|
|
62
|
-
for (const hashDir of projectDirs) {
|
|
63
|
-
const chatFile = findChatFileForSession(hashDir, sessionId);
|
|
64
|
-
if (chatFile) {
|
|
65
|
-
return conversationFromChatFile(chatFile, offset, limit);
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
for (const hashDir of projectDirs) {
|
|
69
|
-
const messages = conversationFromLogsJson(hashDir, sessionId);
|
|
70
|
-
if (messages.length > 0) {
|
|
71
|
-
if (offset === 0 && messages.length > limit) {
|
|
72
|
-
return messages.slice(messages.length - limit);
|
|
73
|
-
}
|
|
74
|
-
return messages.slice(offset, offset + limit);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return [];
|
|
78
|
-
}
|
|
79
|
-
function listGeminiProjects() {
|
|
80
|
-
const projectHashToCwd = buildHashToCwdMap();
|
|
81
|
-
const projectDirs = collectProjectHashDirs();
|
|
82
|
-
const byCwd = /* @__PURE__ */ new Map();
|
|
83
|
-
for (const hashDir of projectDirs) {
|
|
84
|
-
const hash = path.basename(hashDir);
|
|
85
|
-
const cwd = projectHashToCwd.get(hash) ?? hash;
|
|
86
|
-
const chatSessions = collectChatSessions(hashDir, cwd);
|
|
87
|
-
if (chatSessions.length > 0) {
|
|
88
|
-
for (const session of chatSessions) {
|
|
89
|
-
appendToMap(byCwd, cwd, session);
|
|
90
|
-
}
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
const logSessions = sessionsFromLogsJson(hashDir, cwd);
|
|
94
|
-
for (const session of logSessions) {
|
|
95
|
-
appendToMap(byCwd, cwd, session);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const projects = [];
|
|
99
|
-
for (const [cwd, sessions] of byCwd) {
|
|
100
|
-
sessions.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
|
|
101
|
-
const segments = cwd.split("/").filter(Boolean);
|
|
102
|
-
const isRawHash = /^[0-9a-f]{64}$/i.test(cwd);
|
|
103
|
-
projects.push({
|
|
104
|
-
fullPath: cwd,
|
|
105
|
-
id: shortHash$1(cwd),
|
|
106
|
-
lastModified: sessions[0]?.modified ?? "",
|
|
107
|
-
name: isRawHash ? `Gemini (${cwd.slice(0, 8)})` : segments.slice(-2).join("/"),
|
|
108
|
-
sessionCount: sessions.length,
|
|
109
|
-
sessions
|
|
110
|
-
});
|
|
111
|
-
}
|
|
112
|
-
return projects.sort(
|
|
113
|
-
(a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime()
|
|
114
|
-
);
|
|
115
|
-
}
|
|
116
|
-
function appendToMap(map, key, value) {
|
|
117
|
-
let bucket = map.get(key);
|
|
118
|
-
if (!bucket) {
|
|
119
|
-
bucket = [];
|
|
120
|
-
map.set(key, bucket);
|
|
121
|
-
}
|
|
122
|
-
bucket.push(value);
|
|
123
|
-
}
|
|
124
|
-
function buildHashToCwdMap() {
|
|
125
|
-
const map = /* @__PURE__ */ new Map();
|
|
126
|
-
try {
|
|
127
|
-
const raw = fs.readFileSync(GEMINI_PROJECTS_JSON, "utf-8");
|
|
128
|
-
const data = JSON.parse(raw);
|
|
129
|
-
for (const [slugOrHash, absolutePath] of Object.entries(data)) {
|
|
130
|
-
map.set(slugOrHash, absolutePath);
|
|
131
|
-
const computed = crypto.createHash("sha256").update(absolutePath).digest("hex");
|
|
132
|
-
map.set(computed, absolutePath);
|
|
133
|
-
}
|
|
134
|
-
} catch {
|
|
135
|
-
}
|
|
136
|
-
return map;
|
|
137
|
-
}
|
|
138
|
-
function cleanTitle(text) {
|
|
139
|
-
const first = text.replace(/\s+/g, " ").trim().split("\n")[0]?.trim() ?? "";
|
|
140
|
-
if (!first) {
|
|
141
|
-
return "Untitled Session";
|
|
142
|
-
}
|
|
143
|
-
return first.length > 80 ? `${first.slice(0, 77)}...` : first;
|
|
144
|
-
}
|
|
145
|
-
function collectChatFiles(hashDir) {
|
|
146
|
-
const chatsDir = path.join(hashDir, "chats");
|
|
147
|
-
try {
|
|
148
|
-
return fs.readdirSync(chatsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.startsWith("session-") && e.name.endsWith(".json")).map((e) => path.join(chatsDir, e.name));
|
|
149
|
-
} catch {
|
|
150
|
-
return [];
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
function collectChatSessions(hashDir, cwd) {
|
|
154
|
-
const chatFiles = collectChatFiles(hashDir);
|
|
155
|
-
const sessions = [];
|
|
156
|
-
for (const chatFile of chatFiles) {
|
|
157
|
-
try {
|
|
158
|
-
const stat = fs.statSync(chatFile);
|
|
159
|
-
const record = readChatFile(chatFile);
|
|
160
|
-
if (!record) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
const userMessages = record.messages.filter((m) => m.type === "user");
|
|
164
|
-
const firstUserMsg = userMessages[0];
|
|
165
|
-
let title = "Untitled Session";
|
|
166
|
-
if (firstUserMsg) {
|
|
167
|
-
title = cleanTitle(extractTextFromContent(firstUserMsg.content));
|
|
168
|
-
}
|
|
169
|
-
sessions.push({
|
|
170
|
-
created: record.startTime,
|
|
171
|
-
gitBranch: "",
|
|
172
|
-
id: record.sessionId,
|
|
173
|
-
messageCount: record.messages.length,
|
|
174
|
-
modified: stat.mtime.toISOString(),
|
|
175
|
-
projectPath: cwd,
|
|
176
|
-
source: "gemini",
|
|
177
|
-
summary: record.summary ?? "",
|
|
178
|
-
title
|
|
179
|
-
});
|
|
180
|
-
} catch {
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
return sessions;
|
|
184
|
-
}
|
|
185
|
-
function collectProjectHashDirs() {
|
|
186
|
-
try {
|
|
187
|
-
return fs.readdirSync(GEMINI_TMP, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => path.join(GEMINI_TMP, e.name));
|
|
188
|
-
} catch {
|
|
189
|
-
return [];
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
function conversationFromChatFile(chatFile, offset, limit) {
|
|
193
|
-
try {
|
|
194
|
-
const record = readChatFile(chatFile);
|
|
195
|
-
if (!record) {
|
|
196
|
-
return [];
|
|
197
|
-
}
|
|
198
|
-
const messages = record.messages.filter((m) => m.type === "user" || m.type === "gemini").map(messageRecordToMessage);
|
|
199
|
-
if (offset === 0 && messages.length > limit) {
|
|
200
|
-
let startIdx = messages.length - limit;
|
|
201
|
-
while (startIdx > 0 && messages[startIdx]?.role !== "user") {
|
|
202
|
-
startIdx--;
|
|
203
|
-
}
|
|
204
|
-
return messages.slice(startIdx);
|
|
205
|
-
}
|
|
206
|
-
return messages.slice(offset, offset + limit);
|
|
207
|
-
} catch (err) {
|
|
208
|
-
console.error("[gemini] Failed to read chat file:", err);
|
|
209
|
-
return [];
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
function conversationFromLogsJson(hashDir, sessionId) {
|
|
213
|
-
const logsPath = path.join(hashDir, "logs.json");
|
|
214
|
-
const entries = readLogsJson(logsPath);
|
|
215
|
-
return entries.filter((e) => e.sessionId === sessionId).map(
|
|
216
|
-
(entry) => ({
|
|
217
|
-
id: `${entry.sessionId}-${String(entry.messageId)}`,
|
|
218
|
-
parts: [{ content: entry.message, type: "text" }],
|
|
219
|
-
role: "user",
|
|
220
|
-
timestamp: entry.timestamp
|
|
221
|
-
})
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
function extractTextFromContent(content) {
|
|
225
|
-
if (typeof content === "string") {
|
|
226
|
-
return content;
|
|
227
|
-
}
|
|
228
|
-
return content.filter((p) => "text" in p).map((p) => p.text).join(" ");
|
|
229
|
-
}
|
|
230
|
-
function findChatFileForSession(hashDir, sessionId) {
|
|
231
|
-
const shortId = sessionId.slice(0, 8);
|
|
232
|
-
const chatsDir = path.join(hashDir, "chats");
|
|
233
|
-
try {
|
|
234
|
-
const entries = fs.readdirSync(chatsDir, { withFileTypes: true });
|
|
235
|
-
for (const entry of entries) {
|
|
236
|
-
if (!entry.isFile() || !entry.name.startsWith("session-") || !entry.name.endsWith(".json")) {
|
|
237
|
-
continue;
|
|
238
|
-
}
|
|
239
|
-
if (entry.name.includes(shortId)) {
|
|
240
|
-
return path.join(chatsDir, entry.name);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
for (const entry of entries) {
|
|
244
|
-
if (!entry.isFile() || !entry.name.startsWith("session-") || !entry.name.endsWith(".json")) {
|
|
245
|
-
continue;
|
|
246
|
-
}
|
|
247
|
-
const record = readChatFile(path.join(chatsDir, entry.name));
|
|
248
|
-
if (record?.sessionId === sessionId) {
|
|
249
|
-
return path.join(chatsDir, entry.name);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
} catch {
|
|
253
|
-
}
|
|
254
|
-
return null;
|
|
255
|
-
}
|
|
256
|
-
function isGeminiLogEntry(v) {
|
|
257
|
-
if (typeof v !== "object" || v === null) {
|
|
258
|
-
return false;
|
|
259
|
-
}
|
|
260
|
-
const obj = v;
|
|
261
|
-
return typeof obj.sessionId === "string" && typeof obj.messageId === "number" && typeof obj.message === "string" && typeof obj.timestamp === "string";
|
|
262
|
-
}
|
|
263
|
-
function messageRecordToMessage(record) {
|
|
264
|
-
const role = record.type === "user" ? "user" : "assistant";
|
|
265
|
-
const parts = [];
|
|
266
|
-
if (record.type === "gemini" && record.thoughts) {
|
|
267
|
-
for (const thought of record.thoughts) {
|
|
268
|
-
parts.push({ content: thought.summary ?? "", type: "thinking" });
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
const rawContent = record.content;
|
|
272
|
-
const contentParts = typeof rawContent === "string" ? [{ text: rawContent }] : rawContent;
|
|
273
|
-
for (const part of contentParts) {
|
|
274
|
-
if ("functionCall" in part) {
|
|
275
|
-
parts.push({
|
|
276
|
-
id: part.functionCall.id ?? part.functionCall.name,
|
|
277
|
-
input: part.functionCall.args,
|
|
278
|
-
toolName: part.functionCall.name,
|
|
279
|
-
type: "tool_use"
|
|
280
|
-
});
|
|
281
|
-
} else if ("thought" in part && part.thought === true) {
|
|
282
|
-
parts.push({ content: part.text, type: "thinking" });
|
|
283
|
-
} else if ("text" in part) {
|
|
284
|
-
parts.push({ content: part.text, type: "text" });
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
if (record.type === "gemini" && record.toolCalls) {
|
|
288
|
-
for (const tc of record.toolCalls) {
|
|
289
|
-
const alreadyInParts = parts.some((p) => p.type === "tool_use" && p.id === tc.id);
|
|
290
|
-
if (!alreadyInParts) {
|
|
291
|
-
parts.push({
|
|
292
|
-
id: tc.id,
|
|
293
|
-
input: tc.args,
|
|
294
|
-
toolName: tc.name,
|
|
295
|
-
type: "tool_use"
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
return {
|
|
301
|
-
id: record.id,
|
|
302
|
-
parts,
|
|
303
|
-
role,
|
|
304
|
-
timestamp: record.timestamp
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
function readChatFile(filePath) {
|
|
308
|
-
try {
|
|
309
|
-
if (fs.statSync(filePath).size > MAX_GEMINI_FILE_BYTES) {
|
|
310
|
-
return null;
|
|
311
|
-
}
|
|
312
|
-
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
313
|
-
} catch {
|
|
314
|
-
return null;
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
function readLogsJson(logsPath) {
|
|
318
|
-
try {
|
|
319
|
-
if (fs.statSync(logsPath).size > MAX_GEMINI_FILE_BYTES) {
|
|
320
|
-
return [];
|
|
321
|
-
}
|
|
322
|
-
const parsed = JSON.parse(fs.readFileSync(logsPath, "utf-8"));
|
|
323
|
-
return Array.isArray(parsed) ? parsed.filter(isGeminiLogEntry) : [];
|
|
324
|
-
} catch {
|
|
325
|
-
return [];
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
function sessionsFromLogsJson(hashDir, cwd) {
|
|
329
|
-
const logsPath = path.join(hashDir, "logs.json");
|
|
330
|
-
let stat;
|
|
331
|
-
try {
|
|
332
|
-
stat = fs.statSync(logsPath);
|
|
333
|
-
} catch {
|
|
334
|
-
return [];
|
|
335
|
-
}
|
|
336
|
-
const entries = readLogsJson(logsPath);
|
|
337
|
-
if (entries.length === 0) {
|
|
338
|
-
return [];
|
|
339
|
-
}
|
|
340
|
-
const bySession = /* @__PURE__ */ new Map();
|
|
341
|
-
for (const entry of entries) {
|
|
342
|
-
let bucket = bySession.get(entry.sessionId);
|
|
343
|
-
if (!bucket) {
|
|
344
|
-
bucket = [];
|
|
345
|
-
bySession.set(entry.sessionId, bucket);
|
|
346
|
-
}
|
|
347
|
-
bucket.push(entry);
|
|
348
|
-
}
|
|
349
|
-
const sessions = [];
|
|
350
|
-
for (const [sessionId, sessionEntries] of bySession) {
|
|
351
|
-
const first = sessionEntries[0];
|
|
352
|
-
const last = sessionEntries[sessionEntries.length - 1];
|
|
353
|
-
sessions.push({
|
|
354
|
-
created: first?.timestamp ?? stat.birthtime.toISOString(),
|
|
355
|
-
gitBranch: "",
|
|
356
|
-
id: sessionId,
|
|
357
|
-
messageCount: sessionEntries.length,
|
|
358
|
-
modified: last?.timestamp ?? stat.mtime.toISOString(),
|
|
359
|
-
projectPath: cwd,
|
|
360
|
-
source: "gemini",
|
|
361
|
-
summary: "",
|
|
362
|
-
title: cleanTitle(first?.message ?? "")
|
|
363
|
-
});
|
|
364
|
-
}
|
|
365
|
-
return sessions;
|
|
366
|
-
}
|
|
367
|
-
function shortHash$1(input) {
|
|
368
|
-
return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
|
|
369
|
-
}
|
|
370
|
-
const QWEN_PROJECTS = path.join(homedir(), ".qwen", "projects");
|
|
371
|
-
const PREFIX_BYTES = 64 * 1024;
|
|
372
|
-
const MAX_QWEN_FILE_BYTES = 16 * 1024 * 1024;
|
|
373
|
-
const SYSTEM_TAG_PREFIXES = [
|
|
374
|
-
"<command-name>",
|
|
375
|
-
"<local-command",
|
|
376
|
-
"<system-reminder>",
|
|
377
|
-
"<task-notification>"
|
|
378
|
-
];
|
|
379
|
-
function detectActiveQwenSessions(thresholdMs) {
|
|
380
|
-
const cutoff = Date.now() - thresholdMs;
|
|
381
|
-
const out = [];
|
|
382
|
-
for (const filePath of collectQwenFiles()) {
|
|
383
|
-
try {
|
|
384
|
-
const stat = fs.statSync(filePath);
|
|
385
|
-
if (stat.mtimeMs < cutoff) {
|
|
386
|
-
continue;
|
|
387
|
-
}
|
|
388
|
-
const meta = readMeta(readPrefix(filePath));
|
|
389
|
-
if (meta) {
|
|
390
|
-
out.push({ cwd: meta.cwd, id: meta.id, startedAt: stat.birthtimeMs });
|
|
391
|
-
}
|
|
392
|
-
} catch {
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
return out;
|
|
396
|
-
}
|
|
397
|
-
function getQwenConversation(sessionId, offset = 0, limit = 200) {
|
|
398
|
-
if (!/^[A-Za-z0-9_-]+$/.test(sessionId)) {
|
|
399
|
-
return [];
|
|
400
|
-
}
|
|
401
|
-
const filePath = collectQwenFiles().find((p) => path.basename(p) === `${sessionId}.jsonl`);
|
|
402
|
-
if (!filePath) {
|
|
403
|
-
return [];
|
|
404
|
-
}
|
|
405
|
-
try {
|
|
406
|
-
const messages = [];
|
|
407
|
-
for (const line of readQwenTextBounded(filePath).split("\n")) {
|
|
408
|
-
const trimmed = line.trim();
|
|
409
|
-
if (!trimmed) {
|
|
410
|
-
continue;
|
|
411
|
-
}
|
|
412
|
-
try {
|
|
413
|
-
const msg = qwenLineToMessage(JSON.parse(trimmed));
|
|
414
|
-
if (msg) {
|
|
415
|
-
messages.push(msg);
|
|
416
|
-
}
|
|
417
|
-
} catch {
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
if (offset === 0 && messages.length > limit) {
|
|
421
|
-
let startIdx = messages.length - limit;
|
|
422
|
-
while (startIdx > 0 && messages[startIdx].role !== "user") {
|
|
423
|
-
startIdx--;
|
|
424
|
-
}
|
|
425
|
-
return messages.slice(startIdx);
|
|
426
|
-
}
|
|
427
|
-
return messages.slice(offset, offset + limit);
|
|
428
|
-
} catch (error) {
|
|
429
|
-
console.error("[qwen] Failed to read conversation:", error);
|
|
430
|
-
return [];
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
function listQwenProjects() {
|
|
434
|
-
const byCwd = /* @__PURE__ */ new Map();
|
|
435
|
-
for (const filePath of collectQwenFiles()) {
|
|
436
|
-
let stat;
|
|
437
|
-
try {
|
|
438
|
-
stat = fs.statSync(filePath);
|
|
439
|
-
} catch {
|
|
440
|
-
continue;
|
|
441
|
-
}
|
|
442
|
-
const meta = readMeta(readPrefix(filePath));
|
|
443
|
-
if (!meta) {
|
|
444
|
-
continue;
|
|
445
|
-
}
|
|
446
|
-
const session = {
|
|
447
|
-
created: meta.started || stat.birthtime.toISOString(),
|
|
448
|
-
gitBranch: meta.gitBranch,
|
|
449
|
-
id: meta.id,
|
|
450
|
-
messageCount: 0,
|
|
451
|
-
modified: stat.mtime.toISOString(),
|
|
452
|
-
projectPath: meta.cwd,
|
|
453
|
-
source: "qwen",
|
|
454
|
-
summary: "",
|
|
455
|
-
title: meta.title || "Untitled Session"
|
|
456
|
-
};
|
|
457
|
-
const bucket = byCwd.get(meta.cwd);
|
|
458
|
-
if (bucket) {
|
|
459
|
-
bucket.push(session);
|
|
460
|
-
} else {
|
|
461
|
-
byCwd.set(meta.cwd, [session]);
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
const projects = [];
|
|
465
|
-
for (const [cwd, sessions] of byCwd) {
|
|
466
|
-
sessions.sort((a, b) => new Date(b.modified).getTime() - new Date(a.modified).getTime());
|
|
467
|
-
const segments = cwd.split("/").filter(Boolean);
|
|
468
|
-
projects.push({
|
|
469
|
-
fullPath: cwd,
|
|
470
|
-
id: shortHash(cwd),
|
|
471
|
-
lastModified: sessions[0]?.modified ?? "",
|
|
472
|
-
name: segments.slice(-2).join("/"),
|
|
473
|
-
sessionCount: sessions.length,
|
|
474
|
-
sessions
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
return projects.sort(
|
|
478
|
-
(a, b) => new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime()
|
|
479
|
-
);
|
|
480
|
-
}
|
|
481
|
-
function collectQwenFiles() {
|
|
482
|
-
const out = [];
|
|
483
|
-
let projectDirs;
|
|
484
|
-
try {
|
|
485
|
-
projectDirs = fs.readdirSync(QWEN_PROJECTS, { withFileTypes: true });
|
|
486
|
-
} catch {
|
|
487
|
-
return out;
|
|
488
|
-
}
|
|
489
|
-
for (const dir of projectDirs) {
|
|
490
|
-
if (!dir.isDirectory()) {
|
|
491
|
-
continue;
|
|
492
|
-
}
|
|
493
|
-
const chatsDir = path.join(QWEN_PROJECTS, dir.name, "chats");
|
|
494
|
-
try {
|
|
495
|
-
for (const f of fs.readdirSync(chatsDir)) {
|
|
496
|
-
if (f.endsWith(".jsonl")) {
|
|
497
|
-
out.push(path.join(chatsDir, f));
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
} catch {
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return out;
|
|
504
|
-
}
|
|
505
|
-
function qwenLineToMessage(entry) {
|
|
506
|
-
const type = entry.type;
|
|
507
|
-
if (type !== "user" && type !== "assistant") {
|
|
508
|
-
return null;
|
|
509
|
-
}
|
|
510
|
-
const message = entry.message ?? {};
|
|
511
|
-
const rawParts = Array.isArray(message.parts) ? message.parts : [];
|
|
512
|
-
const parts = [];
|
|
513
|
-
for (const raw of rawParts) {
|
|
514
|
-
if (typeof raw !== "object" || raw === null) {
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
const p = raw;
|
|
518
|
-
if (p.functionCall && typeof p.functionCall === "object") {
|
|
519
|
-
const fc = p.functionCall;
|
|
520
|
-
const toolName = typeof fc.name === "string" ? fc.name : "tool";
|
|
521
|
-
parts.push({
|
|
522
|
-
id: typeof fc.id === "string" ? fc.id : toolName,
|
|
523
|
-
input: fc.args ?? {},
|
|
524
|
-
toolName,
|
|
525
|
-
type: "tool_use"
|
|
526
|
-
});
|
|
527
|
-
} else if (p.thought === true && typeof p.text === "string") {
|
|
528
|
-
parts.push({ content: p.text, type: "thinking" });
|
|
529
|
-
} else if (typeof p.text === "string") {
|
|
530
|
-
parts.push({ content: p.text, type: "text" });
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
if (parts.length === 0 && typeof message.content === "string" && message.content) {
|
|
534
|
-
parts.push({ content: message.content, type: "text" });
|
|
535
|
-
}
|
|
536
|
-
if (parts.length === 0) {
|
|
537
|
-
return null;
|
|
538
|
-
}
|
|
539
|
-
return {
|
|
540
|
-
id: typeof entry.uuid === "string" ? entry.uuid : `qwen-${type}`,
|
|
541
|
-
parts,
|
|
542
|
-
role: type === "user" ? "user" : "assistant",
|
|
543
|
-
timestamp: typeof entry.timestamp === "string" ? entry.timestamp : ""
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
function readMeta(prefix) {
|
|
547
|
-
let cwd = "";
|
|
548
|
-
let id = "";
|
|
549
|
-
let gitBranch = "";
|
|
550
|
-
let started = "";
|
|
551
|
-
let title = "";
|
|
552
|
-
for (const line of prefix.split("\n")) {
|
|
553
|
-
const trimmed = line.trim();
|
|
554
|
-
if (!trimmed) {
|
|
555
|
-
continue;
|
|
556
|
-
}
|
|
557
|
-
let entry;
|
|
558
|
-
try {
|
|
559
|
-
entry = JSON.parse(trimmed);
|
|
560
|
-
} catch {
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
if (!cwd && typeof entry.cwd === "string") {
|
|
564
|
-
cwd = entry.cwd;
|
|
565
|
-
}
|
|
566
|
-
if (!id && typeof entry.sessionId === "string") {
|
|
567
|
-
id = entry.sessionId;
|
|
568
|
-
}
|
|
569
|
-
if (!gitBranch && typeof entry.gitBranch === "string") {
|
|
570
|
-
gitBranch = entry.gitBranch;
|
|
571
|
-
}
|
|
572
|
-
if (!started && typeof entry.timestamp === "string") {
|
|
573
|
-
started = entry.timestamp;
|
|
574
|
-
}
|
|
575
|
-
if (!title && entry.type === "user") {
|
|
576
|
-
const msg = entry.message;
|
|
577
|
-
let text = typeof msg?.content === "string" ? msg.content : "";
|
|
578
|
-
if (!text && Array.isArray(msg?.parts)) {
|
|
579
|
-
text = msg.parts.map(
|
|
580
|
-
(p) => p && typeof p === "object" && typeof p.text === "string" ? p.text : ""
|
|
581
|
-
).join(" ").trim();
|
|
582
|
-
}
|
|
583
|
-
if (text && !SYSTEM_TAG_PREFIXES.some((p) => text.startsWith(p))) {
|
|
584
|
-
title = text.split("\n")[0]?.slice(0, 80) ?? "";
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
return id && cwd ? { cwd, gitBranch, id, started, title } : null;
|
|
589
|
-
}
|
|
590
|
-
function readPrefix(filePath) {
|
|
591
|
-
const fd = fs.openSync(filePath, "r");
|
|
592
|
-
try {
|
|
593
|
-
const buf = Buffer.alloc(PREFIX_BYTES);
|
|
594
|
-
const n = fs.readSync(fd, buf, 0, PREFIX_BYTES, 0);
|
|
595
|
-
const text = buf.toString("utf-8", 0, n);
|
|
596
|
-
const lastNl = text.lastIndexOf("\n");
|
|
597
|
-
return lastNl === -1 ? text : text.slice(0, lastNl);
|
|
598
|
-
} finally {
|
|
599
|
-
fs.closeSync(fd);
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
function readQwenTextBounded(filePath) {
|
|
603
|
-
const size = fs.statSync(filePath).size;
|
|
604
|
-
if (size <= MAX_QWEN_FILE_BYTES) {
|
|
605
|
-
return fs.readFileSync(filePath, "utf-8");
|
|
606
|
-
}
|
|
607
|
-
const fd = fs.openSync(filePath, "r");
|
|
608
|
-
try {
|
|
609
|
-
const buf = Buffer.alloc(MAX_QWEN_FILE_BYTES);
|
|
610
|
-
const n = fs.readSync(fd, buf, 0, MAX_QWEN_FILE_BYTES, size - MAX_QWEN_FILE_BYTES);
|
|
611
|
-
const tail = buf.toString("utf-8", 0, n);
|
|
612
|
-
return tail.slice(tail.indexOf("\n") + 1);
|
|
613
|
-
} finally {
|
|
614
|
-
fs.closeSync(fd);
|
|
615
|
-
}
|
|
616
|
-
}
|
|
617
|
-
function shortHash(input) {
|
|
618
|
-
return crypto.createHash("sha256").update(input).digest("hex").slice(0, 8);
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
export { detectActiveQwenSessions as a, listQwenProjects as b, getQwenConversation as c, detectActiveGeminiSessions as d, getGeminiConversation as g, listGeminiProjects as l };
|
|
622
|
-
//# sourceMappingURL=qwen-reader-2fTFuC_D.js.map
|