ec-web 0.0.23 → 0.0.25
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 -21
- package/README.md +17 -17
- package/dist/index.js +1 -1
- package/dist/server.js +6 -15
- package/dist/sources/session-codex.js +11 -11
- package/dist/sources/stats.js +92 -92
- package/dist/static/app.js +11691 -11675
- package/dist/static/index.html +427 -427
- package/dist/static/style.css +4899 -4899
- package/dist/static/vendor/echarts-5.6.0.min.js +45 -45
- package/ecweb-wrapper.cjs +67 -67
- package/package.json +35 -35
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 EvolCore contributors
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EvolCore contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
# EC Web
|
|
2
|
-
|
|
3
|
-
Web console and monitoring dashboard for EvolCore.
|
|
4
|
-
|
|
5
|
-
## Install
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g ec-web
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Run
|
|
12
|
-
|
|
13
|
-
```bash
|
|
14
|
-
ec-web --home /path/to/runtime-home
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
Use `ec watch web` when EC Web is managed by the main daemon.
|
|
1
|
+
# EC Web
|
|
2
|
+
|
|
3
|
+
Web console and monitoring dashboard for EvolCore.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g ec-web
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Run
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
ec-web --home /path/to/runtime-home
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Use `ec watch web` when EC Web is managed by the main daemon.
|
package/dist/index.js
CHANGED
|
@@ -107,7 +107,7 @@ for (const list of Object.values(ifaces)) {
|
|
|
107
107
|
}
|
|
108
108
|
process.stdout.write(`\n${BOLD}${CYAN}EC Web — EvolCore${RST} ${DIM}(home: ${p.root})${RST}\n\n`);
|
|
109
109
|
process.stdout.write(handle.pairingCode
|
|
110
|
-
? ` ${BOLD}配对码:${RST} ${GREEN}${BOLD}${handle.pairingCode}${RST} ${DIM}(daemon 提供,5 分钟内有效,配对后 token 缓存
|
|
110
|
+
? ` ${BOLD}配对码:${RST} ${GREEN}${BOLD}${handle.pairingCode}${RST} ${DIM}(daemon 提供,5 分钟内有效,配对后 token 缓存 7 天,有访问自动续期)${RST}\n\n`
|
|
111
111
|
: ` ${BOLD}配对码:${RST} ${YELLOW}daemon 暂不可用,请先启动 EvolCore${RST}\n\n`);
|
|
112
112
|
process.stdout.write(` ${BOLD}本机:${RST} http://localhost:${handle.port}\n`);
|
|
113
113
|
for (const ip of lanIps)
|
package/dist/server.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
*
|
|
4
4
|
* - HTTP: 静态资源 + 配对 API
|
|
5
5
|
* - WebSocket: 订阅式实时推送(aid / msg / session)
|
|
6
|
-
* - 鉴权: daemon 通过本地 IPC 提供 6 位配对码(5 分钟有效)和远程访问 token(
|
|
7
|
-
* - 安全: 绑定 0.0.0.0
|
|
6
|
+
* - 鉴权: daemon 通过本地 IPC 提供 6 位配对码(5 分钟有效)和远程访问 token(7 天滑动续期)
|
|
7
|
+
* - 安全: 绑定 0.0.0.0(支持远程访问);本地自动登录,远程配对后使用 token,登录后权限一致
|
|
8
8
|
*
|
|
9
9
|
* 与 evolcore daemon 通过 IPC socket(状态、Menu 和远程访问凭据)+ 文件系统(历史数据)旁路通信。
|
|
10
10
|
*/
|
|
@@ -437,7 +437,7 @@ function handlePair(req, res, log) {
|
|
|
437
437
|
return;
|
|
438
438
|
}
|
|
439
439
|
res.writeHead(200, { 'Content-Type': 'application/json' }).end(JSON.stringify({ ok: true, token: result.token }));
|
|
440
|
-
log(`✓ 配对成功 from ${ip}(daemon token 缓存
|
|
440
|
+
log(`✓ 配对成功 from ${ip}(daemon token 缓存 7 天,有访问自动续期)`);
|
|
441
441
|
}).catch(error => {
|
|
442
442
|
res.writeHead(503, { 'Content-Type': 'application/json' }).end(JSON.stringify({ ok: false, reason: 'daemon unavailable' }));
|
|
443
443
|
log(`✗ 配对失败(daemon unavailable: ${error instanceof Error ? error.message : String(error)}) from ${ip}`);
|
|
@@ -790,22 +790,13 @@ export async function startWatchWebServer(opts = {}) {
|
|
|
790
790
|
if (autoToken)
|
|
791
791
|
token = autoToken;
|
|
792
792
|
}
|
|
793
|
-
|
|
793
|
+
const tokenRecord = token ? await validateRemoteToken(token) : null;
|
|
794
|
+
if (!token || !tokenRecord) {
|
|
794
795
|
res.writeHead(401, { 'Content-Type': 'application/json' });
|
|
795
796
|
res.end(JSON.stringify({ success: false, error: 'unauthorized' }));
|
|
796
797
|
return;
|
|
797
798
|
}
|
|
798
|
-
|
|
799
|
-
res.writeHead(403, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
|
|
800
|
-
res.end(JSON.stringify({ success: false, error: 'remote ECWeb connections cannot reveal API keys' }));
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
803
|
-
if (!localDirect && req.method !== 'GET') {
|
|
804
|
-
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
805
|
-
res.end(JSON.stringify({ success: false, error: 'remote ECWeb connections cannot modify daemon settings' }));
|
|
806
|
-
return;
|
|
807
|
-
}
|
|
808
|
-
void handleBasicSettingsApi(req, res, { redactConnectionMaps: !localDirect });
|
|
799
|
+
void handleBasicSettingsApi(req, res, { redactConnectionMaps: !isLocalDirect(req) });
|
|
809
800
|
}
|
|
810
801
|
else if (req.method === 'GET' && (req.url || '').startsWith('/api/stats/')) {
|
|
811
802
|
// Stats API — 本地直连自动发 token(免鉴权),远程需鉴权
|
|
@@ -205,12 +205,12 @@ function listProjects() {
|
|
|
205
205
|
if (!db)
|
|
206
206
|
return [];
|
|
207
207
|
try {
|
|
208
|
-
const rows = db.prepare(`
|
|
209
|
-
SELECT cwd, COUNT(*) as count, MAX(updated_at) as lastActivity
|
|
210
|
-
FROM threads
|
|
211
|
-
WHERE archived = 0
|
|
212
|
-
GROUP BY cwd
|
|
213
|
-
ORDER BY lastActivity DESC
|
|
208
|
+
const rows = db.prepare(`
|
|
209
|
+
SELECT cwd, COUNT(*) as count, MAX(updated_at) as lastActivity
|
|
210
|
+
FROM threads
|
|
211
|
+
WHERE archived = 0
|
|
212
|
+
GROUP BY cwd
|
|
213
|
+
ORDER BY lastActivity DESC
|
|
214
214
|
`).all();
|
|
215
215
|
const projects = rows.map(r => {
|
|
216
216
|
const cwd = r.cwd || '';
|
|
@@ -382,11 +382,11 @@ function listTranscripts(encoded, cwd) {
|
|
|
382
382
|
if (!db)
|
|
383
383
|
return [];
|
|
384
384
|
try {
|
|
385
|
-
const rows = db.prepare(`
|
|
386
|
-
SELECT id, title, first_user_message, updated_at, rollout_path, source, thread_source
|
|
387
|
-
FROM threads
|
|
388
|
-
WHERE cwd = ? AND archived = 0
|
|
389
|
-
ORDER BY updated_at DESC
|
|
385
|
+
const rows = db.prepare(`
|
|
386
|
+
SELECT id, title, first_user_message, updated_at, rollout_path, source, thread_source
|
|
387
|
+
FROM threads
|
|
388
|
+
WHERE cwd = ? AND archived = 0
|
|
389
|
+
ORDER BY updated_at DESC
|
|
390
390
|
`).all(cwd);
|
|
391
391
|
const out = [];
|
|
392
392
|
let parsed = 0, hitMem = 0, hitDisk = 0;
|
package/dist/sources/stats.js
CHANGED
|
@@ -46,41 +46,41 @@ export function queryStatsForDashboard() {
|
|
|
46
46
|
const h24ago = Date.now() - 24 * 60 * 60 * 1000;
|
|
47
47
|
try {
|
|
48
48
|
// Today summary with cost
|
|
49
|
-
const todayRow = db.prepare(`
|
|
50
|
-
SELECT
|
|
51
|
-
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
52
|
-
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
53
|
-
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
54
|
-
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
55
|
-
COUNT(*) AS call_count,
|
|
56
|
-
COALESCE(SUM(cost_gateway_usd),0) AS cost_usd,
|
|
57
|
-
COALESCE(SUM(cost_gateway_cny),0) AS cost_cny
|
|
58
|
-
FROM usage_events WHERE ts >= ?
|
|
49
|
+
const todayRow = db.prepare(`
|
|
50
|
+
SELECT
|
|
51
|
+
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
52
|
+
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
53
|
+
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
54
|
+
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
55
|
+
COUNT(*) AS call_count,
|
|
56
|
+
COALESCE(SUM(cost_gateway_usd),0) AS cost_usd,
|
|
57
|
+
COALESCE(SUM(cost_gateway_cny),0) AS cost_cny
|
|
58
|
+
FROM usage_events WHERE ts >= ?
|
|
59
59
|
`).get(todayStart);
|
|
60
60
|
const totalIn = (todayRow.input_tokens ?? 0) + (todayRow.cache_read_tokens ?? 0);
|
|
61
61
|
const hitRate = totalIn > 0 ? (todayRow.cache_read_tokens ?? 0) / totalIn : 0;
|
|
62
62
|
// Hourly (last 24h)
|
|
63
|
-
const hourly = db.prepare(`
|
|
64
|
-
SELECT
|
|
65
|
-
strftime('%Y-%m-%d %H:00', ts/1000, 'unixepoch', 'localtime') AS hour,
|
|
66
|
-
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
67
|
-
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
68
|
-
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
69
|
-
COUNT(*) AS call_count
|
|
70
|
-
FROM usage_events WHERE ts >= ?
|
|
71
|
-
GROUP BY hour ORDER BY hour
|
|
63
|
+
const hourly = db.prepare(`
|
|
64
|
+
SELECT
|
|
65
|
+
strftime('%Y-%m-%d %H:00', ts/1000, 'unixepoch', 'localtime') AS hour,
|
|
66
|
+
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
67
|
+
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
68
|
+
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
69
|
+
COUNT(*) AS call_count
|
|
70
|
+
FROM usage_events WHERE ts >= ?
|
|
71
|
+
GROUP BY hour ORDER BY hour
|
|
72
72
|
`).all(h24ago);
|
|
73
73
|
// Top models (today)
|
|
74
|
-
const top_models = db.prepare(`
|
|
75
|
-
SELECT model, SUM(input_tokens+output_tokens) AS total_tokens, COUNT(*) AS call_count
|
|
76
|
-
FROM usage_events WHERE ts >= ?
|
|
77
|
-
GROUP BY model ORDER BY total_tokens DESC LIMIT 10
|
|
74
|
+
const top_models = db.prepare(`
|
|
75
|
+
SELECT model, SUM(input_tokens+output_tokens) AS total_tokens, COUNT(*) AS call_count
|
|
76
|
+
FROM usage_events WHERE ts >= ?
|
|
77
|
+
GROUP BY model ORDER BY total_tokens DESC LIMIT 10
|
|
78
78
|
`).all(todayStart);
|
|
79
79
|
// Top peers (today)
|
|
80
|
-
const top_peers = db.prepare(`
|
|
81
|
-
SELECT peer_key, SUM(input_tokens+output_tokens) AS total_tokens, COUNT(*) AS call_count
|
|
82
|
-
FROM usage_events WHERE ts >= ?
|
|
83
|
-
GROUP BY peer_key ORDER BY total_tokens DESC LIMIT 5
|
|
80
|
+
const top_peers = db.prepare(`
|
|
81
|
+
SELECT peer_key, SUM(input_tokens+output_tokens) AS total_tokens, COUNT(*) AS call_count
|
|
82
|
+
FROM usage_events WHERE ts >= ?
|
|
83
|
+
GROUP BY peer_key ORDER BY total_tokens DESC LIMIT 5
|
|
84
84
|
`).all(todayStart);
|
|
85
85
|
return {
|
|
86
86
|
today: { ...todayRow, cache_hit_rate: hitRate },
|
|
@@ -124,16 +124,16 @@ export function queryStatsExplorer(params) {
|
|
|
124
124
|
}
|
|
125
125
|
const where = conds.length ? 'WHERE ' + conds.join(' AND ') : '';
|
|
126
126
|
try {
|
|
127
|
-
return db.prepare(`
|
|
128
|
-
SELECT
|
|
129
|
-
strftime('${strfmt}', ts/1000, 'unixepoch', 'localtime') AS period,
|
|
130
|
-
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
131
|
-
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
132
|
-
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
133
|
-
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
134
|
-
COUNT(*) AS call_count
|
|
135
|
-
FROM usage_events ${where}
|
|
136
|
-
GROUP BY period ORDER BY period
|
|
127
|
+
return db.prepare(`
|
|
128
|
+
SELECT
|
|
129
|
+
strftime('${strfmt}', ts/1000, 'unixepoch', 'localtime') AS period,
|
|
130
|
+
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
131
|
+
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
132
|
+
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
133
|
+
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
134
|
+
COUNT(*) AS call_count
|
|
135
|
+
FROM usage_events ${where}
|
|
136
|
+
GROUP BY period ORDER BY period
|
|
137
137
|
`).all(...p);
|
|
138
138
|
}
|
|
139
139
|
finally {
|
|
@@ -162,14 +162,14 @@ export function queryStatsByPeer(params) {
|
|
|
162
162
|
const where = conds.length ? 'WHERE ' + conds.join(' AND ') : '';
|
|
163
163
|
const limit = params.limit ?? 50;
|
|
164
164
|
try {
|
|
165
|
-
const rows = db.prepare(`
|
|
166
|
-
SELECT peer_key, peer_type,
|
|
167
|
-
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
168
|
-
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
169
|
-
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
170
|
-
COUNT(*) AS call_count
|
|
171
|
-
FROM usage_events ${where}
|
|
172
|
-
GROUP BY peer_key ORDER BY (COALESCE(SUM(input_tokens),0) + COALESCE(SUM(output_tokens),0)) DESC LIMIT ${limit}
|
|
165
|
+
const rows = db.prepare(`
|
|
166
|
+
SELECT peer_key, peer_type,
|
|
167
|
+
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
168
|
+
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
169
|
+
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
170
|
+
COUNT(*) AS call_count
|
|
171
|
+
FROM usage_events ${where}
|
|
172
|
+
GROUP BY peer_key ORDER BY (COALESCE(SUM(input_tokens),0) + COALESCE(SUM(output_tokens),0)) DESC LIMIT ${limit}
|
|
173
173
|
`).all(...p);
|
|
174
174
|
// 为每个peer添加名称和详细信息
|
|
175
175
|
return rows.map((row) => {
|
|
@@ -212,34 +212,34 @@ export function queryStatsOverview(params) {
|
|
|
212
212
|
}
|
|
213
213
|
const where = conds.length ? 'WHERE ' + conds.join(' AND ') : '';
|
|
214
214
|
// Token and cost aggregation with filters
|
|
215
|
-
const allRow = db.prepare(`
|
|
216
|
-
SELECT
|
|
217
|
-
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
218
|
-
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
219
|
-
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
220
|
-
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
221
|
-
COUNT(*) AS call_count,
|
|
222
|
-
COALESCE(SUM(cost_official_usd),0) AS cost_official_usd,
|
|
223
|
-
COALESCE(SUM(cost_official_cny),0) AS cost_official_cny,
|
|
224
|
-
COALESCE(SUM(cost_gateway_usd),0) AS cost_usd,
|
|
225
|
-
COALESCE(SUM(cost_gateway_cny),0) AS cost_cny
|
|
226
|
-
FROM usage_events ${where}
|
|
215
|
+
const allRow = db.prepare(`
|
|
216
|
+
SELECT
|
|
217
|
+
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
218
|
+
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
219
|
+
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
220
|
+
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
221
|
+
COUNT(*) AS call_count,
|
|
222
|
+
COALESCE(SUM(cost_official_usd),0) AS cost_official_usd,
|
|
223
|
+
COALESCE(SUM(cost_official_cny),0) AS cost_official_cny,
|
|
224
|
+
COALESCE(SUM(cost_gateway_usd),0) AS cost_usd,
|
|
225
|
+
COALESCE(SUM(cost_gateway_cny),0) AS cost_cny
|
|
226
|
+
FROM usage_events ${where}
|
|
227
227
|
`).get(...p);
|
|
228
228
|
// By agent aggregation with filters
|
|
229
|
-
const byAgentRows = db.prepare(`
|
|
230
|
-
SELECT agent_aid,
|
|
231
|
-
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
232
|
-
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
233
|
-
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
234
|
-
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
235
|
-
COUNT(*) AS call_count,
|
|
236
|
-
COALESCE(SUM(cost_official_usd),0) AS cost_official_usd,
|
|
237
|
-
COALESCE(SUM(cost_official_cny),0) AS cost_official_cny,
|
|
238
|
-
COALESCE(SUM(cost_gateway_usd),0) AS cost_usd,
|
|
239
|
-
COALESCE(SUM(cost_gateway_cny),0) AS cost_cny
|
|
240
|
-
FROM usage_events ${where}
|
|
241
|
-
GROUP BY agent_aid
|
|
242
|
-
ORDER BY (COALESCE(SUM(input_tokens),0) + COALESCE(SUM(output_tokens),0)) DESC
|
|
229
|
+
const byAgentRows = db.prepare(`
|
|
230
|
+
SELECT agent_aid,
|
|
231
|
+
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
232
|
+
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
233
|
+
COALESCE(SUM(cache_creation_tokens),0) AS cache_creation_tokens,
|
|
234
|
+
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
235
|
+
COUNT(*) AS call_count,
|
|
236
|
+
COALESCE(SUM(cost_official_usd),0) AS cost_official_usd,
|
|
237
|
+
COALESCE(SUM(cost_official_cny),0) AS cost_official_cny,
|
|
238
|
+
COALESCE(SUM(cost_gateway_usd),0) AS cost_usd,
|
|
239
|
+
COALESCE(SUM(cost_gateway_cny),0) AS cost_cny
|
|
240
|
+
FROM usage_events ${where}
|
|
241
|
+
GROUP BY agent_aid
|
|
242
|
+
ORDER BY (COALESCE(SUM(input_tokens),0) + COALESCE(SUM(output_tokens),0)) DESC
|
|
243
243
|
`).all(...p);
|
|
244
244
|
// 为每个agent添加名称
|
|
245
245
|
const byAgentWithNames = byAgentRows.map((row) => ({
|
|
@@ -303,14 +303,14 @@ export function queryStatsByAgent(params) {
|
|
|
303
303
|
const where = conds.length ? 'WHERE ' + conds.join(' AND ') : '';
|
|
304
304
|
const limit = params.limit ?? 50;
|
|
305
305
|
try {
|
|
306
|
-
const rows = db.prepare(`
|
|
307
|
-
SELECT agent_aid,
|
|
308
|
-
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
309
|
-
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
310
|
-
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
311
|
-
COUNT(*) AS call_count
|
|
312
|
-
FROM usage_events ${where}
|
|
313
|
-
GROUP BY agent_aid ORDER BY (COALESCE(SUM(input_tokens),0) + COALESCE(SUM(output_tokens),0)) DESC LIMIT ${limit}
|
|
306
|
+
const rows = db.prepare(`
|
|
307
|
+
SELECT agent_aid,
|
|
308
|
+
COALESCE(SUM(input_tokens),0) AS input_tokens,
|
|
309
|
+
COALESCE(SUM(output_tokens),0) AS output_tokens,
|
|
310
|
+
COALESCE(SUM(cache_read_tokens),0) AS cache_read_tokens,
|
|
311
|
+
COUNT(*) AS call_count
|
|
312
|
+
FROM usage_events ${where}
|
|
313
|
+
GROUP BY agent_aid ORDER BY (COALESCE(SUM(input_tokens),0) + COALESCE(SUM(output_tokens),0)) DESC LIMIT ${limit}
|
|
314
314
|
`).all(...p);
|
|
315
315
|
// 为每个agent添加名称
|
|
316
316
|
return rows.map((row) => ({
|
|
@@ -353,15 +353,15 @@ export function queryUsageDetail(params) {
|
|
|
353
353
|
const countRow = db.prepare(`SELECT COUNT(*) as total FROM usage_events ${where}`).get(...p);
|
|
354
354
|
const total = countRow?.total || 0;
|
|
355
355
|
// 获取数据
|
|
356
|
-
const data = db.prepare(`
|
|
357
|
-
SELECT ts, agent_aid, peer_key, model,
|
|
358
|
-
input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
|
|
359
|
-
COALESCE(cost_official_usd, 0) AS cost_official_usd,
|
|
360
|
-
COALESCE(cost_official_cny, 0) AS cost_official_cny,
|
|
361
|
-
COALESCE(cost_gateway_usd, 0) AS cost_gateway_usd,
|
|
362
|
-
COALESCE(cost_gateway_cny, 0) AS cost_gateway_cny
|
|
363
|
-
FROM usage_events ${where}
|
|
364
|
-
ORDER BY ts DESC LIMIT ${limit} OFFSET ${offset}
|
|
356
|
+
const data = db.prepare(`
|
|
357
|
+
SELECT ts, agent_aid, peer_key, model,
|
|
358
|
+
input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens,
|
|
359
|
+
COALESCE(cost_official_usd, 0) AS cost_official_usd,
|
|
360
|
+
COALESCE(cost_official_cny, 0) AS cost_official_cny,
|
|
361
|
+
COALESCE(cost_gateway_usd, 0) AS cost_gateway_usd,
|
|
362
|
+
COALESCE(cost_gateway_cny, 0) AS cost_gateway_cny
|
|
363
|
+
FROM usage_events ${where}
|
|
364
|
+
ORDER BY ts DESC LIMIT ${limit} OFFSET ${offset}
|
|
365
365
|
`).all(...p);
|
|
366
366
|
// 为每条记录添加agent_name (需要从文件系统读取agent.md)
|
|
367
367
|
const dataWithNames = data.map((row) => ({
|
|
@@ -391,10 +391,10 @@ export function queryUsedModels(params) {
|
|
|
391
391
|
}
|
|
392
392
|
const where = conds.length ? 'WHERE ' + conds.join(' AND ') : '';
|
|
393
393
|
try {
|
|
394
|
-
const rows = db.prepare(`
|
|
395
|
-
SELECT DISTINCT model
|
|
396
|
-
FROM usage_events ${where}
|
|
397
|
-
ORDER BY model
|
|
394
|
+
const rows = db.prepare(`
|
|
395
|
+
SELECT DISTINCT model
|
|
396
|
+
FROM usage_events ${where}
|
|
397
|
+
ORDER BY model
|
|
398
398
|
`).all(...p);
|
|
399
399
|
return rows.map((row) => row.model).filter(Boolean);
|
|
400
400
|
}
|