@mariozechner/pi-coding-agent 0.10.2 → 0.11.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 +20 -1
- package/README.md +52 -2
- package/dist/export-html.d.ts +5 -0
- package/dist/export-html.d.ts.map +1 -1
- package/dist/export-html.js +662 -1
- package/dist/export-html.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +116 -19
- package/dist/main.js.map +1 -1
- package/dist/tools/find.d.ts +9 -0
- package/dist/tools/find.d.ts.map +1 -0
- package/dist/tools/find.js +143 -0
- package/dist/tools/find.js.map +1 -0
- package/dist/tools/grep.d.ts +13 -0
- package/dist/tools/grep.d.ts.map +1 -0
- package/dist/tools/grep.js +207 -0
- package/dist/tools/grep.js.map +1 -0
- package/dist/tools/index.d.ts +42 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/ls.d.ts +8 -0
- package/dist/tools/ls.d.ts.map +1 -0
- package/dist/tools/ls.js +100 -0
- package/dist/tools/ls.js.map +1 -0
- package/dist/tools-manager.d.ts +3 -0
- package/dist/tools-manager.d.ts.map +1 -0
- package/dist/tools-manager.js +186 -0
- package/dist/tools-manager.js.map +1 -0
- package/dist/tui/footer.d.ts +12 -0
- package/dist/tui/footer.d.ts.map +1 -1
- package/dist/tui/footer.js +42 -1
- package/dist/tui/footer.js.map +1 -1
- package/dist/tui/tool-execution.d.ts.map +1 -1
- package/dist/tui/tool-execution.js +77 -0
- package/dist/tui/tool-execution.js.map +1 -1
- package/dist/tui/tui-renderer.d.ts +1 -1
- package/dist/tui/tui-renderer.d.ts.map +1 -1
- package/dist/tui/tui-renderer.js +8 -2
- package/dist/tui/tui-renderer.js.map +1 -1
- package/package.json +4 -4
package/dist/export-html.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
2
2
|
import { homedir } from "os";
|
|
3
3
|
import { basename, dirname, join } from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
@@ -867,4 +867,665 @@ export function exportSessionToHtml(sessionManager, state, outputPath) {
|
|
|
867
867
|
writeFileSync(outputPath, html, "utf8");
|
|
868
868
|
return outputPath;
|
|
869
869
|
}
|
|
870
|
+
/**
|
|
871
|
+
* Parse session manager format (type: "session", "message", "model_change")
|
|
872
|
+
*/
|
|
873
|
+
function parseSessionManagerFormat(lines) {
|
|
874
|
+
const data = {
|
|
875
|
+
sessionId: "unknown",
|
|
876
|
+
timestamp: new Date().toISOString(),
|
|
877
|
+
modelsUsed: new Set(),
|
|
878
|
+
messages: [],
|
|
879
|
+
toolResultsMap: new Map(),
|
|
880
|
+
sessionEvents: [],
|
|
881
|
+
tokenStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
882
|
+
costStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
883
|
+
};
|
|
884
|
+
for (const line of lines) {
|
|
885
|
+
try {
|
|
886
|
+
const entry = JSON.parse(line);
|
|
887
|
+
if (entry.type === "session") {
|
|
888
|
+
data.sessionId = entry.id || "unknown";
|
|
889
|
+
data.timestamp = entry.timestamp || data.timestamp;
|
|
890
|
+
data.cwd = entry.cwd;
|
|
891
|
+
data.systemPrompt = entry.systemPrompt;
|
|
892
|
+
if (entry.modelId) {
|
|
893
|
+
const modelInfo = entry.provider ? `${entry.provider}/${entry.modelId}` : entry.modelId;
|
|
894
|
+
data.modelsUsed.add(modelInfo);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
else if (entry.type === "message") {
|
|
898
|
+
data.messages.push(entry.message);
|
|
899
|
+
data.sessionEvents.push(entry);
|
|
900
|
+
if (entry.message.role === "toolResult") {
|
|
901
|
+
data.toolResultsMap.set(entry.message.toolCallId, entry.message);
|
|
902
|
+
}
|
|
903
|
+
if (entry.message.role === "assistant" && entry.message.usage) {
|
|
904
|
+
const usage = entry.message.usage;
|
|
905
|
+
data.tokenStats.input += usage.input || 0;
|
|
906
|
+
data.tokenStats.output += usage.output || 0;
|
|
907
|
+
data.tokenStats.cacheRead += usage.cacheRead || 0;
|
|
908
|
+
data.tokenStats.cacheWrite += usage.cacheWrite || 0;
|
|
909
|
+
if (usage.cost) {
|
|
910
|
+
data.costStats.input += usage.cost.input || 0;
|
|
911
|
+
data.costStats.output += usage.cost.output || 0;
|
|
912
|
+
data.costStats.cacheRead += usage.cost.cacheRead || 0;
|
|
913
|
+
data.costStats.cacheWrite += usage.cost.cacheWrite || 0;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
else if (entry.type === "model_change") {
|
|
918
|
+
data.sessionEvents.push(entry);
|
|
919
|
+
if (entry.modelId) {
|
|
920
|
+
const modelInfo = entry.provider ? `${entry.provider}/${entry.modelId}` : entry.modelId;
|
|
921
|
+
data.modelsUsed.add(modelInfo);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
catch {
|
|
926
|
+
// Skip malformed lines
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return data;
|
|
930
|
+
}
|
|
931
|
+
/**
|
|
932
|
+
* Parse streaming event format (type: "agent_start", "message_start", "message_end", etc.)
|
|
933
|
+
*/
|
|
934
|
+
function parseStreamingEventFormat(lines) {
|
|
935
|
+
const data = {
|
|
936
|
+
sessionId: "unknown",
|
|
937
|
+
timestamp: new Date().toISOString(),
|
|
938
|
+
modelsUsed: new Set(),
|
|
939
|
+
messages: [],
|
|
940
|
+
toolResultsMap: new Map(),
|
|
941
|
+
sessionEvents: [],
|
|
942
|
+
tokenStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
943
|
+
costStats: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
944
|
+
isStreamingFormat: true,
|
|
945
|
+
};
|
|
946
|
+
let timestampSet = false;
|
|
947
|
+
// Track messages by collecting message_end events (which have the final state)
|
|
948
|
+
for (const line of lines) {
|
|
949
|
+
try {
|
|
950
|
+
const entry = JSON.parse(line);
|
|
951
|
+
if (entry.type === "message_end" && entry.message) {
|
|
952
|
+
const msg = entry.message;
|
|
953
|
+
data.messages.push(msg);
|
|
954
|
+
data.sessionEvents.push({ type: "message", message: msg, timestamp: msg.timestamp });
|
|
955
|
+
// Build tool results map
|
|
956
|
+
if (msg.role === "toolResult") {
|
|
957
|
+
data.toolResultsMap.set(msg.toolCallId, msg);
|
|
958
|
+
}
|
|
959
|
+
// Track models and accumulate stats from assistant messages
|
|
960
|
+
if (msg.role === "assistant") {
|
|
961
|
+
if (msg.model) {
|
|
962
|
+
const modelInfo = msg.provider ? `${msg.provider}/${msg.model}` : msg.model;
|
|
963
|
+
data.modelsUsed.add(modelInfo);
|
|
964
|
+
}
|
|
965
|
+
if (msg.usage) {
|
|
966
|
+
data.tokenStats.input += msg.usage.input || 0;
|
|
967
|
+
data.tokenStats.output += msg.usage.output || 0;
|
|
968
|
+
data.tokenStats.cacheRead += msg.usage.cacheRead || 0;
|
|
969
|
+
data.tokenStats.cacheWrite += msg.usage.cacheWrite || 0;
|
|
970
|
+
if (msg.usage.cost) {
|
|
971
|
+
data.costStats.input += msg.usage.cost.input || 0;
|
|
972
|
+
data.costStats.output += msg.usage.cost.output || 0;
|
|
973
|
+
data.costStats.cacheRead += msg.usage.cost.cacheRead || 0;
|
|
974
|
+
data.costStats.cacheWrite += msg.usage.cost.cacheWrite || 0;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
// Use first message timestamp as session timestamp
|
|
979
|
+
if (!timestampSet && msg.timestamp) {
|
|
980
|
+
data.timestamp = new Date(msg.timestamp).toISOString();
|
|
981
|
+
timestampSet = true;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
catch {
|
|
986
|
+
// Skip malformed lines
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
// Generate a session ID from the timestamp
|
|
990
|
+
data.sessionId = `stream-${data.timestamp.replace(/[:.]/g, "-")}`;
|
|
991
|
+
return data;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Detect the format of a session file by examining the first valid JSON line
|
|
995
|
+
*/
|
|
996
|
+
function detectFormat(lines) {
|
|
997
|
+
for (const line of lines) {
|
|
998
|
+
try {
|
|
999
|
+
const entry = JSON.parse(line);
|
|
1000
|
+
if (entry.type === "session")
|
|
1001
|
+
return "session-manager";
|
|
1002
|
+
if (entry.type === "agent_start" || entry.type === "message_start" || entry.type === "turn_start") {
|
|
1003
|
+
return "streaming-events";
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
catch {
|
|
1007
|
+
// Skip malformed lines
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
return "unknown";
|
|
1011
|
+
}
|
|
1012
|
+
/**
|
|
1013
|
+
* Generate HTML from parsed session data
|
|
1014
|
+
*/
|
|
1015
|
+
function generateHtml(data, inputFilename) {
|
|
1016
|
+
// Calculate message stats
|
|
1017
|
+
const userMessages = data.messages.filter((m) => m.role === "user").length;
|
|
1018
|
+
const assistantMessages = data.messages.filter((m) => m.role === "assistant").length;
|
|
1019
|
+
// Count tool calls from assistant messages
|
|
1020
|
+
let toolCallsCount = 0;
|
|
1021
|
+
for (const message of data.messages) {
|
|
1022
|
+
if (message.role === "assistant") {
|
|
1023
|
+
const assistantMsg = message;
|
|
1024
|
+
toolCallsCount += assistantMsg.content.filter((c) => c.type === "toolCall").length;
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
// Get last assistant message for context info
|
|
1028
|
+
const lastAssistantMessage = data.messages
|
|
1029
|
+
.slice()
|
|
1030
|
+
.reverse()
|
|
1031
|
+
.find((m) => m.role === "assistant" && m.stopReason !== "aborted");
|
|
1032
|
+
const contextTokens = lastAssistantMessage
|
|
1033
|
+
? lastAssistantMessage.usage.input +
|
|
1034
|
+
lastAssistantMessage.usage.output +
|
|
1035
|
+
lastAssistantMessage.usage.cacheRead +
|
|
1036
|
+
lastAssistantMessage.usage.cacheWrite
|
|
1037
|
+
: 0;
|
|
1038
|
+
const lastModel = lastAssistantMessage?.model || "unknown";
|
|
1039
|
+
const lastProvider = lastAssistantMessage?.provider || "";
|
|
1040
|
+
const lastModelInfo = lastProvider ? `${lastProvider}/${lastModel}` : lastModel;
|
|
1041
|
+
// Generate messages HTML
|
|
1042
|
+
let messagesHtml = "";
|
|
1043
|
+
for (const event of data.sessionEvents) {
|
|
1044
|
+
if (event.type === "message" && event.message.role !== "toolResult") {
|
|
1045
|
+
messagesHtml += formatMessage(event.message, data.toolResultsMap);
|
|
1046
|
+
}
|
|
1047
|
+
else if (event.type === "model_change") {
|
|
1048
|
+
messagesHtml += formatModelChange(event);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
// Tools section (only if tools info available)
|
|
1052
|
+
const toolsHtml = data.tools
|
|
1053
|
+
? `
|
|
1054
|
+
<div class="tools-list">
|
|
1055
|
+
<div class="tools-header">Available Tools</div>
|
|
1056
|
+
<div class="tools-content">
|
|
1057
|
+
${data.tools.map((tool) => `<div class="tool-item"><span class="tool-item-name">${escapeHtml(tool.name)}</span> - ${escapeHtml(tool.description)}</div>`).join("")}
|
|
1058
|
+
</div>
|
|
1059
|
+
</div>`
|
|
1060
|
+
: "";
|
|
1061
|
+
// System prompt section (only if available)
|
|
1062
|
+
const systemPromptHtml = data.systemPrompt
|
|
1063
|
+
? `
|
|
1064
|
+
<div class="system-prompt">
|
|
1065
|
+
<div class="system-prompt-header">System Prompt</div>
|
|
1066
|
+
<div class="system-prompt-content">${escapeHtml(data.systemPrompt)}</div>
|
|
1067
|
+
</div>`
|
|
1068
|
+
: "";
|
|
1069
|
+
return `<!DOCTYPE html>
|
|
1070
|
+
<html lang="en">
|
|
1071
|
+
<head>
|
|
1072
|
+
<meta charset="UTF-8">
|
|
1073
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1074
|
+
<title>Session Export - ${escapeHtml(inputFilename)}</title>
|
|
1075
|
+
<style>
|
|
1076
|
+
* {
|
|
1077
|
+
margin: 0;
|
|
1078
|
+
padding: 0;
|
|
1079
|
+
box-sizing: border-box;
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
body {
|
|
1083
|
+
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
|
|
1084
|
+
font-size: 12px;
|
|
1085
|
+
line-height: 1.6;
|
|
1086
|
+
color: ${COLORS.text};
|
|
1087
|
+
background: ${COLORS.bodyBg};
|
|
1088
|
+
padding: 24px;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
.container {
|
|
1092
|
+
max-width: 700px;
|
|
1093
|
+
margin: 0 auto;
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
.header {
|
|
1097
|
+
margin-bottom: 24px;
|
|
1098
|
+
padding: 16px;
|
|
1099
|
+
background: ${COLORS.containerBg};
|
|
1100
|
+
border-radius: 4px;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
.header h1 {
|
|
1104
|
+
font-size: 14px;
|
|
1105
|
+
font-weight: bold;
|
|
1106
|
+
margin-bottom: 12px;
|
|
1107
|
+
color: ${COLORS.cyan};
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
.header-info {
|
|
1111
|
+
display: flex;
|
|
1112
|
+
flex-direction: column;
|
|
1113
|
+
gap: 3px;
|
|
1114
|
+
font-size: 11px;
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
.info-item {
|
|
1118
|
+
color: ${COLORS.textDim};
|
|
1119
|
+
display: flex;
|
|
1120
|
+
align-items: baseline;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
.info-label {
|
|
1124
|
+
font-weight: 600;
|
|
1125
|
+
margin-right: 8px;
|
|
1126
|
+
min-width: 100px;
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
.info-value {
|
|
1130
|
+
color: ${COLORS.text};
|
|
1131
|
+
flex: 1;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
.info-value.cost {
|
|
1135
|
+
font-family: 'SF Mono', monospace;
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
.messages {
|
|
1139
|
+
display: flex;
|
|
1140
|
+
flex-direction: column;
|
|
1141
|
+
gap: 16px;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
.message-timestamp {
|
|
1145
|
+
font-size: 10px;
|
|
1146
|
+
color: ${COLORS.textDim};
|
|
1147
|
+
margin-bottom: 4px;
|
|
1148
|
+
opacity: 0.8;
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
.user-message {
|
|
1152
|
+
background: ${COLORS.userMessageBg};
|
|
1153
|
+
padding: 12px 16px;
|
|
1154
|
+
border-radius: 4px;
|
|
1155
|
+
white-space: pre-wrap;
|
|
1156
|
+
word-wrap: break-word;
|
|
1157
|
+
overflow-wrap: break-word;
|
|
1158
|
+
word-break: break-word;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
.assistant-message {
|
|
1162
|
+
padding: 0;
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
.assistant-text {
|
|
1166
|
+
padding: 12px 16px;
|
|
1167
|
+
white-space: pre-wrap;
|
|
1168
|
+
word-wrap: break-word;
|
|
1169
|
+
overflow-wrap: break-word;
|
|
1170
|
+
word-break: break-word;
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
.thinking-text {
|
|
1174
|
+
padding: 12px 16px;
|
|
1175
|
+
color: ${COLORS.italic};
|
|
1176
|
+
font-style: italic;
|
|
1177
|
+
white-space: pre-wrap;
|
|
1178
|
+
word-wrap: break-word;
|
|
1179
|
+
overflow-wrap: break-word;
|
|
1180
|
+
word-break: break-word;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
.model-change {
|
|
1184
|
+
padding: 8px 16px;
|
|
1185
|
+
background: rgb(40, 40, 50);
|
|
1186
|
+
border-radius: 4px;
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
.model-change-text {
|
|
1190
|
+
color: ${COLORS.textDim};
|
|
1191
|
+
font-size: 11px;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
.model-name {
|
|
1195
|
+
color: ${COLORS.cyan};
|
|
1196
|
+
font-weight: bold;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
.tool-execution {
|
|
1200
|
+
padding: 12px 16px;
|
|
1201
|
+
border-radius: 4px;
|
|
1202
|
+
margin-top: 8px;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
.tool-header {
|
|
1206
|
+
font-weight: bold;
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
.tool-name {
|
|
1210
|
+
font-weight: bold;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
.tool-path {
|
|
1214
|
+
color: ${COLORS.cyan};
|
|
1215
|
+
word-break: break-all;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
.line-count {
|
|
1219
|
+
color: ${COLORS.textDim};
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.tool-command {
|
|
1223
|
+
font-weight: bold;
|
|
1224
|
+
white-space: pre-wrap;
|
|
1225
|
+
word-wrap: break-word;
|
|
1226
|
+
overflow-wrap: break-word;
|
|
1227
|
+
word-break: break-word;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
.tool-output {
|
|
1231
|
+
margin-top: 12px;
|
|
1232
|
+
color: ${COLORS.textDim};
|
|
1233
|
+
white-space: pre-wrap;
|
|
1234
|
+
word-wrap: break-word;
|
|
1235
|
+
overflow-wrap: break-word;
|
|
1236
|
+
word-break: break-word;
|
|
1237
|
+
font-family: inherit;
|
|
1238
|
+
overflow-x: auto;
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
.tool-output > div {
|
|
1242
|
+
line-height: 1.4;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
.tool-output pre {
|
|
1246
|
+
margin: 0;
|
|
1247
|
+
font-family: inherit;
|
|
1248
|
+
color: inherit;
|
|
1249
|
+
white-space: pre-wrap;
|
|
1250
|
+
word-wrap: break-word;
|
|
1251
|
+
overflow-wrap: break-word;
|
|
1252
|
+
}
|
|
1253
|
+
|
|
1254
|
+
.tool-output.expandable {
|
|
1255
|
+
cursor: pointer;
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
.tool-output.expandable:hover {
|
|
1259
|
+
opacity: 0.9;
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
.tool-output.expandable .output-full {
|
|
1263
|
+
display: none;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
.tool-output.expandable.expanded .output-preview {
|
|
1267
|
+
display: none;
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
.tool-output.expandable.expanded .output-full {
|
|
1271
|
+
display: block;
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
.expand-hint {
|
|
1275
|
+
color: ${COLORS.cyan};
|
|
1276
|
+
font-style: italic;
|
|
1277
|
+
margin-top: 4px;
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
.system-prompt {
|
|
1281
|
+
background: rgb(60, 55, 40);
|
|
1282
|
+
padding: 12px 16px;
|
|
1283
|
+
border-radius: 4px;
|
|
1284
|
+
margin-bottom: 16px;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
.system-prompt-header {
|
|
1288
|
+
font-weight: bold;
|
|
1289
|
+
color: ${COLORS.yellow};
|
|
1290
|
+
margin-bottom: 8px;
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
.system-prompt-content {
|
|
1294
|
+
color: ${COLORS.textDim};
|
|
1295
|
+
white-space: pre-wrap;
|
|
1296
|
+
word-wrap: break-word;
|
|
1297
|
+
overflow-wrap: break-word;
|
|
1298
|
+
word-break: break-word;
|
|
1299
|
+
font-size: 11px;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
.tools-list {
|
|
1303
|
+
background: rgb(60, 55, 40);
|
|
1304
|
+
padding: 12px 16px;
|
|
1305
|
+
border-radius: 4px;
|
|
1306
|
+
margin-bottom: 16px;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
.tools-header {
|
|
1310
|
+
font-weight: bold;
|
|
1311
|
+
color: ${COLORS.yellow};
|
|
1312
|
+
margin-bottom: 8px;
|
|
1313
|
+
}
|
|
1314
|
+
|
|
1315
|
+
.tools-content {
|
|
1316
|
+
color: ${COLORS.textDim};
|
|
1317
|
+
font-size: 11px;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
.tool-item {
|
|
1321
|
+
margin: 4px 0;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.tool-item-name {
|
|
1325
|
+
font-weight: bold;
|
|
1326
|
+
color: ${COLORS.text};
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
.tool-diff {
|
|
1330
|
+
margin-top: 12px;
|
|
1331
|
+
font-size: 11px;
|
|
1332
|
+
font-family: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, 'DejaVu Sans Mono', monospace;
|
|
1333
|
+
overflow-x: auto;
|
|
1334
|
+
max-width: 100%;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
.diff-line-old {
|
|
1338
|
+
color: ${COLORS.red};
|
|
1339
|
+
white-space: pre-wrap;
|
|
1340
|
+
word-wrap: break-word;
|
|
1341
|
+
overflow-wrap: break-word;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
.diff-line-new {
|
|
1345
|
+
color: ${COLORS.green};
|
|
1346
|
+
white-space: pre-wrap;
|
|
1347
|
+
word-wrap: break-word;
|
|
1348
|
+
overflow-wrap: break-word;
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
.diff-line-context {
|
|
1352
|
+
color: ${COLORS.textDim};
|
|
1353
|
+
white-space: pre-wrap;
|
|
1354
|
+
word-wrap: break-word;
|
|
1355
|
+
overflow-wrap: break-word;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
.error-text {
|
|
1359
|
+
color: ${COLORS.red};
|
|
1360
|
+
padding: 12px 16px;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
.footer {
|
|
1364
|
+
margin-top: 48px;
|
|
1365
|
+
padding: 20px;
|
|
1366
|
+
text-align: center;
|
|
1367
|
+
color: ${COLORS.textDim};
|
|
1368
|
+
font-size: 10px;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
.streaming-notice {
|
|
1372
|
+
background: rgb(50, 45, 35);
|
|
1373
|
+
padding: 12px 16px;
|
|
1374
|
+
border-radius: 4px;
|
|
1375
|
+
margin-bottom: 16px;
|
|
1376
|
+
color: ${COLORS.textDim};
|
|
1377
|
+
font-size: 11px;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
@media print {
|
|
1381
|
+
body {
|
|
1382
|
+
background: white;
|
|
1383
|
+
color: black;
|
|
1384
|
+
}
|
|
1385
|
+
.tool-execution {
|
|
1386
|
+
border: 1px solid #ddd;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
</style>
|
|
1390
|
+
</head>
|
|
1391
|
+
<body>
|
|
1392
|
+
<div class="container">
|
|
1393
|
+
<div class="header">
|
|
1394
|
+
<h1>pi v${VERSION}</h1>
|
|
1395
|
+
<div class="header-info">
|
|
1396
|
+
<div class="info-item">
|
|
1397
|
+
<span class="info-label">Session:</span>
|
|
1398
|
+
<span class="info-value">${escapeHtml(data.sessionId)}</span>
|
|
1399
|
+
</div>
|
|
1400
|
+
<div class="info-item">
|
|
1401
|
+
<span class="info-label">Date:</span>
|
|
1402
|
+
<span class="info-value">${new Date(data.timestamp).toLocaleString()}</span>
|
|
1403
|
+
</div>
|
|
1404
|
+
<div class="info-item">
|
|
1405
|
+
<span class="info-label">Models:</span>
|
|
1406
|
+
<span class="info-value">${Array.from(data.modelsUsed)
|
|
1407
|
+
.map((m) => escapeHtml(m))
|
|
1408
|
+
.join(", ") || "unknown"}</span>
|
|
1409
|
+
</div>
|
|
1410
|
+
</div>
|
|
1411
|
+
</div>
|
|
1412
|
+
|
|
1413
|
+
<div class="header">
|
|
1414
|
+
<h1>Messages</h1>
|
|
1415
|
+
<div class="header-info">
|
|
1416
|
+
<div class="info-item">
|
|
1417
|
+
<span class="info-label">User:</span>
|
|
1418
|
+
<span class="info-value">${userMessages}</span>
|
|
1419
|
+
</div>
|
|
1420
|
+
<div class="info-item">
|
|
1421
|
+
<span class="info-label">Assistant:</span>
|
|
1422
|
+
<span class="info-value">${assistantMessages}</span>
|
|
1423
|
+
</div>
|
|
1424
|
+
<div class="info-item">
|
|
1425
|
+
<span class="info-label">Tool Calls:</span>
|
|
1426
|
+
<span class="info-value">${toolCallsCount}</span>
|
|
1427
|
+
</div>
|
|
1428
|
+
</div>
|
|
1429
|
+
</div>
|
|
1430
|
+
|
|
1431
|
+
<div class="header">
|
|
1432
|
+
<h1>Tokens & Cost</h1>
|
|
1433
|
+
<div class="header-info">
|
|
1434
|
+
<div class="info-item">
|
|
1435
|
+
<span class="info-label">Input:</span>
|
|
1436
|
+
<span class="info-value">${data.tokenStats.input.toLocaleString()} tokens</span>
|
|
1437
|
+
</div>
|
|
1438
|
+
<div class="info-item">
|
|
1439
|
+
<span class="info-label">Output:</span>
|
|
1440
|
+
<span class="info-value">${data.tokenStats.output.toLocaleString()} tokens</span>
|
|
1441
|
+
</div>
|
|
1442
|
+
<div class="info-item">
|
|
1443
|
+
<span class="info-label">Cache Read:</span>
|
|
1444
|
+
<span class="info-value">${data.tokenStats.cacheRead.toLocaleString()} tokens</span>
|
|
1445
|
+
</div>
|
|
1446
|
+
<div class="info-item">
|
|
1447
|
+
<span class="info-label">Cache Write:</span>
|
|
1448
|
+
<span class="info-value">${data.tokenStats.cacheWrite.toLocaleString()} tokens</span>
|
|
1449
|
+
</div>
|
|
1450
|
+
<div class="info-item">
|
|
1451
|
+
<span class="info-label">Total:</span>
|
|
1452
|
+
<span class="info-value">${(data.tokenStats.input + data.tokenStats.output + data.tokenStats.cacheRead + data.tokenStats.cacheWrite).toLocaleString()} tokens</span>
|
|
1453
|
+
</div>
|
|
1454
|
+
<div class="info-item">
|
|
1455
|
+
<span class="info-label">Input Cost:</span>
|
|
1456
|
+
<span class="info-value cost">$${data.costStats.input.toFixed(4)}</span>
|
|
1457
|
+
</div>
|
|
1458
|
+
<div class="info-item">
|
|
1459
|
+
<span class="info-label">Output Cost:</span>
|
|
1460
|
+
<span class="info-value cost">$${data.costStats.output.toFixed(4)}</span>
|
|
1461
|
+
</div>
|
|
1462
|
+
<div class="info-item">
|
|
1463
|
+
<span class="info-label">Cache Read Cost:</span>
|
|
1464
|
+
<span class="info-value cost">$${data.costStats.cacheRead.toFixed(4)}</span>
|
|
1465
|
+
</div>
|
|
1466
|
+
<div class="info-item">
|
|
1467
|
+
<span class="info-label">Cache Write Cost:</span>
|
|
1468
|
+
<span class="info-value cost">$${data.costStats.cacheWrite.toFixed(4)}</span>
|
|
1469
|
+
</div>
|
|
1470
|
+
<div class="info-item">
|
|
1471
|
+
<span class="info-label">Total Cost:</span>
|
|
1472
|
+
<span class="info-value cost"><strong>$${(data.costStats.input + data.costStats.output + data.costStats.cacheRead + data.costStats.cacheWrite).toFixed(4)}</strong></span>
|
|
1473
|
+
</div>
|
|
1474
|
+
<div class="info-item">
|
|
1475
|
+
<span class="info-label">Context Usage:</span>
|
|
1476
|
+
<span class="info-value">${contextTokens.toLocaleString()} tokens (last turn) - ${escapeHtml(lastModelInfo)}</span>
|
|
1477
|
+
</div>
|
|
1478
|
+
</div>
|
|
1479
|
+
</div>
|
|
1480
|
+
|
|
1481
|
+
${systemPromptHtml}
|
|
1482
|
+
${toolsHtml}
|
|
1483
|
+
|
|
1484
|
+
${data.isStreamingFormat
|
|
1485
|
+
? `<div class="streaming-notice">
|
|
1486
|
+
<em>Note: This session was reconstructed from raw agent event logs, which do not contain system prompt or tool definitions.</em>
|
|
1487
|
+
</div>`
|
|
1488
|
+
: ""}
|
|
1489
|
+
|
|
1490
|
+
<div class="messages">
|
|
1491
|
+
${messagesHtml}
|
|
1492
|
+
</div>
|
|
1493
|
+
|
|
1494
|
+
<div class="footer">
|
|
1495
|
+
Generated by pi coding-agent on ${new Date().toLocaleString()}
|
|
1496
|
+
</div>
|
|
1497
|
+
</div>
|
|
1498
|
+
</body>
|
|
1499
|
+
</html>`;
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Export a session file to HTML (standalone, without AgentState or SessionManager)
|
|
1503
|
+
* Auto-detects format: session manager format or streaming event format
|
|
1504
|
+
*/
|
|
1505
|
+
export function exportFromFile(inputPath, outputPath) {
|
|
1506
|
+
if (!existsSync(inputPath)) {
|
|
1507
|
+
throw new Error(`File not found: ${inputPath}`);
|
|
1508
|
+
}
|
|
1509
|
+
const content = readFileSync(inputPath, "utf8");
|
|
1510
|
+
const lines = content
|
|
1511
|
+
.trim()
|
|
1512
|
+
.split("\n")
|
|
1513
|
+
.filter((l) => l.trim());
|
|
1514
|
+
if (lines.length === 0) {
|
|
1515
|
+
throw new Error(`Empty file: ${inputPath}`);
|
|
1516
|
+
}
|
|
1517
|
+
const format = detectFormat(lines);
|
|
1518
|
+
if (format === "unknown") {
|
|
1519
|
+
throw new Error(`Unknown session file format: ${inputPath}`);
|
|
1520
|
+
}
|
|
1521
|
+
const data = format === "session-manager" ? parseSessionManagerFormat(lines) : parseStreamingEventFormat(lines);
|
|
1522
|
+
// Generate output path if not provided
|
|
1523
|
+
if (!outputPath) {
|
|
1524
|
+
const inputBasename = basename(inputPath, ".jsonl");
|
|
1525
|
+
outputPath = `pi-session-${inputBasename}.html`;
|
|
1526
|
+
}
|
|
1527
|
+
const html = generateHtml(data, basename(inputPath));
|
|
1528
|
+
writeFileSync(outputPath, html, "utf8");
|
|
1529
|
+
return outputPath;
|
|
1530
|
+
}
|
|
870
1531
|
//# sourceMappingURL=export-html.js.map
|