claude-simple 0.2.10 → 0.2.11
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/package.json +1 -1
- package/public/index.html +2 -2
- package/public/shell/templates.js +8 -4
- package/public/style.css +13 -0
- package/src/agents/sessions.js +53 -8
package/package.json
CHANGED
package/public/index.html
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
<script src="/vendor/marked.min.js"></script>
|
|
83
83
|
<link id="hljs-theme" rel="stylesheet" href="/vendor/highlight-dark.min.css">
|
|
84
84
|
<script src="/vendor/highlight.min.js"></script>
|
|
85
|
-
<link rel="stylesheet" href="style.css?v=ui-
|
|
85
|
+
<link rel="stylesheet" href="style.css?v=ui-12">
|
|
86
86
|
</head>
|
|
87
87
|
<body class="h-dvh overflow-hidden flex flex-col bg-base-100 text-base-content">
|
|
88
88
|
<div id="root" class="flex flex-col flex-1 overflow-hidden"></div>
|
|
@@ -98,6 +98,6 @@
|
|
|
98
98
|
}
|
|
99
99
|
}, 10000);
|
|
100
100
|
</script>
|
|
101
|
-
<script type="module" src="app.js?v=ui-
|
|
101
|
+
<script type="module" src="app.js?v=ui-12"></script>
|
|
102
102
|
</body>
|
|
103
103
|
</html>
|
|
@@ -82,10 +82,14 @@ export const AppShell = (opts = {}) => {
|
|
|
82
82
|
<button id="btn-share-session" class="btn btn-ghost btn-xs border border-base-300" title="复制当前会话分享链接">分享</button>
|
|
83
83
|
<button id="btn-refresh-conn" class="btn btn-ghost btn-xs border border-base-300" title="重新连接并同步">↻</button>
|
|
84
84
|
<button id="btn-pwa-install" class="btn btn-ghost btn-xs border border-base-300 hidden" title="安装到主屏幕">安装</button>
|
|
85
|
-
<button id="btn-meta-agent"
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<button id="btn-
|
|
85
|
+
<button id="btn-meta-agent" class="btn btn-ghost btn-xs border border-primary/40 text-primary topbar-icon-btn" title="Meta Agent — 报告 / 问答 / VLM" aria-label="打开 Meta Agent">
|
|
86
|
+
<svg class="topbar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" aria-hidden="true"><path d="m12 2 1.6 6.4L20 10l-6.4 1.6L12 18l-1.6-6.4L4 10l6.4-1.6L12 2Z"/><path d="m19 16 .7 2.3L22 19l-2.3.7L19 22l-.7-2.3L16 19l2.3-.7L19 16Z"/></svg><span class="topbar-icon-label">Agent</span>
|
|
87
|
+
</button>
|
|
88
|
+
<button id="btn-new-session" class="btn btn-ghost btn-xs border border-base-300 topbar-icon-btn" title="New session" aria-label="新建会话">
|
|
89
|
+
<svg class="topbar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><circle cx="12" cy="12" r="8"/><path d="M12 8v8M8 12h8"/></svg><span class="topbar-icon-label">New</span>
|
|
90
|
+
</button>
|
|
91
|
+
<button id="btn-theme" class="btn btn-ghost btn-xs border border-base-300 topbar-icon-btn" title="切换主题" aria-label="切换主题"><svg class="topbar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><path d="M20 15.5A8 8 0 0 1 8.5 4 8 8 0 1 0 20 15.5Z"/></svg></button>
|
|
92
|
+
<button id="btn-settings" class="btn btn-ghost btn-xs border border-base-300 topbar-icon-btn" title="设置" aria-label="设置"><svg class="topbar-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.7 1.7 0 0 0 .34 1.88l.06.06-2.12 2.12-.06-.06A1.7 1.7 0 0 0 15.74 18a1.7 1.7 0 0 0-1.02 1.56V20h-3v-.44A1.7 1.7 0 0 0 10.7 18a1.7 1.7 0 0 0-1.88.34l-.06.06-2.12-2.12.06-.06A1.7 1.7 0 0 0 7.04 14.3 1.7 1.7 0 0 0 5.5 13.28H5v-3h.5a1.7 1.7 0 0 0 1.54-1.02A1.7 1.7 0 0 0 6.7 7.38l-.06-.06L8.76 5.2l.06.06A1.7 1.7 0 0 0 10.7 5a1.7 1.7 0 0 0 1.02-1.56V3h3v.44A1.7 1.7 0 0 0 15.74 5a1.7 1.7 0 0 0 1.88-.34l.06-.06 2.12 2.12-.06.06a1.7 1.7 0 0 0-.34 1.88 1.7 1.7 0 0 0 1.56 1.02h.5v3h-.5A1.7 1.7 0 0 0 19.4 15Z"/></svg></button>
|
|
89
93
|
<button id="btn-logout" class="btn btn-ghost btn-xs border border-base-300">Sign out</button>
|
|
90
94
|
</div>
|
|
91
95
|
</div>
|
package/public/style.css
CHANGED
|
@@ -1141,6 +1141,18 @@ html.pwa-standalone .ma-panel {
|
|
|
1141
1141
|
#recent-menu.recent-menu-open {
|
|
1142
1142
|
display: block; /* beat .hidden if both race */
|
|
1143
1143
|
}
|
|
1144
|
+
.topbar-icon-btn {
|
|
1145
|
+
gap: .35rem;
|
|
1146
|
+
min-width: 2rem;
|
|
1147
|
+
padding-inline: .45rem;
|
|
1148
|
+
}
|
|
1149
|
+
.topbar-icon {
|
|
1150
|
+
width: 1rem;
|
|
1151
|
+
height: 1rem;
|
|
1152
|
+
display: block;
|
|
1153
|
+
flex: 0 0 auto;
|
|
1154
|
+
}
|
|
1155
|
+
.topbar-icon-label { display: inline; }
|
|
1144
1156
|
@media (max-width: 640px) {
|
|
1145
1157
|
#recent-menu {
|
|
1146
1158
|
min-width: 0;
|
|
@@ -1161,6 +1173,7 @@ html.pwa-standalone .ma-panel {
|
|
|
1161
1173
|
#btn-pwa-install {
|
|
1162
1174
|
display: none !important;
|
|
1163
1175
|
}
|
|
1176
|
+
.topbar-icon-label { display: none; }
|
|
1164
1177
|
#topbar-project {
|
|
1165
1178
|
font-size: 11px;
|
|
1166
1179
|
max-width: 30vw;
|
package/src/agents/sessions.js
CHANGED
|
@@ -910,6 +910,14 @@ function geminiJournalPath(sessionId, geminiHome) {
|
|
|
910
910
|
return path.join(geminiUiSessionsRoot(geminiHome), `${sessionId}.jsonl`);
|
|
911
911
|
}
|
|
912
912
|
|
|
913
|
+
// agy keeps the actual native transcript here. This is plain JSONL (unlike
|
|
914
|
+
// its conversation SQLite database) and is available for conversations made
|
|
915
|
+
// in the Antigravity app as well as print-mode conversations created by us.
|
|
916
|
+
function geminiNativeTranscriptPath(sessionId, geminiHome) {
|
|
917
|
+
const home = geminiHome || path.join(os.homedir(), '.gemini');
|
|
918
|
+
return path.join(home, 'antigravity-cli', 'brain', sessionId, '.system_generated', 'logs', 'transcript.jsonl');
|
|
919
|
+
}
|
|
920
|
+
|
|
913
921
|
function geminiPathFromUri(uri) {
|
|
914
922
|
if (!uri || typeof uri !== 'string') return null;
|
|
915
923
|
try { return decodeURIComponent(uri.replace(/^file:\/\//, '')); } catch { return null; }
|
|
@@ -1030,10 +1038,46 @@ async function findGeminiJournal(sessionId, geminiHome) {
|
|
|
1030
1038
|
try { await fs.access(file); return file; } catch { return null; }
|
|
1031
1039
|
}
|
|
1032
1040
|
|
|
1041
|
+
async function findGeminiNativeTranscript(sessionId, geminiHome) {
|
|
1042
|
+
const file = geminiNativeTranscriptPath(sessionId, geminiHome);
|
|
1043
|
+
try { await fs.access(file); return file; } catch { return null; }
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
function geminiUserText(content) {
|
|
1047
|
+
const text = String(content || '').trim();
|
|
1048
|
+
// Native rows wrap the visible user request in transport metadata. Keep
|
|
1049
|
+
// the chat transcript readable while retaining a safe fallback for old rows.
|
|
1050
|
+
const request = text.match(/<USER_REQUEST>\s*([\s\S]*?)\s*<\/USER_REQUEST>/i);
|
|
1051
|
+
return (request?.[1] || text).trim();
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
/** Parse agy's native brain transcript into the common UI history shape. */
|
|
1055
|
+
export async function parseGeminiNativeMessages(filePath) {
|
|
1056
|
+
const messages = [];
|
|
1057
|
+
try {
|
|
1058
|
+
const stream = fsSync.createReadStream(filePath);
|
|
1059
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
1060
|
+
for await (const line of rl) {
|
|
1061
|
+
let d; try { d = JSON.parse(line); } catch { continue; }
|
|
1062
|
+
if (d.status && d.status !== 'DONE') continue;
|
|
1063
|
+
const ts = Date.parse(d.created_at || '') || undefined;
|
|
1064
|
+
if (d.source === 'USER_EXPLICIT' && d.type === 'USER_INPUT') {
|
|
1065
|
+
const content = geminiUserText(d.content);
|
|
1066
|
+
if (content) messages.push({ role: 'user', type: 'text', content, ts });
|
|
1067
|
+
} else if (d.source === 'MODEL' && ['GENERIC', 'PLANNER_RESPONSE'].includes(d.type)) {
|
|
1068
|
+
const content = String(d.content || '').trim();
|
|
1069
|
+
if (content) messages.push({ role: 'assistant', type: 'text', content, ts });
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
} catch {}
|
|
1073
|
+
return messages;
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1033
1076
|
async function loadGeminiContext(sessionId, geminiHome) {
|
|
1034
|
-
const
|
|
1035
|
-
|
|
1036
|
-
|
|
1077
|
+
const native = await findGeminiNativeTranscript(sessionId, geminiHome);
|
|
1078
|
+
const file = native || await findGeminiJournal(sessionId, geminiHome);
|
|
1079
|
+
if (!file) return { note: 'This Gemini conversation has no readable transcript on this machine.' };
|
|
1080
|
+
const messages = native ? await parseGeminiNativeMessages(native) : await parseGeminiMessages(file);
|
|
1037
1081
|
const usages = messages.filter(m => m.type === 'token_usage').map(m => m.usage || {});
|
|
1038
1082
|
const total = { input_tokens: 0, output_tokens: 0, reasoning_tokens: 0, cache_read_input_tokens: 0, total_tokens: 0 };
|
|
1039
1083
|
for (const u of usages) for (const key of Object.keys(total)) total[key] += Number(u[key]) || 0;
|
|
@@ -1384,16 +1428,17 @@ export async function loadSessionMessages(sessionId, agent, { claudeConfigDirs,
|
|
|
1384
1428
|
return { messages: await parseGeminiMessages(file), context: await loadCursorContext(sessionId, cursorHome), agent: 'cursor' };
|
|
1385
1429
|
}
|
|
1386
1430
|
if (a === 'gemini') {
|
|
1387
|
-
const
|
|
1431
|
+
const native = await findGeminiNativeTranscript(sessionId, geminiHome);
|
|
1432
|
+
const file = native || await findGeminiJournal(sessionId, geminiHome);
|
|
1388
1433
|
if (!file) {
|
|
1389
1434
|
const native = (await scanGeminiSessions(geminiHome)).some(s => s.sessionId === sessionId);
|
|
1390
1435
|
if (!native) return null;
|
|
1391
|
-
// agy's native transcript is a private protobuf SQLite format. The
|
|
1392
|
-
// conversation can still be resumed normally; UI-created turns gain a
|
|
1393
|
-
// readable journal from that point onward.
|
|
1394
1436
|
return { messages: [], context: await loadGeminiContext(sessionId, geminiHome), agent: 'gemini' };
|
|
1395
1437
|
}
|
|
1396
|
-
return {
|
|
1438
|
+
return {
|
|
1439
|
+
messages: native ? await parseGeminiNativeMessages(native) : await parseGeminiMessages(file),
|
|
1440
|
+
context: await loadGeminiContext(sessionId, geminiHome), agent: 'gemini',
|
|
1441
|
+
};
|
|
1397
1442
|
}
|
|
1398
1443
|
if (a === 'codex') {
|
|
1399
1444
|
const file = await findCodexSessionFile(sessionId, codexHome);
|