tide-commander 1.111.2 → 1.113.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-Bc0DqA9X.js} +1 -1
- package/dist/assets/{BossSpawnModal-DzivoRl1.js → BossSpawnModal-D-HI5-Rm.js} +1 -1
- package/dist/assets/{ControlsModal-B8j6My0t.js → ControlsModal-CTIbjWJQ.js} +1 -1
- package/dist/assets/{DockerLogsModal-BNwx7lVp.js → DockerLogsModal-CZrAeIrY.js} +1 -1
- package/dist/assets/{EmbeddedEditor-CBoOoGL8.js → EmbeddedEditor-DeNPLdlN.js} +1 -1
- package/dist/assets/{GmailOAuthSetup-BACIjX54.js → GmailOAuthSetup-CVE2MUNk.js} +1 -1
- package/dist/assets/{GoogleOAuthSetup-BuASGyWh.js → GoogleOAuthSetup-DPVaJm-J.js} +1 -1
- package/dist/assets/{IframeModal-ckpeQpQB.js → IframeModal-D0Njx7nV.js} +1 -1
- package/dist/assets/{IntegrationsPanel-Dh7UOnTC.js → IntegrationsPanel-N8GC9dFw.js} +2 -2
- package/dist/assets/{LogViewerModal-Cj7Hv-mD.js → LogViewerModal-DXfAOI0h.js} +1 -1
- package/dist/assets/{MonitoringModal-Bx4vcjBr.js → MonitoringModal-uCFkCztW.js} +1 -1
- package/dist/assets/{PM2LogsModal-RBIjfi4J.js → PM2LogsModal-DGW_92OG.js} +1 -1
- package/dist/assets/{RestoreArchivedAreaModal-BBYlYIso.js → RestoreArchivedAreaModal-DWyz3iM2.js} +1 -1
- package/dist/assets/{Scene2DCanvas-CYB1Y5Wa.js → Scene2DCanvas-CNpWfkXW.js} +1 -1
- package/dist/assets/{SceneManager-6vk047Kd.js → SceneManager-EwY3yHEm.js} +1 -1
- package/dist/assets/{SkillsPanel-CRRypqdy.js → SkillsPanel-YTLlXzWr.js} +1 -1
- package/dist/assets/{SlackMultiInstanceSetup-DisMBGxX.js → SlackMultiInstanceSetup-bAWpygC_.js} +1 -1
- package/dist/assets/{SpawnModal-qiu5nT-F.js → SpawnModal-BWuxUbwF.js} +1 -1
- package/dist/assets/StatisticsModal-fsomOWqv.js +1 -0
- package/dist/assets/{SubordinateAssignmentModal-AUCAiGkE.js → SubordinateAssignmentModal-Cyhy71yX.js} +1 -1
- package/dist/assets/{TriggerManagerPanel-56SuYzNh.js → TriggerManagerPanel-BCDVqT8Q.js} +1 -1
- package/dist/assets/{WorkflowEditorPanel-H0IGkyMd.js → WorkflowEditorPanel-Btq2XPt5.js} +1 -1
- package/dist/assets/{index-94AqwQn0.js → index-BEm8Kg82.js} +1 -1
- package/dist/assets/{index-9NugNkE4.js → index-BPAwPCsE.js} +1 -1
- package/dist/assets/{index-DY395tVJ.js → index-C1Y50w20.js} +2 -2
- package/dist/assets/{index--3sQ1gHE.js → index-CMa6Bc5_.js} +3 -3
- package/dist/assets/{index-C-kx99MP.js → index-Cn-EYfAP.js} +1 -1
- package/dist/assets/{index-DFWA70Lq.js → index-DvzFVxNn.js} +1 -1
- package/dist/assets/{index-DZP3fZ3b.js → index-Glbq4ytE.js} +1 -1
- package/dist/assets/{index-pRI5GcXd.js → index-aMq5H0fp.js} +1 -1
- package/dist/assets/{index-Dv0tQnLX.js → index-qAdi3D_H.js} +1 -1
- package/dist/assets/main-BEfBoz8e.css +1 -0
- package/dist/assets/main-vfMoYhg1.js +214 -0
- package/dist/assets/{web-CccJFC0m.js → web-CMiQoHZs.js} +1 -1
- package/dist/assets/{web-CdhZGenV.js → web-DN5Nx28H.js} +1 -1
- package/dist/assets/{web-RZLwjKCT.js → web-ZMOcyRHy.js} +1 -1
- package/dist/index.html +2 -2
- package/dist/locales/de/terminal.json +1 -0
- package/dist/locales/en/terminal.json +1 -0
- package/dist/locales/es/terminal.json +1 -0
- package/dist/locales/fr/terminal.json +1 -0
- package/dist/locales/hi/terminal.json +1 -0
- package/dist/locales/it/terminal.json +1 -0
- package/dist/locales/ja/terminal.json +1 -0
- package/dist/locales/pt/terminal.json +1 -0
- package/dist/locales/ru/terminal.json +1 -0
- package/dist/locales/zh-CN/terminal.json +1 -0
- package/dist/src/packages/server/claude/backend.js +2 -0
- package/dist/src/packages/server/data/builtin-skills/boss-instructions.js +1 -1
- package/dist/src/packages/server/routes/agents.js +17 -1
- package/dist/src/packages/server/services/claude-usage-service.js +164 -9
- package/dist/src/packages/server/services/llm-matcher-service.js +5 -0
- package/dist/src/packages/shared/agent-types.js +2 -0
- package/package.json +1 -1
- package/dist/assets/StatisticsModal-Bzk7qqOd.js +0 -1
- package/dist/assets/main-BUSv52j9.css +0 -1
- package/dist/assets/main-Bon1sZAi.js +0 -214
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{co as t}from"./main-vfMoYhg1.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-vfMoYhg1.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};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{co as a}from"./main-vfMoYhg1.js";import{ImpactStyle as i,NotificationType as r}from"./index-C1Y50w20.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};
|
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-vfMoYhg1.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-BEfBoz8e.css">
|
|
29
29
|
</head>
|
|
30
30
|
<body>
|
|
31
31
|
<div id="app"></div>
|
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"byStatus": "Nach Status",
|
|
230
230
|
"byName": "Nach Name",
|
|
231
231
|
"searchAgents": "Agenten suchen...",
|
|
232
|
+
"searchArea": "Bereich suchen...",
|
|
232
233
|
"areas": "Bereiche",
|
|
233
234
|
"noAgentsDeployed": "Keine Agenten bereitgestellt",
|
|
234
235
|
"noAgentsMatch": "Keine Agenten passen zu den Filtern",
|
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"byStatus": "Por estado",
|
|
230
230
|
"byName": "Por nombre",
|
|
231
231
|
"searchAgents": "Buscar agentes...",
|
|
232
|
+
"searchArea": "Buscar área...",
|
|
232
233
|
"areas": "Areas",
|
|
233
234
|
"noAgentsDeployed": "Sin agentes desplegados",
|
|
234
235
|
"noAgentsMatch": "Ningun agente coincide con los filtros",
|
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"byStatus": "Par statut",
|
|
230
230
|
"byName": "Par nom",
|
|
231
231
|
"searchAgents": "Rechercher des agents...",
|
|
232
|
+
"searchArea": "Rechercher une zone...",
|
|
232
233
|
"areas": "Zones",
|
|
233
234
|
"noAgentsDeployed": "Aucun agent déployé",
|
|
234
235
|
"noAgentsMatch": "Aucun agent ne correspond aux filtres",
|
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"byStatus": "स्थिति के अनुसार",
|
|
230
230
|
"byName": "नाम के अनुसार",
|
|
231
231
|
"searchAgents": "एजेंट खोजें...",
|
|
232
|
+
"searchArea": "क्षेत्र खोजें...",
|
|
232
233
|
"areas": "क्षेत्र",
|
|
233
234
|
"noAgentsDeployed": "कोई एजेंट तैनात नहीं",
|
|
234
235
|
"noAgentsMatch": "कोई एजेंट फ़िल्टर से मेल नहीं खाता",
|
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"byStatus": "Per stato",
|
|
230
230
|
"byName": "Per nome",
|
|
231
231
|
"searchAgents": "Cerca agenti...",
|
|
232
|
+
"searchArea": "Cerca area...",
|
|
232
233
|
"areas": "Aree",
|
|
233
234
|
"noAgentsDeployed": "Nessun agente distribuito",
|
|
234
235
|
"noAgentsMatch": "Nessun agente corrisponde ai filtri",
|
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"byStatus": "Por Status",
|
|
230
230
|
"byName": "Por Nome",
|
|
231
231
|
"searchAgents": "Pesquisar agentes...",
|
|
232
|
+
"searchArea": "Pesquisar área...",
|
|
232
233
|
"areas": "Areas",
|
|
233
234
|
"noAgentsDeployed": "Nenhum agente implantado",
|
|
234
235
|
"noAgentsMatch": "Nenhum agente corresponde aos filtros",
|
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"byStatus": "По статусу",
|
|
230
230
|
"byName": "По имени",
|
|
231
231
|
"searchAgents": "Поиск агентов...",
|
|
232
|
+
"searchArea": "Поиск области...",
|
|
232
233
|
"areas": "Зоны",
|
|
233
234
|
"noAgentsDeployed": "Нет развёрнутых агентов",
|
|
234
235
|
"noAgentsMatch": "Нет агентов, соответствующих фильтрам",
|
|
@@ -168,6 +168,8 @@ export class ClaudeBackend {
|
|
|
168
168
|
cliModel = 'claude-opus-4-7';
|
|
169
169
|
else if (config.model === 'claude-opus-4-8[1m]')
|
|
170
170
|
cliModel = 'claude-opus-4-8';
|
|
171
|
+
else if (config.model === 'claude-fable-5[1m]')
|
|
172
|
+
cliModel = 'claude-fable-5';
|
|
171
173
|
args.push('--model', cliModel);
|
|
172
174
|
}
|
|
173
175
|
// Reasoning effort level
|
|
@@ -427,7 +427,7 @@ You can ONLY spawn new agents when the user EXPLICITLY requests it.
|
|
|
427
427
|
"name": "<Agent Name>",
|
|
428
428
|
"class": "<agent class slug>",
|
|
429
429
|
"cwd": "<optional working directory>",
|
|
430
|
-
"model": "<optional: claude-opus-4-8[1m] | opus[1m] | claude-opus-4-8 | claude-opus-4-7 | claude-sonnet-4-6 | claude-haiku-4-5 | etc.>",
|
|
430
|
+
"model": "<optional: claude-fable-5[1m] | claude-fable-5 | claude-opus-4-8[1m] | opus[1m] | claude-opus-4-8 | claude-opus-4-7 | claude-sonnet-4-6 | claude-haiku-4-5 | etc.>",
|
|
431
431
|
"effort": "<optional: low | medium | high | xHigh | max>",
|
|
432
432
|
"initialSkillIds": ["<optional skill-id-1>", "<skill-id-2>"],
|
|
433
433
|
"provider": "<optional: claude | codex | opencode>",
|
|
@@ -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
|
+
}
|
|
@@ -18,6 +18,11 @@ const MODEL_MAP = {
|
|
|
18
18
|
haiku: 'claude-haiku-4-5-20251001',
|
|
19
19
|
sonnet: 'claude-sonnet-4-6-20250514',
|
|
20
20
|
opus: 'claude-opus-4-8',
|
|
21
|
+
fable: 'claude-fable-5',
|
|
22
|
+
'fable-5': 'claude-fable-5',
|
|
23
|
+
fable5: 'claude-fable-5',
|
|
24
|
+
'claude-fable-5': 'claude-fable-5',
|
|
25
|
+
'claude-fable-5[1m]': 'claude-fable-5',
|
|
21
26
|
'claude-opus-4-8': 'claude-opus-4-8',
|
|
22
27
|
'claude-opus-4-8[1m]': 'claude-opus-4-8',
|
|
23
28
|
'claude-opus-4-7': 'claude-opus-4-7',
|
|
@@ -63,12 +63,14 @@ export const CODEX_MODELS = {
|
|
|
63
63
|
};
|
|
64
64
|
export const CLAUDE_MODELS = {
|
|
65
65
|
sonnet: { label: 'Sonnet', description: 'Balanced performance and cost (recommended)', icon: '⚡', contextWindow: 200000 },
|
|
66
|
+
'claude-fable-5[1m]': { label: 'Fable 5 [1M]', description: 'Most powerful, most intelligent Claude model — new tier above Opus, 1M token context window', icon: '🪄', contextWindow: 1000000 },
|
|
66
67
|
'claude-opus-4-8[1m]': { label: 'Opus 4.8 [1M]', description: 'Latest Opus with 1M token context window — most capable, best for very long tasks', icon: '🧠', contextWindow: 1000000 },
|
|
67
68
|
'opus[1m]': { label: 'Opus 4.7 [1M]', description: 'Previous Opus generation with 1M token context window', icon: '🧠', contextWindow: 1000000 },
|
|
68
69
|
haiku: { label: 'Haiku', description: 'Fast and economical', icon: '🚀', contextWindow: 200000 },
|
|
69
70
|
// Plain (200K) Opus IDs are kept as valid model values for existing agents
|
|
70
71
|
// and CLI passthrough, but hidden from the "new agent" picker in favor of
|
|
71
72
|
// the 1M variants above.
|
|
73
|
+
'claude-fable-5': { label: 'Fable 5 (200K)', description: 'Fable 5, 200K context window (1M variant preferred)', icon: '🪄', contextWindow: 200000, deprecated: true },
|
|
72
74
|
'claude-opus-4-8': { label: 'Opus 4.8 (200K)', description: 'Latest Opus, 200K context window (1M variant preferred)', icon: '🧠', contextWindow: 200000, deprecated: true },
|
|
73
75
|
'claude-opus-4-7': { label: 'Opus 4.7 (200K)', description: 'Previous Opus generation, 200K context window (1M variant preferred)', icon: '🧠', contextWindow: 200000, deprecated: true },
|
|
74
76
|
opus: { label: 'Opus (legacy)', description: 'Legacy alias — prefer Opus 4.8 [1M]', icon: '🧠', contextWindow: 200000, deprecated: true },
|
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};
|