tide-commander 1.111.2 → 1.112.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/dist/assets/{BossLogsModal-DLTh_jKZ.js → BossLogsModal-CwHTS7Io.js} +1 -1
- package/dist/assets/{BossSpawnModal-DzivoRl1.js → BossSpawnModal-BXfNSkDz.js} +1 -1
- package/dist/assets/{ControlsModal-B8j6My0t.js → ControlsModal-DR0t4ILF.js} +1 -1
- package/dist/assets/{DockerLogsModal-BNwx7lVp.js → DockerLogsModal-DwnzwL14.js} +1 -1
- package/dist/assets/{EmbeddedEditor-CBoOoGL8.js → EmbeddedEditor-Cgbf4PPi.js} +1 -1
- package/dist/assets/{GmailOAuthSetup-BACIjX54.js → GmailOAuthSetup-COUn-rL4.js} +1 -1
- package/dist/assets/{GoogleOAuthSetup-BuASGyWh.js → GoogleOAuthSetup-DY-oV0lC.js} +1 -1
- package/dist/assets/{IframeModal-ckpeQpQB.js → IframeModal-Cw0HXgic.js} +1 -1
- package/dist/assets/{IntegrationsPanel-Dh7UOnTC.js → IntegrationsPanel-Barw4hcr.js} +2 -2
- package/dist/assets/{LogViewerModal-Cj7Hv-mD.js → LogViewerModal-Wbt81evL.js} +1 -1
- package/dist/assets/{MonitoringModal-Bx4vcjBr.js → MonitoringModal-CNicCwC1.js} +1 -1
- package/dist/assets/{PM2LogsModal-RBIjfi4J.js → PM2LogsModal-CjxdOw_Q.js} +1 -1
- package/dist/assets/{RestoreArchivedAreaModal-BBYlYIso.js → RestoreArchivedAreaModal-DMQSgBDn.js} +1 -1
- package/dist/assets/{Scene2DCanvas-CYB1Y5Wa.js → Scene2DCanvas-Bv96khxo.js} +1 -1
- package/dist/assets/{SceneManager-6vk047Kd.js → SceneManager-BegYb5dZ.js} +1 -1
- package/dist/assets/{SkillsPanel-CRRypqdy.js → SkillsPanel-BMXqdVzv.js} +1 -1
- package/dist/assets/{SlackMultiInstanceSetup-DisMBGxX.js → SlackMultiInstanceSetup-C00oSgQy.js} +1 -1
- package/dist/assets/{SpawnModal-qiu5nT-F.js → SpawnModal-pU2K5vrz.js} +1 -1
- package/dist/assets/StatisticsModal-CC9rhzmt.js +1 -0
- package/dist/assets/{SubordinateAssignmentModal-AUCAiGkE.js → SubordinateAssignmentModal-BPSip6u2.js} +1 -1
- package/dist/assets/{TriggerManagerPanel-56SuYzNh.js → TriggerManagerPanel-DPSqS7Vo.js} +1 -1
- package/dist/assets/{WorkflowEditorPanel-H0IGkyMd.js → WorkflowEditorPanel-Cq7ZF7dg.js} +1 -1
- package/dist/assets/{index-pRI5GcXd.js → index-44R4VNzL.js} +1 -1
- package/dist/assets/{index--3sQ1gHE.js → index-B-I8YHsB.js} +3 -3
- package/dist/assets/{index-DFWA70Lq.js → index-BL_PuMCK.js} +1 -1
- package/dist/assets/{index-C-kx99MP.js → index-ByFl2HJL.js} +1 -1
- package/dist/assets/{index-DY395tVJ.js → index-CiYtVsF2.js} +2 -2
- package/dist/assets/{index-94AqwQn0.js → index-DFJv26as.js} +1 -1
- package/dist/assets/{index-Dv0tQnLX.js → index-DKYfRvHE.js} +1 -1
- package/dist/assets/{index-DZP3fZ3b.js → index-DhZrpzSa.js} +1 -1
- package/dist/assets/{index-9NugNkE4.js → index-WmtsKBi6.js} +1 -1
- package/dist/assets/{main-Bon1sZAi.js → main-D9ETZv9V.js} +20 -20
- package/dist/assets/{main-BUSv52j9.css → main-OfLIQ_-I.css} +1 -1
- package/dist/assets/{web-RZLwjKCT.js → web-B5kMJoeB.js} +1 -1
- package/dist/assets/{web-CccJFC0m.js → web-DthvRrDx.js} +1 -1
- package/dist/assets/{web-CdhZGenV.js → web-gLG4upzZ.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/src/packages/server/routes/agents.js +17 -1
- package/dist/src/packages/server/services/claude-usage-service.js +164 -9
- package/package.json +1 -1
- package/dist/assets/StatisticsModal-Bzk7qqOd.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{co as a}from"./main-D9ETZv9V.js";import{ImpactStyle as i,NotificationType as r}from"./index-CiYtVsF2.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class h extends a{constructor(){super(...arguments),this.selectionStarted=!1}async impact(t){const e=this.patternForImpact(t==null?void 0:t.style);this.vibrateWithPattern(e)}async notification(t){const e=this.patternForNotification(t==null?void 0:t.type);this.vibrateWithPattern(e)}async vibrate(t){const e=(t==null?void 0:t.duration)||300;this.vibrateWithPattern([e])}async selectionStart(){this.selectionStarted=!0}async selectionChanged(){this.selectionStarted&&this.vibrateWithPattern([70])}async selectionEnd(){this.selectionStarted=!1}patternForImpact(t=i.Heavy){return t===i.Medium?[43]:t===i.Light?[20]:[61]}patternForNotification(t=r.Success){return t===r.Warning?[30,40,30,50,60]:t===r.Error?[27,45,50]:[35,65,21]}vibrateWithPattern(t){if(navigator.vibrate)navigator.vibrate(t);else throw this.unavailable("Browser does not support the vibrate API")}}export{h as HapticsWeb};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{co as t}from"./main-D9ETZv9V.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class a extends t{constructor(){super(),this.handleVisibilityChange=()=>{const e={isActive:document.hidden!==!0};this.notifyListeners("appStateChange",e),document.hidden?this.notifyListeners("pause",null):this.notifyListeners("resume",null)},document.addEventListener("visibilitychange",this.handleVisibilityChange,!1)}exitApp(){throw this.unimplemented("Not implemented on web.")}async getInfo(){throw this.unimplemented("Not implemented on web.")}async getLaunchUrl(){return{url:""}}async getState(){return{isActive:document.hidden!==!0}}async minimizeApp(){throw this.unimplemented("Not implemented on web.")}async toggleBackButtonHandler(){throw this.unimplemented("Not implemented on web.")}async getAppLanguage(){return{value:navigator.language.split("-")[0].toLowerCase()}}}export{a as AppWeb};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{co as s}from"./main-D9ETZv9V.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";class l extends s{constructor(){super(...arguments),this.pending=[],this.deliveredNotifications=[],this.hasNotificationSupport=()=>{if(!("Notification"in window)||!Notification.requestPermission)return!1;if(Notification.permission!=="granted")try{new Notification("")}catch(i){if(i instanceof Error&&i.name==="TypeError")return!1}return!0}}async getDeliveredNotifications(){const i=[];for(const t of this.deliveredNotifications){const e={title:t.title,id:parseInt(t.tag),body:t.body};i.push(e)}return{notifications:i}}async removeDeliveredNotifications(i){for(const t of i.notifications){const e=this.deliveredNotifications.find(n=>n.tag===String(t.id));e==null||e.close(),this.deliveredNotifications=this.deliveredNotifications.filter(()=>!e)}}async removeAllDeliveredNotifications(){for(const i of this.deliveredNotifications)i.close();this.deliveredNotifications=[]}async createChannel(){throw this.unimplemented("Not implemented on web.")}async deleteChannel(){throw this.unimplemented("Not implemented on web.")}async listChannels(){throw this.unimplemented("Not implemented on web.")}async schedule(i){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");for(const t of i.notifications)this.sendNotification(t);return{notifications:i.notifications.map(t=>({id:t.id}))}}async getPending(){return{notifications:this.pending}}async registerActionTypes(){throw this.unimplemented("Not implemented on web.")}async cancel(i){this.pending=this.pending.filter(t=>!i.notifications.find(e=>e.id===t.id))}async areEnabled(){const{display:i}=await this.checkPermissions();return{value:i==="granted"}}async changeExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async checkExactNotificationSetting(){throw this.unimplemented("Not implemented on web.")}async requestPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(await Notification.requestPermission())}}async checkPermissions(){if(!this.hasNotificationSupport())throw this.unavailable("Notifications not supported in this browser.");return{display:this.transformNotificationPermission(Notification.permission)}}transformNotificationPermission(i){switch(i){case"granted":return"granted";case"denied":return"denied";default:return"prompt"}}sendPending(){var i;const t=[],e=new Date().getTime();for(const n of this.pending)!((i=n.schedule)===null||i===void 0)&&i.at&&n.schedule.at.getTime()<=e&&(this.buildNotification(n),t.push(n));this.pending=this.pending.filter(n=>!t.find(o=>o===n))}sendNotification(i){var t;if(!((t=i.schedule)===null||t===void 0)&&t.at){const e=i.schedule.at.getTime()-new Date().getTime();this.pending.push(i),setTimeout(()=>{this.sendPending()},e);return}this.buildNotification(i)}buildNotification(i){const t=new Notification(i.title,{body:i.body,tag:String(i.id)});return t.addEventListener("click",this.onClick.bind(this,i),!1),t.addEventListener("show",this.onShow.bind(this,i),!1),t.addEventListener("close",()=>{this.deliveredNotifications=this.deliveredNotifications.filter(()=>!this)},!1),this.deliveredNotifications.push(t),t}onClick(i){const t={actionId:"tap",notification:i};this.notifyListeners("localNotificationActionPerformed",t)}onShow(i){this.notifyListeners("localNotificationReceived",i)}}export{l as LocalNotificationsWeb};
|
package/dist/index.html
CHANGED
|
@@ -22,10 +22,10 @@
|
|
|
22
22
|
<link rel="icon" type="image/png" sizes="16x16" href="/assets/icons/favicon-16x16.png" />
|
|
23
23
|
<link rel="apple-touch-icon" sizes="180x180" href="/assets/icons/apple-touch-icon.png" />
|
|
24
24
|
<title>Tide Commander</title>
|
|
25
|
-
<script type="module" crossorigin src="/assets/main-
|
|
25
|
+
<script type="module" crossorigin src="/assets/main-D9ETZv9V.js"></script>
|
|
26
26
|
<link rel="modulepreload" crossorigin href="/assets/vendor-react--Eh9ivFN.js">
|
|
27
27
|
<link rel="modulepreload" crossorigin href="/assets/vendor-three-Chj50gSY.js">
|
|
28
|
-
<link rel="stylesheet" crossorigin href="/assets/main-
|
|
28
|
+
<link rel="stylesheet" crossorigin href="/assets/main-OfLIQ_-I.css">
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<div id="app"></div>
|
|
@@ -19,7 +19,7 @@ import { buildCustomAgentConfig } from '../websocket/handlers/command-handler.js
|
|
|
19
19
|
import { clearDelegation, getBossForSubordinate } from '../websocket/handlers/boss-response-handler.js';
|
|
20
20
|
import { OpencodeBackend } from '../opencode/backend.js';
|
|
21
21
|
import { getSystemPrompt, setSystemPrompt, clearSystemPrompt, isEchoPromptEnabled, setEchoPromptEnabled, getCodexBinaryPath, setCodexBinaryPath, isTmuxModeEnabled, setTmuxModeEnabled } from '../services/system-prompt-service.js';
|
|
22
|
-
import { buildClaudeUsageByAgentSummary, buildClaudeUsageSnapshot } from '../services/claude-usage-service.js';
|
|
22
|
+
import { buildClaudeUsageByAgentSummary, buildClaudeUsageByDaySummary, buildClaudeUsageSnapshot } from '../services/claude-usage-service.js';
|
|
23
23
|
import { getBackupStatus, setBackupEnabled } from '../services/backup-service.js';
|
|
24
24
|
const log = createLogger('Routes');
|
|
25
25
|
const router = Router();
|
|
@@ -292,6 +292,22 @@ router.get('/usage-by-agent', async (req, res) => {
|
|
|
292
292
|
res.status(500).json({ error: err?.message ?? 'Failed to build usage summary' });
|
|
293
293
|
}
|
|
294
294
|
});
|
|
295
|
+
// GET /api/agents/usage-by-day - Claude JSONL usage totals grouped by local day
|
|
296
|
+
router.get('/usage-by-day', async (req, res) => {
|
|
297
|
+
try {
|
|
298
|
+
const agents = agentService.getAllAgents();
|
|
299
|
+
const summary = await buildClaudeUsageByDaySummary(agents, {
|
|
300
|
+
since: req.query.since,
|
|
301
|
+
until: req.query.until,
|
|
302
|
+
days: req.query.days,
|
|
303
|
+
});
|
|
304
|
+
res.json(summary);
|
|
305
|
+
}
|
|
306
|
+
catch (err) {
|
|
307
|
+
log.error(' Failed to build usage-by-day summary:', err);
|
|
308
|
+
res.status(500).json({ error: err?.message ?? 'Failed to build daily usage summary' });
|
|
309
|
+
}
|
|
310
|
+
});
|
|
295
311
|
// GET /api/agents/:id/process-output - Get `witr --pid` output for this agent process
|
|
296
312
|
router.get('/:id/process-output', async (req, res) => {
|
|
297
313
|
try {
|
|
@@ -42,6 +42,8 @@ function toPositiveInteger(value) {
|
|
|
42
42
|
: 0;
|
|
43
43
|
}
|
|
44
44
|
function parseBoundary(value) {
|
|
45
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
46
|
+
return value;
|
|
45
47
|
if (typeof value !== 'string' || value.trim() === '')
|
|
46
48
|
return null;
|
|
47
49
|
const asNumber = Number(value);
|
|
@@ -154,21 +156,76 @@ function addUsage(acc, requestId, timestamp, usage) {
|
|
|
154
156
|
acc.lastTimestamp = timestamp;
|
|
155
157
|
}
|
|
156
158
|
}
|
|
159
|
+
function createTokenTotals() {
|
|
160
|
+
return {
|
|
161
|
+
input: 0,
|
|
162
|
+
cacheCreation: 0,
|
|
163
|
+
cacheRead: 0,
|
|
164
|
+
output: 0,
|
|
165
|
+
total: 0,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
157
168
|
function createAccumulator() {
|
|
158
169
|
return {
|
|
159
170
|
requestIds: new Set(),
|
|
160
171
|
firstTimestamp: null,
|
|
161
172
|
lastTimestamp: null,
|
|
162
|
-
tokens:
|
|
163
|
-
input: 0,
|
|
164
|
-
cacheCreation: 0,
|
|
165
|
-
cacheRead: 0,
|
|
166
|
-
output: 0,
|
|
167
|
-
total: 0,
|
|
168
|
-
},
|
|
173
|
+
tokens: createTokenTotals(),
|
|
169
174
|
};
|
|
170
175
|
}
|
|
171
|
-
|
|
176
|
+
function createDailyAccumulator() {
|
|
177
|
+
return {
|
|
178
|
+
requestIds: new Set(),
|
|
179
|
+
buckets: new Map(),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function addTokenTotals(target, source) {
|
|
183
|
+
target.input += source.input;
|
|
184
|
+
target.cacheCreation += source.cacheCreation;
|
|
185
|
+
target.cacheRead += source.cacheRead;
|
|
186
|
+
target.output += source.output;
|
|
187
|
+
target.total += source.total;
|
|
188
|
+
}
|
|
189
|
+
function localDateKey(timestampMs) {
|
|
190
|
+
const d = new Date(timestampMs);
|
|
191
|
+
const yyyy = d.getFullYear();
|
|
192
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
193
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
194
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
195
|
+
}
|
|
196
|
+
function startOfLocalDay(timestampMs) {
|
|
197
|
+
const d = new Date(timestampMs);
|
|
198
|
+
d.setHours(0, 0, 0, 0);
|
|
199
|
+
return d.getTime();
|
|
200
|
+
}
|
|
201
|
+
function addUsageToDailyAccumulator(acc, requestId, timestamp, usage) {
|
|
202
|
+
if (acc.requestIds.has(requestId))
|
|
203
|
+
return;
|
|
204
|
+
const timestampMs = Date.parse(timestamp);
|
|
205
|
+
if (!Number.isFinite(timestampMs))
|
|
206
|
+
return;
|
|
207
|
+
const input = toPositiveInteger(usage.input_tokens);
|
|
208
|
+
const cacheCreation = toPositiveInteger(usage.cache_creation_input_tokens);
|
|
209
|
+
const cacheRead = toPositiveInteger(usage.cache_read_input_tokens);
|
|
210
|
+
const output = toPositiveInteger(usage.output_tokens);
|
|
211
|
+
const total = input + cacheCreation + cacheRead + output;
|
|
212
|
+
if (total <= 0)
|
|
213
|
+
return;
|
|
214
|
+
acc.requestIds.add(requestId);
|
|
215
|
+
const date = localDateKey(timestampMs);
|
|
216
|
+
const bucket = acc.buckets.get(date) ?? {
|
|
217
|
+
requestCount: 0,
|
|
218
|
+
tokens: createTokenTotals(),
|
|
219
|
+
};
|
|
220
|
+
bucket.requestCount += 1;
|
|
221
|
+
bucket.tokens.input += input;
|
|
222
|
+
bucket.tokens.cacheCreation += cacheCreation;
|
|
223
|
+
bucket.tokens.cacheRead += cacheRead;
|
|
224
|
+
bucket.tokens.output += output;
|
|
225
|
+
bucket.tokens.total += total;
|
|
226
|
+
acc.buckets.set(date, bucket);
|
|
227
|
+
}
|
|
228
|
+
async function scanFileUsageRecords(filePath, sinceMs, untilMs, onRecord) {
|
|
172
229
|
// Fast-path: if `since` is set and the file hasn't been touched since then,
|
|
173
230
|
// no line in it can be within the window — skip the read entirely. Safe
|
|
174
231
|
// because Claude appends to these files; mtime tracks the latest line.
|
|
@@ -195,9 +252,19 @@ async function scanFileIntoAccumulator(filePath, acc, sinceMs, untilMs) {
|
|
|
195
252
|
continue;
|
|
196
253
|
if (untilMs !== null && timestampMs > untilMs)
|
|
197
254
|
continue;
|
|
198
|
-
|
|
255
|
+
onRecord(parsed.requestId, parsed.timestamp, parsed.usage);
|
|
199
256
|
}
|
|
200
257
|
}
|
|
258
|
+
async function scanFileIntoAccumulator(filePath, acc, sinceMs, untilMs) {
|
|
259
|
+
await scanFileUsageRecords(filePath, sinceMs, untilMs, (requestId, timestamp, usage) => {
|
|
260
|
+
addUsage(acc, requestId, timestamp, usage);
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
async function scanFileIntoDailyAccumulator(filePath, acc, sinceMs, untilMs) {
|
|
264
|
+
await scanFileUsageRecords(filePath, sinceMs, untilMs, (requestId, timestamp, usage) => {
|
|
265
|
+
addUsageToDailyAccumulator(acc, requestId, timestamp, usage);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
201
268
|
function todayString() {
|
|
202
269
|
const d = new Date();
|
|
203
270
|
const yyyy = d.getFullYear();
|
|
@@ -293,3 +360,91 @@ export async function buildClaudeUsageByAgentSummary(agents, opts = {}) {
|
|
|
293
360
|
entries,
|
|
294
361
|
};
|
|
295
362
|
}
|
|
363
|
+
function parseDays(value) {
|
|
364
|
+
const parsed = typeof value === 'string' ? Number(value) : value;
|
|
365
|
+
if (typeof parsed !== 'number' || !Number.isFinite(parsed))
|
|
366
|
+
return 14;
|
|
367
|
+
return Math.max(2, Math.min(60, Math.round(parsed)));
|
|
368
|
+
}
|
|
369
|
+
function buildDayKeys(sinceMs, untilMs) {
|
|
370
|
+
const keys = [];
|
|
371
|
+
const untilDay = startOfLocalDay(untilMs);
|
|
372
|
+
for (let cursor = startOfLocalDay(sinceMs); cursor <= untilDay; cursor += 24 * 60 * 60 * 1000) {
|
|
373
|
+
keys.push(localDateKey(cursor));
|
|
374
|
+
}
|
|
375
|
+
return keys;
|
|
376
|
+
}
|
|
377
|
+
export async function buildClaudeUsageByDaySummary(agents, opts = {}) {
|
|
378
|
+
const untilMs = parseBoundary(opts.until) ?? Date.now();
|
|
379
|
+
const sinceMs = parseBoundary(opts.since) ?? (startOfLocalDay(untilMs) - ((parseDays(opts.days) - 1) * 24 * 60 * 60 * 1000));
|
|
380
|
+
const agentBuckets = new Map();
|
|
381
|
+
for (const agent of agents) {
|
|
382
|
+
if (agent.provider !== 'claude' || !agent.sessionId)
|
|
383
|
+
continue;
|
|
384
|
+
const paths = findClaudeSessionFile(agent);
|
|
385
|
+
if (!paths)
|
|
386
|
+
continue;
|
|
387
|
+
try {
|
|
388
|
+
const mainAcc = createDailyAccumulator();
|
|
389
|
+
await scanFileIntoDailyAccumulator(paths.main, mainAcc, sinceMs, untilMs);
|
|
390
|
+
const subAcc = createDailyAccumulator();
|
|
391
|
+
for (const subPath of paths.subagents) {
|
|
392
|
+
await scanFileIntoDailyAccumulator(subPath, subAcc, sinceMs, untilMs);
|
|
393
|
+
}
|
|
394
|
+
const buckets = new Map();
|
|
395
|
+
const mergeBucket = (date, source) => {
|
|
396
|
+
const target = buckets.get(date) ?? {
|
|
397
|
+
requestCount: 0,
|
|
398
|
+
tokens: createTokenTotals(),
|
|
399
|
+
};
|
|
400
|
+
target.requestCount += source.requestCount;
|
|
401
|
+
addTokenTotals(target.tokens, source.tokens);
|
|
402
|
+
buckets.set(date, target);
|
|
403
|
+
};
|
|
404
|
+
for (const [date, bucket] of mainAcc.buckets)
|
|
405
|
+
mergeBucket(date, bucket);
|
|
406
|
+
for (const [date, bucket] of subAcc.buckets)
|
|
407
|
+
mergeBucket(date, bucket);
|
|
408
|
+
const agentTotal = [...buckets.values()].reduce((sum, bucket) => sum + bucket.tokens.total, 0);
|
|
409
|
+
if (agentTotal <= 0)
|
|
410
|
+
continue;
|
|
411
|
+
agentBuckets.set(agent.id, {
|
|
412
|
+
agentId: agent.id,
|
|
413
|
+
agentName: agent.name,
|
|
414
|
+
buckets,
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
catch (err) {
|
|
418
|
+
log.warn(`Failed to scan Claude daily usage for agent ${agent.name} (${agent.id}): ${err}`);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
const days = buildDayKeys(sinceMs, untilMs).map((date) => {
|
|
422
|
+
const dayAgents = [];
|
|
423
|
+
for (const agentUsage of agentBuckets.values()) {
|
|
424
|
+
const bucket = agentUsage.buckets.get(date);
|
|
425
|
+
if (!bucket || bucket.tokens.total <= 0)
|
|
426
|
+
continue;
|
|
427
|
+
dayAgents.push({
|
|
428
|
+
agentId: agentUsage.agentId,
|
|
429
|
+
agentName: agentUsage.agentName,
|
|
430
|
+
tokens: { ...bucket.tokens },
|
|
431
|
+
requestCount: bucket.requestCount,
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
dayAgents.sort((a, b) => b.tokens.total - a.tokens.total);
|
|
435
|
+
return {
|
|
436
|
+
date,
|
|
437
|
+
totalTokens: dayAgents.reduce((sum, entry) => sum + entry.tokens.total, 0),
|
|
438
|
+
requestCount: dayAgents.reduce((sum, entry) => sum + entry.requestCount, 0),
|
|
439
|
+
agents: dayAgents,
|
|
440
|
+
};
|
|
441
|
+
});
|
|
442
|
+
return {
|
|
443
|
+
provider: 'claude',
|
|
444
|
+
fetchedAt: Date.now(),
|
|
445
|
+
since: normalizeBoundary(sinceMs),
|
|
446
|
+
until: normalizeBoundary(untilMs),
|
|
447
|
+
source: 'claude-jsonl',
|
|
448
|
+
days,
|
|
449
|
+
};
|
|
450
|
+
}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{u as y,h as w,ad as l,cb as C,j as s,b6 as S,I as j,bx as N}from"./main-Bon1sZAi.js";import"./vendor-react--Eh9ivFN.js";import"./vendor-three-Chj50gSY.js";const d=["#6ab8c8","#c89a5a","#9a80c0","#5cb88a","#c85a5a","#c87a9a","#c8c87a","#5a8fd4","#d45a5a","#8a6fbf"];function V(){const i=new Date;return i.setHours(0,0,0,0),i.getTime()}function b(i){if(!i)return"N/A";try{return new Date(i).toLocaleString()}catch{return"N/A"}}function D(i,n){if(i.length===0||n<=0)return"conic-gradient(rgba(50, 50, 62, 0.95) 0deg 360deg)";let t=0;return`conic-gradient(${i.map((u,e)=>{const m=t,c=u.tokens.total/n*360;return t+=c,`${d[e%d.length]} ${m.toFixed(2)}deg ${t.toFixed(2)}deg`}).join(", ")})`}function T({isOpen:i,onClose:n}){const{t}=y(["terminal","common"]),{handleMouseDown:_,handleClick:u}=w(n),[e,m]=l.useState(null),[c,h]=l.useState(!1),[o,p]=l.useState(null),x=l.useMemo(()=>V(),[]),g=l.useCallback(async()=>{h(!0),p(null);try{const a=await C({since:x,until:Date.now()});m(a)}catch(a){p((a==null?void 0:a.message)||"Error")}finally{h(!1)}},[x]);if(l.useEffect(()=>{i&&g()},[i,g]),!i)return null;const r=(e==null?void 0:e.entries)??[],f=(e==null?void 0:e.totalTokens)??0,v=D(r,f);return s.jsx(S,{children:s.jsx("div",{className:"modal-overlay visible",onMouseDown:_,onClick:u,children:s.jsxs("div",{className:"modal statistics-modal",children:[s.jsxs("div",{className:"modal-header statistics-modal__header",children:[s.jsxs("div",{className:"statistics-modal__title",children:[s.jsx("span",{className:"statistics-modal__icon",children:s.jsx(j,{name:"dashboard",size:15})}),s.jsx("span",{children:t("terminal:statistics.title",{defaultValue:"Statistics"})})]}),s.jsx("button",{className:"modal-close statistics-modal__close",onClick:n,title:t("common:buttons.close"),children:"×"})]}),s.jsx("div",{className:"modal-body statistics-modal__body",children:s.jsxs("section",{className:"statistics-panel",children:[s.jsxs("div",{className:"statistics-panel__header",children:[s.jsxs("div",{children:[s.jsx("h3",{children:t("terminal:statistics.usageByAgent",{defaultValue:"Claude usage today"})}),s.jsx("span",{className:"statistics-panel__subtitle",children:t("terminal:statistics.usageByAgentSubtitle",{defaultValue:"Input + cache creation + cache read + output tokens, deduped by Claude request id"})})]}),s.jsx("button",{type:"button",className:"statistics-panel__refresh",onClick:g,disabled:c,title:t("terminal:statistics.refresh",{defaultValue:"Refresh usage"}),children:s.jsx(j,{name:"refresh",size:14})})]}),c&&!e&&s.jsx("div",{className:"statistics-panel__empty",children:t("terminal:statistics.loading",{defaultValue:"Loading usage..."})}),!c&&o&&s.jsx("div",{className:"statistics-panel__empty statistics-panel__empty--error",children:t("terminal:statistics.error",{defaultValue:"Failed to load usage: {{error}}",error:o})}),!o&&e&&r.length===0&&s.jsx("div",{className:"statistics-panel__empty",children:t("terminal:statistics.empty",{defaultValue:"No Claude token usage found for today."})}),!o&&e&&r.length>0&&s.jsxs(s.Fragment,{children:[s.jsxs("div",{className:"statistics-panel__layout",children:[s.jsx("div",{className:"statistics-panel__chart-wrap",children:s.jsx("div",{className:"statistics-panel__pie",style:{background:v},role:"img","aria-label":t("terminal:statistics.pieLabel",{defaultValue:"Claude token usage by agent"}),children:s.jsxs("div",{className:"statistics-panel__pie-hole",children:[s.jsx("strong",{children:N(f)}),s.jsx("span",{children:t("terminal:statistics.total",{defaultValue:"total"})})]})})}),s.jsx("div",{className:"statistics-panel__list",children:r.map((a,k)=>s.jsxs("div",{className:"statistics-panel__row",title:`${a.agentName}: ${a.tokens.total.toLocaleString()} tokens`,children:[s.jsx("span",{className:"statistics-panel__swatch",style:{backgroundColor:d[k%d.length]}}),s.jsxs("div",{className:"statistics-panel__row-main",children:[s.jsx("div",{className:"statistics-panel__row-name",children:a.agentName}),s.jsxs("div",{className:"statistics-panel__row-meta",children:[a.requestCount.toLocaleString()," ",t("terminal:statistics.requests",{defaultValue:"requests"})," / ",a.percent,"%"]})]}),s.jsx("strong",{children:N(a.tokens.total)})]},a.agentId))})]}),s.jsx("div",{className:"statistics-panel__footnote",children:t("terminal:statistics.window",{defaultValue:"Window: {{since}} to {{until}}",since:b(e.since),until:b(e.until)})})]})]})})]})})})}export{T as StatisticsModal};
|