agent-tank 0.9.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/LICENSE +21 -0
- package/README.md +675 -0
- package/bin/agent-tank.js +301 -0
- package/media/www-preview.png +0 -0
- package/package.json +65 -0
- package/src/activity-monitor.js +312 -0
- package/src/agents/api-response-parser.js +255 -0
- package/src/agents/base.js +396 -0
- package/src/agents/claude.js +388 -0
- package/src/agents/codex-pty-helpers.js +226 -0
- package/src/agents/codex-rpc-helpers.js +167 -0
- package/src/agents/codex.js +383 -0
- package/src/agents/gemini.js +302 -0
- package/src/agents/keepalive-helper.js +115 -0
- package/src/agents/oauth-helper.js +210 -0
- package/src/agents/pty-output-parser.js +301 -0
- package/src/auto-refresh-manager.js +246 -0
- package/src/client-auto-refresh-helpers.js +143 -0
- package/src/client-auto-refresh.js +321 -0
- package/src/client-favicon.js +198 -0
- package/src/client-monitor.js +242 -0
- package/src/client-script.js +147 -0
- package/src/discovery.js +128 -0
- package/src/history-store.js +273 -0
- package/src/http-handler.js +182 -0
- package/src/icons.js +67 -0
- package/src/index.js +376 -0
- package/src/json-rpc-client.js +285 -0
- package/src/keepalive-manager.js +163 -0
- package/src/logger.js +213 -0
- package/src/pace-attachment.js +104 -0
- package/src/pace-evaluator.js +269 -0
- package/src/public-status.js +276 -0
- package/src/server.js +144 -0
- package/src/snapshot-metrics.js +79 -0
- package/src/status-page.css +1149 -0
- package/src/status-page.js +128 -0
- package/src/usage-formatters.js +259 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KeepaliveManager - Manages periodic session keepalive for registered agents
|
|
3
|
+
*
|
|
4
|
+
* Prevents session expiration by scheduling lightweight ping/keepalive operations
|
|
5
|
+
* on registered agents at a configurable interval.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
class KeepaliveManager {
|
|
9
|
+
/**
|
|
10
|
+
* Create a KeepaliveManager instance.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} options - Configuration options
|
|
13
|
+
* @param {number} [options.interval=300] - Keepalive interval in seconds (default: 5 minutes)
|
|
14
|
+
* @param {boolean} [options.enabled=true] - Whether keepalive is enabled
|
|
15
|
+
*/
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.interval = options.interval ?? 300; // Default: 5 minutes
|
|
18
|
+
this.enabled = options.enabled !== false; // Default: true
|
|
19
|
+
this.agents = new Map();
|
|
20
|
+
this.timer = null;
|
|
21
|
+
this.lastKeepaliveAt = null;
|
|
22
|
+
this.isRunning = false;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Register an agent with the keepalive manager.
|
|
27
|
+
*
|
|
28
|
+
* @param {string} name - Agent name
|
|
29
|
+
* @param {Object} agent - Agent instance (must have a keepalive() method)
|
|
30
|
+
*/
|
|
31
|
+
register(name, agent) {
|
|
32
|
+
if (!agent) {
|
|
33
|
+
console.warn(`[KeepaliveManager] Cannot register null agent: ${name}`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (typeof agent.keepalive !== 'function') {
|
|
38
|
+
console.warn(`[KeepaliveManager] Agent ${name} does not implement keepalive() method`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
this.agents.set(name, agent);
|
|
43
|
+
console.log(`[KeepaliveManager] Registered agent: ${name}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Unregister an agent from the keepalive manager.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} name - Agent name to unregister
|
|
50
|
+
*/
|
|
51
|
+
unregister(name) {
|
|
52
|
+
if (this.agents.has(name)) {
|
|
53
|
+
this.agents.delete(name);
|
|
54
|
+
console.log(`[KeepaliveManager] Unregistered agent: ${name}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Start the keepalive scheduler.
|
|
60
|
+
* Runs keepalive immediately on start, then at the configured interval.
|
|
61
|
+
*/
|
|
62
|
+
start() {
|
|
63
|
+
if (this.isRunning) {
|
|
64
|
+
console.log('[KeepaliveManager] Already running');
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!this.enabled) {
|
|
69
|
+
console.log('[KeepaliveManager] Keepalive disabled');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (this.interval <= 0) {
|
|
74
|
+
console.log('[KeepaliveManager] Keepalive interval is 0, disabled');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (this.agents.size === 0) {
|
|
79
|
+
console.log('[KeepaliveManager] No agents registered, not starting');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.isRunning = true;
|
|
84
|
+
const intervalMs = this.interval * 1000;
|
|
85
|
+
|
|
86
|
+
console.log(`[KeepaliveManager] Starting with ${this.interval}s interval for ${this.agents.size} agent(s)`);
|
|
87
|
+
|
|
88
|
+
// Start the periodic timer
|
|
89
|
+
this.timer = setInterval(() => {
|
|
90
|
+
this._runKeepalive();
|
|
91
|
+
}, intervalMs);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Stop the keepalive scheduler.
|
|
96
|
+
*/
|
|
97
|
+
stop() {
|
|
98
|
+
if (this.timer) {
|
|
99
|
+
clearInterval(this.timer);
|
|
100
|
+
this.timer = null;
|
|
101
|
+
}
|
|
102
|
+
this.isRunning = false;
|
|
103
|
+
console.log('[KeepaliveManager] Stopped');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Run keepalive on all registered agents.
|
|
108
|
+
* Errors in individual agents do not affect others.
|
|
109
|
+
*
|
|
110
|
+
* @returns {Promise<Object>} Results keyed by agent name
|
|
111
|
+
*/
|
|
112
|
+
async _runKeepalive() {
|
|
113
|
+
if (this.agents.size === 0) {
|
|
114
|
+
return {};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
console.log(`[KeepaliveManager] Running keepalive for ${this.agents.size} agent(s)...`);
|
|
118
|
+
const results = {};
|
|
119
|
+
|
|
120
|
+
const promises = Array.from(this.agents.entries()).map(async ([name, agent]) => {
|
|
121
|
+
try {
|
|
122
|
+
await agent.keepalive();
|
|
123
|
+
results[name] = { success: true };
|
|
124
|
+
console.log(`[KeepaliveManager] Keepalive successful: ${name}`);
|
|
125
|
+
} catch (err) {
|
|
126
|
+
// Log error but don't crash - keepalive errors should not affect the server
|
|
127
|
+
results[name] = { success: false, error: err.message };
|
|
128
|
+
console.error(`[KeepaliveManager] Keepalive failed for ${name}: ${err.message}`);
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
await Promise.all(promises);
|
|
133
|
+
this.lastKeepaliveAt = new Date().toISOString();
|
|
134
|
+
|
|
135
|
+
return results;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Manually trigger a keepalive run.
|
|
140
|
+
*
|
|
141
|
+
* @returns {Promise<Object>} Results keyed by agent name
|
|
142
|
+
*/
|
|
143
|
+
async runNow() {
|
|
144
|
+
return this._runKeepalive();
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get the current status of the keepalive manager.
|
|
149
|
+
*
|
|
150
|
+
* @returns {Object} Status information
|
|
151
|
+
*/
|
|
152
|
+
getStatus() {
|
|
153
|
+
return {
|
|
154
|
+
enabled: this.enabled,
|
|
155
|
+
interval: this.interval,
|
|
156
|
+
isRunning: this.isRunning,
|
|
157
|
+
registeredAgents: Array.from(this.agents.keys()),
|
|
158
|
+
lastKeepaliveAt: this.lastKeepaliveAt,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = { KeepaliveManager };
|
package/src/logger.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom ANSI Logger Utility
|
|
3
|
+
*
|
|
4
|
+
* A lightweight, dependency-free ANSI logger providing color-coded output,
|
|
5
|
+
* agent-specific styles, and highlighted URLs.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// ANSI escape codes for styling
|
|
9
|
+
const ANSI = {
|
|
10
|
+
// Reset
|
|
11
|
+
reset: '\x1b[0m',
|
|
12
|
+
|
|
13
|
+
// Text styles
|
|
14
|
+
bold: '\x1b[1m',
|
|
15
|
+
dim: '\x1b[2m',
|
|
16
|
+
underline: '\x1b[4m',
|
|
17
|
+
|
|
18
|
+
// Foreground colors
|
|
19
|
+
black: '\x1b[30m',
|
|
20
|
+
red: '\x1b[31m',
|
|
21
|
+
green: '\x1b[32m',
|
|
22
|
+
yellow: '\x1b[33m',
|
|
23
|
+
blue: '\x1b[34m',
|
|
24
|
+
magenta: '\x1b[35m',
|
|
25
|
+
cyan: '\x1b[36m',
|
|
26
|
+
white: '\x1b[37m',
|
|
27
|
+
|
|
28
|
+
// Bright foreground colors
|
|
29
|
+
brightBlack: '\x1b[90m',
|
|
30
|
+
brightRed: '\x1b[91m',
|
|
31
|
+
brightGreen: '\x1b[92m',
|
|
32
|
+
brightYellow: '\x1b[93m',
|
|
33
|
+
brightBlue: '\x1b[94m',
|
|
34
|
+
brightMagenta: '\x1b[95m',
|
|
35
|
+
brightCyan: '\x1b[96m',
|
|
36
|
+
brightWhite: '\x1b[97m',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Agent color mapping
|
|
40
|
+
const AGENT_COLORS = {
|
|
41
|
+
claude: ANSI.magenta,
|
|
42
|
+
gemini: ANSI.cyan,
|
|
43
|
+
codex: ANSI.green,
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// URL regex pattern for highlighting
|
|
47
|
+
const URL_PATTERN = /https?:\/\/[^\s<>"{}|\\^`[\]]+/g;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Highlights URLs in a string with underline and bright white color
|
|
51
|
+
* (bright white provides better readability on dark terminal backgrounds)
|
|
52
|
+
* @param {string} text - The text to process
|
|
53
|
+
* @returns {string} - Text with highlighted URLs
|
|
54
|
+
*/
|
|
55
|
+
function highlightUrls(text) {
|
|
56
|
+
if (typeof text !== 'string') {
|
|
57
|
+
return text;
|
|
58
|
+
}
|
|
59
|
+
return text.replace(URL_PATTERN, (url) => {
|
|
60
|
+
return `${ANSI.brightWhite}${ANSI.underline}${url}${ANSI.reset}`;
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Formats arguments into a single string, handling objects and arrays
|
|
66
|
+
* @param {...any} args - Arguments to format
|
|
67
|
+
* @returns {string} - Formatted string
|
|
68
|
+
*/
|
|
69
|
+
function formatArgs(...args) {
|
|
70
|
+
return args.map(arg => {
|
|
71
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
72
|
+
try {
|
|
73
|
+
return JSON.stringify(arg);
|
|
74
|
+
} catch {
|
|
75
|
+
return String(arg);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return String(arg);
|
|
79
|
+
}).join(' ');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Colorizes text with the specified ANSI color
|
|
84
|
+
* @param {string} color - ANSI color code
|
|
85
|
+
* @param {string} text - Text to colorize
|
|
86
|
+
* @returns {string} - Colorized text
|
|
87
|
+
*/
|
|
88
|
+
function colorize(color, text) {
|
|
89
|
+
return `${color}${text}${ANSI.reset}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Creates a prefixed log message
|
|
94
|
+
* @param {string} prefix - The prefix (e.g., "INFO", "ERROR")
|
|
95
|
+
* @param {string} color - ANSI color for the prefix
|
|
96
|
+
* @param {...any} args - Message arguments
|
|
97
|
+
* @returns {string} - Formatted message
|
|
98
|
+
*/
|
|
99
|
+
function createPrefixedMessage(prefix, color, ...args) {
|
|
100
|
+
const formattedMessage = highlightUrls(formatArgs(...args));
|
|
101
|
+
return `${colorize(color, `[${prefix}]`)} ${formattedMessage}`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Logger object with formatting methods
|
|
106
|
+
*/
|
|
107
|
+
const logger = {
|
|
108
|
+
/**
|
|
109
|
+
* Log an informational message (blue prefix)
|
|
110
|
+
* @param {...any} args - Message arguments
|
|
111
|
+
*/
|
|
112
|
+
info(...args) {
|
|
113
|
+
console.log(createPrefixedMessage('INFO', ANSI.blue, ...args));
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Log a success message (green prefix)
|
|
118
|
+
* @param {...any} args - Message arguments
|
|
119
|
+
*/
|
|
120
|
+
success(...args) {
|
|
121
|
+
console.log(createPrefixedMessage('SUCCESS', ANSI.green, ...args));
|
|
122
|
+
},
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Log a warning message (yellow prefix)
|
|
126
|
+
* @param {...any} args - Message arguments
|
|
127
|
+
*/
|
|
128
|
+
warn(...args) {
|
|
129
|
+
console.warn(createPrefixedMessage('WARN', ANSI.yellow, ...args));
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Log an error message (red prefix)
|
|
134
|
+
* @param {...any} args - Message arguments
|
|
135
|
+
*/
|
|
136
|
+
error(...args) {
|
|
137
|
+
console.error(createPrefixedMessage('ERROR', ANSI.red, ...args));
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Log an agent-specific message with mapped color
|
|
142
|
+
* @param {string} agentName - Name of the agent (claude, gemini, codex)
|
|
143
|
+
* @param {...any} args - Message arguments
|
|
144
|
+
*/
|
|
145
|
+
agent(agentName, ...args) {
|
|
146
|
+
const normalizedName = agentName.toLowerCase();
|
|
147
|
+
const color = AGENT_COLORS[normalizedName] || ANSI.white;
|
|
148
|
+
const formattedMessage = highlightUrls(formatArgs(...args));
|
|
149
|
+
console.log(`${colorize(color, `[${agentName}]`)} ${formattedMessage}`);
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Log a server-related message (cyan prefix with "SERVER" label)
|
|
154
|
+
* @param {...any} args - Message arguments
|
|
155
|
+
*/
|
|
156
|
+
server(...args) {
|
|
157
|
+
console.log(createPrefixedMessage('SERVER', ANSI.cyan, ...args));
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Format text with dim styling (for verbose/secondary output)
|
|
162
|
+
* @param {string} text - Text to dim
|
|
163
|
+
* @returns {string} - Dimmed text
|
|
164
|
+
*/
|
|
165
|
+
dim(text) {
|
|
166
|
+
return `${ANSI.dim}${text}${ANSI.reset}`;
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Format and dim JSON data (for large data payloads)
|
|
171
|
+
* @param {any} data - Data to format as JSON
|
|
172
|
+
* @param {boolean} [pretty=false] - Whether to pretty-print the JSON
|
|
173
|
+
* @returns {string} - Dimmed JSON string
|
|
174
|
+
*/
|
|
175
|
+
json(data, pretty = false) {
|
|
176
|
+
let jsonStr;
|
|
177
|
+
try {
|
|
178
|
+
jsonStr = pretty ? JSON.stringify(data, null, 2) : JSON.stringify(data);
|
|
179
|
+
} catch {
|
|
180
|
+
jsonStr = String(data);
|
|
181
|
+
}
|
|
182
|
+
return `${ANSI.dim}${jsonStr}${ANSI.reset}`;
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Get the color code for a specific agent
|
|
187
|
+
* @param {string} agentName - Name of the agent
|
|
188
|
+
* @returns {string} - ANSI color code
|
|
189
|
+
*/
|
|
190
|
+
getAgentColor(agentName) {
|
|
191
|
+
const normalizedName = agentName.toLowerCase();
|
|
192
|
+
return AGENT_COLORS[normalizedName] || ANSI.white;
|
|
193
|
+
},
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Highlight URLs in text (exposed for external use)
|
|
197
|
+
* @param {string} text - Text to process
|
|
198
|
+
* @returns {string} - Text with highlighted URLs
|
|
199
|
+
*/
|
|
200
|
+
highlightUrls,
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Raw ANSI codes for custom formatting
|
|
204
|
+
*/
|
|
205
|
+
ANSI,
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Agent color mapping for custom formatting
|
|
209
|
+
*/
|
|
210
|
+
AGENT_COLORS,
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
module.exports = logger;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pace evaluation attachment utilities.
|
|
3
|
+
*
|
|
4
|
+
* Attaches pace evaluation data to agent usage metrics.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { evaluatePace } = require('./pace-evaluator.js');
|
|
8
|
+
const { CYCLE_DURATIONS } = require('./usage-formatters.js');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Attach pace evaluation to a usage data object if it has required fields.
|
|
12
|
+
* @param {Object} data - Usage data object with percentUsed and resetsInSeconds
|
|
13
|
+
* @param {string} percentField - Field name for percent value
|
|
14
|
+
* @param {string} cycle - Cycle type for duration lookup
|
|
15
|
+
*/
|
|
16
|
+
function attachPaceToData(data, percentField, cycle) {
|
|
17
|
+
if (!data) return;
|
|
18
|
+
const percent = data[percentField];
|
|
19
|
+
if (typeof percent !== 'number' || typeof data.resetsInSeconds !== 'number') return;
|
|
20
|
+
|
|
21
|
+
const paceEval = evaluatePace({
|
|
22
|
+
usagePercent: percent,
|
|
23
|
+
resetsInSeconds: data.resetsInSeconds,
|
|
24
|
+
cycleDurationSeconds: CYCLE_DURATIONS[cycle]
|
|
25
|
+
});
|
|
26
|
+
if (paceEval) {
|
|
27
|
+
data.paceEval = paceEval;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Attach pace evaluation to Claude usage metrics.
|
|
33
|
+
* @param {Object} usage - Claude usage data
|
|
34
|
+
*/
|
|
35
|
+
function attachClaudePace(usage) {
|
|
36
|
+
const sections = [
|
|
37
|
+
{ data: usage.session, cycle: 'session' },
|
|
38
|
+
{ data: usage.weeklyAll, cycle: 'weekly' },
|
|
39
|
+
{ data: usage.weeklySonnet, cycle: 'weekly' },
|
|
40
|
+
{ data: usage.weekly, cycle: 'weekly' },
|
|
41
|
+
{ data: usage.extraUsage, cycle: 'weekly' }
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
for (const { data, cycle } of sections) {
|
|
45
|
+
attachPaceToData(data, 'percent', cycle);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Attach pace evaluation to Gemini usage metrics.
|
|
51
|
+
* @param {Object} usage - Gemini usage data
|
|
52
|
+
*/
|
|
53
|
+
function attachGeminiPace(usage) {
|
|
54
|
+
if (!usage.models || !Array.isArray(usage.models)) return;
|
|
55
|
+
|
|
56
|
+
for (const model of usage.models) {
|
|
57
|
+
attachPaceToData(model, 'percentUsed', 'sessionGemini');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Attach pace evaluation to Codex usage metrics.
|
|
63
|
+
* @param {Object} usage - Codex usage data
|
|
64
|
+
*/
|
|
65
|
+
function attachCodexPace(usage) {
|
|
66
|
+
// Main limits
|
|
67
|
+
attachPaceToData(usage.fiveHour, 'percentUsed', 'fiveHour');
|
|
68
|
+
attachPaceToData(usage.weekly, 'percentUsed', 'weekly');
|
|
69
|
+
|
|
70
|
+
// Per-model limits
|
|
71
|
+
if (usage.modelLimits && Array.isArray(usage.modelLimits)) {
|
|
72
|
+
for (const ml of usage.modelLimits) {
|
|
73
|
+
attachPaceToData(ml.fiveHour, 'percentUsed', 'fiveHour');
|
|
74
|
+
attachPaceToData(ml.weekly, 'percentUsed', 'weekly');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Attach pace evaluation data to usage metrics based on agent type.
|
|
81
|
+
* @param {string} agentName - Name of the agent
|
|
82
|
+
* @param {Object} usage - Usage data object
|
|
83
|
+
*/
|
|
84
|
+
function attachPaceEvaluation(agentName, usage) {
|
|
85
|
+
switch (agentName) {
|
|
86
|
+
case 'claude':
|
|
87
|
+
attachClaudePace(usage);
|
|
88
|
+
break;
|
|
89
|
+
case 'gemini':
|
|
90
|
+
attachGeminiPace(usage);
|
|
91
|
+
break;
|
|
92
|
+
case 'codex':
|
|
93
|
+
attachCodexPace(usage);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
attachPaceEvaluation,
|
|
100
|
+
attachClaudePace,
|
|
101
|
+
attachGeminiPace,
|
|
102
|
+
attachCodexPace,
|
|
103
|
+
attachPaceToData
|
|
104
|
+
};
|