claude-code-session-manager 0.8.3 → 0.8.5
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/LICENSE +21 -0
- package/dist/assets/{cssMode-DyaNC2Cs.js → cssMode-BCLoTYI0.js} +1 -1
- package/dist/assets/{editor.main-BhSGi_Jw.js → editor.main-UoasbVGy.js} +3 -3
- package/dist/assets/{freemarker2-DZH3si5v.js → freemarker2-dhfKZR7u.js} +1 -1
- package/dist/assets/{handlebars-DvzTd6uL.js → handlebars-DdpqwFuV.js} +1 -1
- package/dist/assets/{html-C5GmopAN.js → html-1oTJClkg.js} +1 -1
- package/dist/assets/{htmlMode-DwnrHwx1.js → htmlMode-CF1QbIg-.js} +1 -1
- package/dist/assets/index-DWDcKbgI.js +3046 -0
- package/dist/assets/index-eqxng9X2.css +32 -0
- package/dist/assets/{javascript-JqHrxiCa.js → javascript-BP_Q5MFx.js} +1 -1
- package/dist/assets/{jsonMode-8rZcy09i.js → jsonMode-BtjA-2w_.js} +1 -1
- package/dist/assets/{liquid-ClpD_v7G.js → liquid-DstuL8vm.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-u0WgQBQz.js → lspLanguageFeatures-DvSiaY4f.js} +1 -1
- package/dist/assets/{mdx-DtViUgdm.js → mdx-qO-uvsJd.js} +1 -1
- package/dist/assets/{python-CaAvhRGm.js → python-CCPz_1cy.js} +1 -1
- package/dist/assets/{razor-saGNVU7l.js → razor-B7tCzkdh.js} +1 -1
- package/dist/assets/{tsMode-HZwWTCj8.js → tsMode-hUkEyjsH.js} +1 -1
- package/dist/assets/{typescript-BInV4PNE.js → typescript-BeXECzAk.js} +1 -1
- package/dist/assets/{whisperWorker-ivwFFLMj.js → whisperWorker-QfIS0sPF.js} +5 -5
- package/dist/assets/{xml-tgO806YR.js → xml-MRJd4GHf.js} +1 -1
- package/dist/assets/{yaml-CHApZArv.js → yaml-CzGliMNL.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +16 -1
- package/src/main/historyAggregator.cjs +208 -0
- package/src/main/index.cjs +4 -0
- package/src/main/ipcSchemas.cjs +15 -0
- package/src/main/lib/schedulerConfig.cjs +2 -0
- package/src/main/scheduler.cjs +604 -120
- package/src/main/supervisor.cjs +512 -0
- package/src/main/usage.cjs +44 -2
- package/src/preload/api.d.ts +64 -2
- package/src/preload/index.cjs +10 -0
- package/dist/assets/index-BGshD4Pw.js +0 -2976
- package/dist/assets/index-DCK87t79.css +0 -32
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./editor.main-
|
|
1
|
+
import{l as e}from"./editor.main-UoasbVGy.js";import"./index-DWDcKbgI.js";const o={comments:{blockComment:["<!--","-->"]},brackets:[["<",">"]],autoClosingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],surroundingPairs:[{open:"<",close:">"},{open:"'",close:"'"},{open:'"',close:'"'}],onEnterRules:[{beforeText:new RegExp("<([_:\\w][_:\\w-.\\d]*)([^/>]*(?!/)>)[^<]*$","i"),afterText:/^<\/([_:\w][_:\w-.\d]*)\s*>$/i,action:{indentAction:e.IndentAction.IndentOutdent}},{beforeText:new RegExp("<(\\w[\\w\\d]*)([^/>]*(?!/)>)[^<]*$","i"),action:{indentAction:e.IndentAction.Indent}}]},i={defaultToken:"",tokenPostfix:".xml",ignoreCase:!0,qualifiedName:/(?:[\w\.\-]+:)?[\w\.\-]+/,tokenizer:{root:[[/[^<&]+/,""],{include:"@whitespace"},[/(<)(@qualifiedName)/,[{token:"delimiter"},{token:"tag",next:"@tag"}]],[/(<\/)(@qualifiedName)(\s*)(>)/,[{token:"delimiter"},{token:"tag"},"",{token:"delimiter"}]],[/(<\?)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/(<\!)(@qualifiedName)/,[{token:"delimiter"},{token:"metatag",next:"@tag"}]],[/<\!\[CDATA\[/,{token:"delimiter.cdata",next:"@cdata"}],[/&\w+;/,"string.escape"]],cdata:[[/[^\]]+/,""],[/\]\]>/,{token:"delimiter.cdata",next:"@pop"}],[/\]/,""]],tag:[[/[ \t\r\n]+/,""],[/(@qualifiedName)(\s*=\s*)("[^"]*"|'[^']*')/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">?\/]*|'[^'>?\/]*)(?=[\?\/]\>)/,["attribute.name","","attribute.value"]],[/(@qualifiedName)(\s*=\s*)("[^">]*|'[^'>]*)/,["attribute.name","","attribute.value"]],[/@qualifiedName/,"attribute.name"],[/\?>/,{token:"delimiter",next:"@pop"}],[/(\/)(>)/,[{token:"tag"},{token:"delimiter",next:"@pop"}]],[/>/,{token:"delimiter",next:"@pop"}]],whitespace:[[/[ \t\r\n]+/,""],[/<!--/,{token:"comment",next:"@comment"}]],comment:[[/[^<\-]+/,"comment.content"],[/-->/,{token:"comment",next:"@pop"}],[/<!--/,"comment.content.invalid"],[/[<\-]/,"comment.content"]]}};export{o as conf,i as language};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{l as e}from"./editor.main-
|
|
1
|
+
import{l as e}from"./editor.main-UoasbVGy.js";import"./index-DWDcKbgI.js";const o={comments:{lineComment:"#"},brackets:[["{","}"],["[","]"],["(",")"]],autoClosingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],surroundingPairs:[{open:"{",close:"}"},{open:"[",close:"]"},{open:"(",close:")"},{open:'"',close:'"'},{open:"'",close:"'"}],folding:{offSide:!0},onEnterRules:[{beforeText:/:\s*$/,action:{indentAction:e.IndentAction.Indent}}]},r={tokenPostfix:".yaml",brackets:[{token:"delimiter.bracket",open:"{",close:"}"},{token:"delimiter.square",open:"[",close:"]"}],keywords:["true","True","TRUE","false","False","FALSE","null","Null","Null","~"],numberInteger:/(?:0|[+-]?[0-9]+)/,numberFloat:/(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,numberOctal:/0o[0-7]+/,numberHex:/0x[0-9a-fA-F]+/,numberInfinity:/[+-]?\.(?:inf|Inf|INF)/,numberNaN:/\.(?:nan|Nan|NAN)/,numberDate:/\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,escapes:/\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,tokenizer:{root:[{include:"@whitespace"},{include:"@comment"},[/%[^ ]+.*$/,"meta.directive"],[/---/,"operators.directivesEnd"],[/\.{3}/,"operators.documentEnd"],[/[-?:](?= )/,"operators"],{include:"@anchor"},{include:"@tagHandle"},{include:"@flowCollections"},{include:"@blockStyle"},[/@numberInteger(?![ \t]*\S+)/,"number"],[/@numberFloat(?![ \t]*\S+)/,"number.float"],[/@numberOctal(?![ \t]*\S+)/,"number.octal"],[/@numberHex(?![ \t]*\S+)/,"number.hex"],[/@numberInfinity(?![ \t]*\S+)/,"number.infinity"],[/@numberNaN(?![ \t]*\S+)/,"number.nan"],[/@numberDate(?![ \t]*\S+)/,"number.date"],[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/,["type","white","operators","white"]],{include:"@flowScalars"},[/.+?(?=(\s+#|$))/,{cases:{"@keywords":"keyword","@default":"string"}}]],object:[{include:"@whitespace"},{include:"@comment"},[/\}/,"@brackets","@pop"],[/,/,"delimiter.comma"],[/:(?= )/,"operators"],[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/,"type"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\},]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],array:[{include:"@whitespace"},{include:"@comment"},[/\]/,"@brackets","@pop"],[/,/,"delimiter.comma"],{include:"@flowCollections"},{include:"@flowScalars"},{include:"@tagHandle"},{include:"@anchor"},{include:"@flowNumber"},[/[^\],]+/,{cases:{"@keywords":"keyword","@default":"string"}}]],multiString:[[/^( +).+$/,"string","@multiStringContinued.$1"]],multiStringContinued:[[/^( *).+$/,{cases:{"$1==$S2":"string","@default":{token:"@rematch",next:"@popall"}}}]],whitespace:[[/[ \t\r\n]+/,"white"]],comment:[[/#.*$/,"comment"]],flowCollections:[[/\[/,"@brackets","@array"],[/\{/,"@brackets","@object"]],flowScalars:[[/"([^"\\]|\\.)*$/,"string.invalid"],[/'([^'\\]|\\.)*$/,"string.invalid"],[/'[^']*'/,"string"],[/"/,"string","@doubleQuotedString"]],doubleQuotedString:[[/[^\\"]+/,"string"],[/@escapes/,"string.escape"],[/\\./,"string.escape.invalid"],[/"/,"string","@pop"]],blockStyle:[[/[>|][0-9]*[+-]?$/,"operators","@multiString"]],flowNumber:[[/@numberInteger(?=[ \t]*[,\]\}])/,"number"],[/@numberFloat(?=[ \t]*[,\]\}])/,"number.float"],[/@numberOctal(?=[ \t]*[,\]\}])/,"number.octal"],[/@numberHex(?=[ \t]*[,\]\}])/,"number.hex"],[/@numberInfinity(?=[ \t]*[,\]\}])/,"number.infinity"],[/@numberNaN(?=[ \t]*[,\]\}])/,"number.nan"],[/@numberDate(?=[ \t]*[,\]\}])/,"number.date"]],tagHandle:[[/\![^ ]*/,"tag"]],anchor:[[/[&*][^ ]+/,"namespace"]]}};export{o as conf,r as language};
|
package/dist/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Claude Session Manager</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-DWDcKbgI.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-eqxng9X2.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body class="bg-bg text-fg font-mono antialiased">
|
|
11
11
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-session-manager",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.5",
|
|
4
4
|
"description": "Local cockpit for Claude Code CLI sessions — terminal + full config surface.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/main/index.cjs",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"start": "electron .",
|
|
27
27
|
"postinstall": "electron-rebuild -f -w node-pty",
|
|
28
28
|
"prepublishOnly": "vite build",
|
|
29
|
+
"test:unit": "vitest run",
|
|
29
30
|
"test:e2e": "xvfb-run -a playwright test",
|
|
30
31
|
"test:e2e:mic": "xvfb-run -a playwright test e2e/mic.spec.mjs",
|
|
31
32
|
"test:e2e:gen-fixture": "espeak-ng -w /tmp/sm-raw.wav -s 140 'testing one two three four five' && ffmpeg -y -i /tmp/sm-raw.wav -ar 48000 -ac 2 -sample_fmt s16 e2e/fixtures/speech.wav",
|
|
@@ -42,6 +43,18 @@
|
|
|
42
43
|
],
|
|
43
44
|
"author": "bilkobibitkov",
|
|
44
45
|
"license": "MIT",
|
|
46
|
+
"homepage": "https://github.com/StanislavBG/claude-code-session-manager#readme",
|
|
47
|
+
"repository": {
|
|
48
|
+
"type": "git",
|
|
49
|
+
"url": "https://github.com/StanislavBG/claude-code-session-manager.git"
|
|
50
|
+
},
|
|
51
|
+
"bugs": {
|
|
52
|
+
"url": "https://github.com/StanislavBG/claude-code-session-manager/issues"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public",
|
|
56
|
+
"provenance": true
|
|
57
|
+
},
|
|
45
58
|
"engines": {
|
|
46
59
|
"node": ">=18"
|
|
47
60
|
},
|
|
@@ -82,9 +95,11 @@
|
|
|
82
95
|
"postcss": "^8.4.49",
|
|
83
96
|
"react": "^18.3.1",
|
|
84
97
|
"react-dom": "^18.3.1",
|
|
98
|
+
"recharts": "^2.15.4",
|
|
85
99
|
"tailwindcss": "^3.4.15",
|
|
86
100
|
"typescript": "^5.6.3",
|
|
87
101
|
"vite": "^6.0.1",
|
|
102
|
+
"vitest": "^2.1.9",
|
|
88
103
|
"wait-on": "^8.0.1"
|
|
89
104
|
}
|
|
90
105
|
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { ipcMain } = require('electron');
|
|
4
|
+
const fsp = require('node:fs/promises');
|
|
5
|
+
const path = require('node:path');
|
|
6
|
+
const os = require('node:os');
|
|
7
|
+
|
|
8
|
+
const PROJECTS_DIR = path.join(os.homedir(), '.claude', 'projects');
|
|
9
|
+
const SLOW_THRESHOLD_MS = 2_000;
|
|
10
|
+
const MAX_FILE_BYTES = 20 * 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
function decodeCwd(encoded) {
|
|
13
|
+
return '/' + encoded.replace(/-+/g, '/');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function subtractDays(dateStr, days) {
|
|
17
|
+
const d = new Date(dateStr + 'T00:00:00Z');
|
|
18
|
+
d.setUTCDate(d.getUTCDate() - days);
|
|
19
|
+
return d.toISOString().slice(0, 10);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function parseJSONL(filePath, stat) {
|
|
23
|
+
const acc = {
|
|
24
|
+
promptCount: 0,
|
|
25
|
+
inputTokens: 0,
|
|
26
|
+
outputTokens: 0,
|
|
27
|
+
cacheReadTokens: 0,
|
|
28
|
+
cacheCreationTokens: 0,
|
|
29
|
+
toolCallCount: 0,
|
|
30
|
+
toolBreakdown: {},
|
|
31
|
+
errorCount: 0,
|
|
32
|
+
sessionDate: null,
|
|
33
|
+
skipped: false,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
if (stat.size > MAX_FILE_BYTES) {
|
|
37
|
+
acc.skipped = true;
|
|
38
|
+
return acc;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let text;
|
|
42
|
+
try {
|
|
43
|
+
text = await fsp.readFile(filePath, 'utf8');
|
|
44
|
+
} catch {
|
|
45
|
+
return acc;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const lines = text.split('\n');
|
|
49
|
+
let firstTs = null;
|
|
50
|
+
|
|
51
|
+
for (const raw of lines) {
|
|
52
|
+
const line = raw.trim();
|
|
53
|
+
if (!line) continue;
|
|
54
|
+
let obj;
|
|
55
|
+
try {
|
|
56
|
+
obj = JSON.parse(line);
|
|
57
|
+
} catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (firstTs === null) {
|
|
62
|
+
const ts = obj.ts ?? obj.timestamp;
|
|
63
|
+
if (ts) firstTs = ts;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const role = obj.role ?? obj.message?.role;
|
|
67
|
+
if (role === 'user') acc.promptCount++;
|
|
68
|
+
|
|
69
|
+
const usage = obj.usage ?? obj.message?.usage;
|
|
70
|
+
if (usage && typeof usage === 'object') {
|
|
71
|
+
if (typeof usage.inputTokens === 'number') acc.inputTokens += usage.inputTokens;
|
|
72
|
+
if (typeof usage.outputTokens === 'number') acc.outputTokens += usage.outputTokens;
|
|
73
|
+
if (typeof usage.cacheReadInputTokens === 'number') acc.cacheReadTokens += usage.cacheReadInputTokens;
|
|
74
|
+
if (typeof usage.cacheCreationInputTokens === 'number') acc.cacheCreationTokens += usage.cacheCreationInputTokens;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const content = obj.message?.content ?? obj.content;
|
|
78
|
+
if (Array.isArray(content)) {
|
|
79
|
+
for (const block of content) {
|
|
80
|
+
if (block?.type === 'tool_use' && typeof block.name === 'string') {
|
|
81
|
+
acc.toolCallCount++;
|
|
82
|
+
acc.toolBreakdown[block.name] = (acc.toolBreakdown[block.name] ?? 0) + 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (
|
|
88
|
+
(obj.type === 'tool_result' && obj.is_error === true) ||
|
|
89
|
+
obj.message?.stop_reason === 'error' ||
|
|
90
|
+
obj.stop_reason === 'error'
|
|
91
|
+
) {
|
|
92
|
+
acc.errorCount++;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
acc.sessionDate = firstTs
|
|
98
|
+
? new Date(firstTs).toISOString().slice(0, 10)
|
|
99
|
+
: new Date(stat.mtimeMs).toISOString().slice(0, 10);
|
|
100
|
+
} catch {
|
|
101
|
+
acc.sessionDate = new Date(stat.mtimeMs).toISOString().slice(0, 10);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return acc;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function registerHistoryAggregatorHandlers() {
|
|
108
|
+
ipcMain.handle('history:aggregate', async (_e, req) => {
|
|
109
|
+
const t0 = Date.now();
|
|
110
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
111
|
+
let effectiveTo = (req?.toDate && /^\d{4}-\d{2}-\d{2}$/.test(req.toDate)) ? req.toDate : today;
|
|
112
|
+
if (effectiveTo > today) effectiveTo = today;
|
|
113
|
+
const effectiveFrom = (req?.fromDate && /^\d{4}-\d{2}-\d{2}$/.test(req.fromDate))
|
|
114
|
+
? req.fromDate
|
|
115
|
+
: subtractDays(today, 30);
|
|
116
|
+
|
|
117
|
+
const buckets = new Map();
|
|
118
|
+
let partial = false;
|
|
119
|
+
let skippedLargeFiles = 0;
|
|
120
|
+
|
|
121
|
+
let projectDirs;
|
|
122
|
+
try {
|
|
123
|
+
projectDirs = await fsp.readdir(PROJECTS_DIR, { withFileTypes: true });
|
|
124
|
+
} catch {
|
|
125
|
+
return { rows: [], partial: false, scannedMs: Date.now() - t0 };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
for (const projEntry of projectDirs) {
|
|
129
|
+
if (!projEntry.isDirectory()) continue;
|
|
130
|
+
const encodedCwd = projEntry.name;
|
|
131
|
+
const projectDir = path.join(PROJECTS_DIR, encodedCwd);
|
|
132
|
+
|
|
133
|
+
let files;
|
|
134
|
+
try {
|
|
135
|
+
files = await fsp.readdir(projectDir, { withFileTypes: true });
|
|
136
|
+
} catch {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
for (const fileEntry of files) {
|
|
141
|
+
if (!fileEntry.name.endsWith('.jsonl')) continue;
|
|
142
|
+
const filePath = path.join(projectDir, fileEntry.name);
|
|
143
|
+
|
|
144
|
+
let stat;
|
|
145
|
+
try {
|
|
146
|
+
stat = await fsp.stat(filePath);
|
|
147
|
+
} catch {
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const parsed = await parseJSONL(filePath, stat);
|
|
152
|
+
if (parsed.skipped) { skippedLargeFiles++; continue; }
|
|
153
|
+
|
|
154
|
+
const { sessionDate } = parsed;
|
|
155
|
+
if (!sessionDate || sessionDate < effectiveFrom || sessionDate >= effectiveTo) continue;
|
|
156
|
+
|
|
157
|
+
const key = `${sessionDate}|${encodedCwd}`;
|
|
158
|
+
if (!buckets.has(key)) {
|
|
159
|
+
buckets.set(key, {
|
|
160
|
+
date: sessionDate,
|
|
161
|
+
projectCwd: decodeCwd(encodedCwd),
|
|
162
|
+
encodedCwd,
|
|
163
|
+
promptCount: 0,
|
|
164
|
+
inputTokens: 0,
|
|
165
|
+
outputTokens: 0,
|
|
166
|
+
cacheReadTokens: 0,
|
|
167
|
+
cacheCreationTokens: 0,
|
|
168
|
+
toolCallCount: 0,
|
|
169
|
+
toolBreakdown: {},
|
|
170
|
+
sessionCount: 0,
|
|
171
|
+
errorCount: 0,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const b = buckets.get(key);
|
|
176
|
+
b.promptCount += parsed.promptCount;
|
|
177
|
+
b.inputTokens += parsed.inputTokens;
|
|
178
|
+
b.outputTokens += parsed.outputTokens;
|
|
179
|
+
b.cacheReadTokens += parsed.cacheReadTokens;
|
|
180
|
+
b.cacheCreationTokens += parsed.cacheCreationTokens;
|
|
181
|
+
b.toolCallCount += parsed.toolCallCount;
|
|
182
|
+
for (const [tool, cnt] of Object.entries(parsed.toolBreakdown)) {
|
|
183
|
+
b.toolBreakdown[tool] = (b.toolBreakdown[tool] ?? 0) + cnt;
|
|
184
|
+
}
|
|
185
|
+
b.sessionCount++;
|
|
186
|
+
b.errorCount += parsed.errorCount;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (Date.now() - t0 > SLOW_THRESHOLD_MS) {
|
|
190
|
+
console.warn(`[historyAggregator] slow scan: ${Date.now() - t0}ms`);
|
|
191
|
+
partial = true;
|
|
192
|
+
break;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const rows = Array.from(buckets.values()).map((b) => ({
|
|
197
|
+
...b,
|
|
198
|
+
estimatedCostUsd: (b.inputTokens * 3 + b.outputTokens * 15) / 1_000_000,
|
|
199
|
+
}));
|
|
200
|
+
|
|
201
|
+
rows.sort((a, b) => a.date.localeCompare(b.date) || a.projectCwd.localeCompare(b.projectCwd));
|
|
202
|
+
|
|
203
|
+
const scannedMs = Date.now() - t0;
|
|
204
|
+
return { rows, partial, scannedMs, skippedLargeFiles };
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
module.exports = { registerHistoryAggregatorHandlers };
|
package/src/main/index.cjs
CHANGED
|
@@ -15,9 +15,11 @@ const logs = require('./logs.cjs');
|
|
|
15
15
|
const voiceHotkey = require('./voiceHotkey.cjs');
|
|
16
16
|
const voiceWizard = require('./voiceWizard.cjs');
|
|
17
17
|
const scheduler = require('./scheduler.cjs');
|
|
18
|
+
const supervisor = require('./supervisor.cjs');
|
|
18
19
|
const watchers = require('./watchers.cjs');
|
|
19
20
|
const otel = require('./otel.cjs');
|
|
20
21
|
const otelSettings = require('./otelSettings.cjs');
|
|
22
|
+
const { registerHistoryAggregatorHandlers } = require('./historyAggregator.cjs');
|
|
21
23
|
|
|
22
24
|
let mainWindow = null;
|
|
23
25
|
let rebooting = false;
|
|
@@ -410,6 +412,7 @@ voiceHotkey.registerHotkeyHandlers();
|
|
|
410
412
|
voiceWizard.registerWizardHandlers();
|
|
411
413
|
scheduler.registerScheduleHandlers();
|
|
412
414
|
watchers.registerWatcherHandlers();
|
|
415
|
+
registerHistoryAggregatorHandlers();
|
|
413
416
|
|
|
414
417
|
// OTEL telemetry export (opt-in via ~/.config/session-manager/otel.json).
|
|
415
418
|
ipcMain.handle('otel:get-config', async () => otelSettings.load());
|
|
@@ -630,6 +633,7 @@ app.on('activate', () => {
|
|
|
630
633
|
});
|
|
631
634
|
|
|
632
635
|
app.on('before-quit', () => {
|
|
636
|
+
supervisor.stopSupervisor();
|
|
633
637
|
ptyManager.killAll();
|
|
634
638
|
configMgr.closeAllWatchers();
|
|
635
639
|
transcripts.closeAll();
|
package/src/main/ipcSchemas.cjs
CHANGED
|
@@ -127,8 +127,22 @@ const setConfigSchema = z.object({
|
|
|
127
127
|
).optional(),
|
|
128
128
|
firePolicy: z.enum(['when-available', 'on-reset', 'manual']).optional(),
|
|
129
129
|
utilizationThreshold: z.number().min(0).max(200).optional(),
|
|
130
|
+
supervisor: z.object({
|
|
131
|
+
enabled: z.boolean().optional(),
|
|
132
|
+
intervalMinutes: z.number().int().min(5).max(60).optional(),
|
|
133
|
+
maxConcurrentProbes: z.number().int().min(1).max(5).optional(),
|
|
134
|
+
probeStaleThresholdMinutes: z.number().int().min(5).max(30).optional(),
|
|
135
|
+
}).optional(),
|
|
130
136
|
}).strict();
|
|
131
137
|
|
|
138
|
+
// ──────────────────────────────────────────── History
|
|
139
|
+
const DATE_YYYY_MM_DD = /^\d{4}-\d{2}-\d{2}$/;
|
|
140
|
+
|
|
141
|
+
const historyAggregate = z.object({
|
|
142
|
+
fromDate: z.string().regex(DATE_YYYY_MM_DD).optional(),
|
|
143
|
+
toDate: z.string().regex(DATE_YYYY_MM_DD).optional(),
|
|
144
|
+
}).optional().nullable();
|
|
145
|
+
|
|
132
146
|
/**
|
|
133
147
|
* Wrap an IPC handler with schema validation. Returns a new handler that
|
|
134
148
|
* parses the payload before calling the original.
|
|
@@ -162,6 +176,7 @@ module.exports = {
|
|
|
162
176
|
openInFinder,
|
|
163
177
|
openInTerminal,
|
|
164
178
|
archiveProject,
|
|
179
|
+
historyAggregate,
|
|
165
180
|
},
|
|
166
181
|
validated,
|
|
167
182
|
};
|