agent-tank 0.9.5 → 0.9.7
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/README.md +25 -12
- package/bin/agent-tank.js +33 -15
- package/package.json +5 -3
- package/src/activity-monitor.js +18 -7
- package/src/agents/agy.js +301 -0
- package/src/agents/base.js +18 -1
- package/src/agents/claude.js +39 -10
- package/src/agents/pty-output-parser.js +10 -4
- package/src/client-auto-refresh-helpers.js +7 -7
- package/src/client-auto-refresh.js +2 -2
- package/src/client-monitor.js +2 -2
- package/src/discovery.js +9 -24
- package/src/history-store.js +1 -1
- package/src/icons.js +2 -2
- package/src/index.js +5 -5
- package/src/logger.js +2 -2
- package/src/pace-attachment.js +7 -7
- package/src/public-status.js +5 -5
- package/src/snapshot-metrics.js +6 -6
- package/src/status-page.css +6 -6
- package/src/status-page.js +10 -2
- package/src/usage-formatters.js +7 -8
- package/src/agents/gemini.js +0 -412
package/src/agents/claude.js
CHANGED
|
@@ -209,20 +209,28 @@ class ClaudeAgent extends BaseAgent {
|
|
|
209
209
|
const hasSessionData = parsed.session && typeof parsed.session.percent === 'number';
|
|
210
210
|
const hasLegacyWeekly = parsed.weekly && typeof parsed.weekly.percent === 'number';
|
|
211
211
|
const hasAllModelsWeekly = parsed.weeklyAll && typeof parsed.weeklyAll.percent === 'number';
|
|
212
|
+
const hasSonnetWeekly = parsed.weeklySonnet && typeof parsed.weeklySonnet.percent === 'number';
|
|
212
213
|
|
|
213
214
|
// Newer Claude builds often emit usable session/weekly data before the UI fully settles.
|
|
214
|
-
//
|
|
215
|
-
return hasSessionData && (hasLegacyWeekly || hasAllModelsWeekly);
|
|
215
|
+
// For the all-model weekly format, wait for the separate Sonnet section too.
|
|
216
|
+
return Boolean(hasSessionData && (hasLegacyWeekly || (hasAllModelsWeekly && hasSonnetWeekly)));
|
|
216
217
|
}
|
|
217
218
|
|
|
218
219
|
sendCommands(shell, _output) {
|
|
219
220
|
logger.agent(this.name, 'Sending /usage command...');
|
|
220
221
|
// Claude keeps slash-command suggestions open for /usage on newer builds.
|
|
221
222
|
// Confirm the command selection, then submit the actual command execution.
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
223
|
+
const writeIfActive = (value) => {
|
|
224
|
+
if (!shell) return;
|
|
225
|
+
try {
|
|
226
|
+
shell.write(value);
|
|
227
|
+
} catch (_err) {
|
|
228
|
+
// The persistent exit handler records the underlying process state.
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
setTimeout(() => writeIfActive('/usage'), 100);
|
|
232
|
+
setTimeout(() => writeIfActive('\r'), 500);
|
|
233
|
+
setTimeout(() => writeIfActive('\r'), 900);
|
|
226
234
|
}
|
|
227
235
|
|
|
228
236
|
// After getting /usage output, dismiss dialog so next refresh starts with clean prompt
|
|
@@ -286,6 +294,18 @@ class ClaudeAgent extends BaseAgent {
|
|
|
286
294
|
this._onDataCallback = null;
|
|
287
295
|
this._commandInFlight = false;
|
|
288
296
|
clearTimeout(timer);
|
|
297
|
+
if (this.shell && statusOutput) {
|
|
298
|
+
try {
|
|
299
|
+
this.shell.write('\x1b');
|
|
300
|
+
} catch (_err) {
|
|
301
|
+
// The persistent exit handler records the underlying process state.
|
|
302
|
+
}
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
this.output = '';
|
|
305
|
+
resolve(result);
|
|
306
|
+
}, 300);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
289
309
|
resolve(result);
|
|
290
310
|
};
|
|
291
311
|
|
|
@@ -306,10 +326,19 @@ class ClaudeAgent extends BaseAgent {
|
|
|
306
326
|
|
|
307
327
|
logger.agent(this.name, 'Sending /status command for metadata...');
|
|
308
328
|
this.output = '';
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
329
|
+
const writeIfActive = (value) => {
|
|
330
|
+
if (!this.shell) {
|
|
331
|
+
finish(this._parseStatusOutput(statusOutput));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
this.shell.write(value);
|
|
336
|
+
} catch (_err) {
|
|
337
|
+
finish(this._parseStatusOutput(statusOutput));
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
setTimeout(() => writeIfActive('/status'), 100);
|
|
341
|
+
setTimeout(() => writeIfActive('\r'), 500);
|
|
313
342
|
});
|
|
314
343
|
}
|
|
315
344
|
|
|
@@ -125,6 +125,12 @@ function extractResetTime(section) {
|
|
|
125
125
|
// Look for date+time pattern FIRST (more specific)
|
|
126
126
|
const dateTimeMatch = section.match(/((?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2},?\s*\d{1,2}(?::\d{2})?\s*(am|pm))\s*\(([^)]+)\)/i);
|
|
127
127
|
if (dateTimeMatch) return `${dateTimeMatch[1]} (${dateTimeMatch[3]})`;
|
|
128
|
+
// Compact PTY output can lose the column spaces: "Jun10,6pm(Europe/Berlin)".
|
|
129
|
+
const compactDateTimeMatch = section.match(/((?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec))(\d{1,2}),?(\d{1,2})(?::(\d{2}))?\s*(am|pm)\s*\(([^)]+)\)/i);
|
|
130
|
+
if (compactDateTimeMatch) {
|
|
131
|
+
const [, month, day, hour, minutes, ampm, tz] = compactDateTimeMatch;
|
|
132
|
+
return `${month} ${day}, ${hour}${minutes ? `:${minutes}` : ''}${ampm} (${tz})`;
|
|
133
|
+
}
|
|
128
134
|
// Try clean time-only pattern
|
|
129
135
|
const timeOnlyMatch = section.match(/(\d{1,2}(?::\d{2})?\s*(?:am|pm))\s*\(([^)]+)\)/i);
|
|
130
136
|
if (timeOnlyMatch) return `${timeOnlyMatch[1]} (${timeOnlyMatch[2]})`;
|
|
@@ -149,7 +155,7 @@ function extractResetTime(section) {
|
|
|
149
155
|
*/
|
|
150
156
|
function parseUsageSection(section) {
|
|
151
157
|
if (!section) return null;
|
|
152
|
-
const percentMatch = section.match(/(\d+)\s*%\s*
|
|
158
|
+
const percentMatch = section.match(/(\d+)\s*%\s*use?d/i);
|
|
153
159
|
if (!percentMatch) return null;
|
|
154
160
|
const resetsAt = extractResetTime(section);
|
|
155
161
|
const resetData = resetsAt ? parseResetTime(resetsAt) : null;
|
|
@@ -245,21 +251,21 @@ function parsePtyOutput(clean) {
|
|
|
245
251
|
const usage = { session: null, weeklyAll: null, weeklySonnet: null };
|
|
246
252
|
|
|
247
253
|
// Parse session
|
|
248
|
-
const sessionData = parseUsageSection(extractSection(clean, 'Current\\s
|
|
254
|
+
const sessionData = parseUsageSection(extractSection(clean, 'Current\\s*session', 'Current\\s*week'));
|
|
249
255
|
if (sessionData) {
|
|
250
256
|
usage.session = { label: 'Current session', ...sessionData };
|
|
251
257
|
addPaceData(usage.session, 'session');
|
|
252
258
|
}
|
|
253
259
|
|
|
254
260
|
// Parse weekly (all models)
|
|
255
|
-
const weeklyAllData = parseUsageSection(extractSection(clean, 'Current\\s
|
|
261
|
+
const weeklyAllData = parseUsageSection(extractSection(clean, 'Current\\s*week\\s*\\(?\\s*all\\s*models\\s*\\)?', 'Current\\s*week\\s*\\(?\\s*Son'));
|
|
256
262
|
if (weeklyAllData) {
|
|
257
263
|
usage.weeklyAll = { label: 'Current week (all models)', ...weeklyAllData };
|
|
258
264
|
addPaceData(usage.weeklyAll, 'weekly');
|
|
259
265
|
}
|
|
260
266
|
|
|
261
267
|
// Parse weekly (Sonnet only)
|
|
262
|
-
const weeklySonnetData = parseUsageSection(extractSection(clean, 'Current\\s
|
|
268
|
+
const weeklySonnetData = parseUsageSection(extractSection(clean, 'Current\\s*week\\s*\\(?\\s*Son\\w*\\s*\\w*ly\\s*\\)?', 'Extra\\s*usage|esc\\s*to\\s*cancel|Current\\s*week\\s*\\('));
|
|
263
269
|
if (weeklySonnetData) {
|
|
264
270
|
usage.weeklySonnet = { label: 'Current week (Sonnet only)', ...weeklySonnetData };
|
|
265
271
|
addPaceData(usage.weeklySonnet, 'weekly');
|
|
@@ -41,7 +41,7 @@ const timeHelpers = `
|
|
|
41
41
|
// Cycle duration constants (mirrored from server-side)
|
|
42
42
|
const CYCLE_DURATIONS = {
|
|
43
43
|
session: 5 * 60 * 60,
|
|
44
|
-
|
|
44
|
+
sessionAgy: 24 * 60 * 60,
|
|
45
45
|
weekly: 7 * 24 * 60 * 60,
|
|
46
46
|
fiveHour: 5 * 60 * 60
|
|
47
47
|
};
|
|
@@ -74,21 +74,21 @@ const metricExtractors = `
|
|
|
74
74
|
return metrics;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
// Extract metrics from
|
|
78
|
-
function
|
|
77
|
+
// Extract metrics from Antigravity usage data
|
|
78
|
+
function extractAgyMetrics(usage) {
|
|
79
79
|
const metrics = [];
|
|
80
80
|
if (usage.models && usage.models.length > 0) {
|
|
81
81
|
for (const model of usage.models) {
|
|
82
|
-
const modelName = model.model
|
|
83
|
-
const metricId = \`
|
|
82
|
+
const modelName = model.model;
|
|
83
|
+
const metricId = \`agy-\${modelName.toLowerCase().replace(/[^a-z0-9]/g, '-')}\`;
|
|
84
84
|
metrics.push({
|
|
85
85
|
metricId,
|
|
86
|
-
agent: '
|
|
86
|
+
agent: 'agy',
|
|
87
87
|
label: modelName,
|
|
88
88
|
percent: model.percentUsed ?? 0,
|
|
89
89
|
resetsIn: model.resetsIn || '',
|
|
90
90
|
resetsAt: model.resetsAt || '',
|
|
91
|
-
cycle: '
|
|
91
|
+
cycle: 'sessionAgy'
|
|
92
92
|
});
|
|
93
93
|
}
|
|
94
94
|
}
|
|
@@ -142,8 +142,8 @@ ${metricExtractors}
|
|
|
142
142
|
let metrics = [];
|
|
143
143
|
if (agentName === 'claude') {
|
|
144
144
|
metrics = extractClaudeMetrics(agentData.usage);
|
|
145
|
-
} else if (agentName === '
|
|
146
|
-
metrics =
|
|
145
|
+
} else if (agentName === 'agy') {
|
|
146
|
+
metrics = extractAgyMetrics(agentData.usage);
|
|
147
147
|
} else if (agentName === 'codex') {
|
|
148
148
|
metrics = extractCodexMetrics(agentData.usage);
|
|
149
149
|
}
|
package/src/client-monitor.js
CHANGED
|
@@ -183,7 +183,7 @@ const monitorScript = `
|
|
|
183
183
|
const { percent, color, agent, label } = trackedMetric;
|
|
184
184
|
|
|
185
185
|
// Update page title: XX% Agent | Agent Tank (clean format for squeezed tabs)
|
|
186
|
-
const agentName = agent.charAt(0).toUpperCase() + agent.slice(1);
|
|
186
|
+
const agentName = agent === 'agy' ? 'Antigravity' : agent.charAt(0).toUpperCase() + agent.slice(1);
|
|
187
187
|
document.title = \`\${percent}% \${agentName} | Agent Tank\`;
|
|
188
188
|
|
|
189
189
|
// Generate favicon using Canvas API
|
|
@@ -204,7 +204,7 @@ const monitorScript = `
|
|
|
204
204
|
|
|
205
205
|
// Send browser notification
|
|
206
206
|
if ('Notification' in window && Notification.permission === 'granted') {
|
|
207
|
-
const agentName = agent.charAt(0).toUpperCase() + agent.slice(1);
|
|
207
|
+
const agentName = agent === 'agy' ? 'Antigravity' : agent.charAt(0).toUpperCase() + agent.slice(1);
|
|
208
208
|
new Notification('Agent Tank Warning', {
|
|
209
209
|
body: \`\${agentName} \${label} is at \${percent}% usage!\`,
|
|
210
210
|
icon: '/favicon.ico',
|
package/src/discovery.js
CHANGED
|
@@ -22,22 +22,15 @@ async function discoverAgents() {
|
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
// Check for
|
|
26
|
-
if (commandExists('
|
|
27
|
-
const version =
|
|
25
|
+
// Check for Antigravity CLI
|
|
26
|
+
if (commandExists('agy')) {
|
|
27
|
+
const version = getAgyVersion();
|
|
28
28
|
if (version) {
|
|
29
|
-
|
|
30
|
-
logger.warn(`Gemini CLI version ${version.full} detected. Version 0.24.5+ required for /stats command.`);
|
|
31
|
-
logger.warn('Please update: npm update -g gemini');
|
|
32
|
-
} else {
|
|
33
|
-
logger.success(`✅ Gemini CLI version ${version.full} detected`);
|
|
34
|
-
found.push('gemini');
|
|
35
|
-
}
|
|
29
|
+
logger.success(`✅ Antigravity CLI version ${version.full} detected`);
|
|
36
30
|
} else {
|
|
37
|
-
|
|
38
|
-
logger.warn('Gemini found but version unknown. Version 0.24.5+ required for /stats command.');
|
|
39
|
-
found.push('gemini');
|
|
31
|
+
logger.success('✅ Antigravity CLI detected (version unknown)');
|
|
40
32
|
}
|
|
33
|
+
found.push('agy');
|
|
41
34
|
}
|
|
42
35
|
|
|
43
36
|
// Check for Codex with version info
|
|
@@ -81,9 +74,9 @@ function getClaudeVersion() {
|
|
|
81
74
|
return null;
|
|
82
75
|
}
|
|
83
76
|
|
|
84
|
-
function
|
|
77
|
+
function getAgyVersion() {
|
|
85
78
|
try {
|
|
86
|
-
const output = execSync('
|
|
79
|
+
const output = execSync('agy --version 2>&1', { encoding: 'utf-8' });
|
|
87
80
|
const match = output.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
88
81
|
if (match) {
|
|
89
82
|
return {
|
|
@@ -117,12 +110,4 @@ function getCodexVersion() {
|
|
|
117
110
|
return null;
|
|
118
111
|
}
|
|
119
112
|
|
|
120
|
-
|
|
121
|
-
if (version.major > minMajor) return true;
|
|
122
|
-
if (version.major < minMajor) return false;
|
|
123
|
-
if (version.minor > minMinor) return true;
|
|
124
|
-
if (version.minor < minMinor) return false;
|
|
125
|
-
return version.patch >= minPatch;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
module.exports = { discoverAgents };
|
|
113
|
+
module.exports = { discoverAgents };
|
package/src/history-store.js
CHANGED
|
@@ -130,7 +130,7 @@ class HistoryStore {
|
|
|
130
130
|
/**
|
|
131
131
|
* Add a usage snapshot to history.
|
|
132
132
|
*
|
|
133
|
-
* @param {string} agentName - Name of the agent (e.g., 'claude', '
|
|
133
|
+
* @param {string} agentName - Name of the agent (e.g., 'claude', 'agy', 'codex')
|
|
134
134
|
* @param {Object} usageData - The usage data to store
|
|
135
135
|
* @param {string} [timestamp] - ISO timestamp (defaults to current time)
|
|
136
136
|
* @returns {Object} The stored record
|
package/src/icons.js
CHANGED
|
@@ -12,8 +12,8 @@ const agentIcons = {
|
|
|
12
12
|
claude: `<svg class="agent-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
|
13
13
|
<path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z" />
|
|
14
14
|
</svg>`,
|
|
15
|
-
|
|
16
|
-
<path d="
|
|
15
|
+
agy: `<svg class="agent-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" aria-label="Antigravity">
|
|
16
|
+
<path d="M21.751 22.607c1.34 1.005 3.35.335 1.508-1.508C17.73 15.74 18.904 1 12.037 1 5.17 1 6.342 15.74.815 21.1c-2.01 2.009.167 2.511 1.507 1.506 5.192-3.517 4.857-9.714 9.715-9.714 4.857 0 4.522 6.197 9.714 9.715z" />
|
|
17
17
|
</svg>`,
|
|
18
18
|
codex: `<svg class="agent-icon" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
|
19
19
|
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
|
package/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@ const path = require('node:path');
|
|
|
2
2
|
const os = require('node:os');
|
|
3
3
|
const { execFileSync } = require('node:child_process');
|
|
4
4
|
const { ClaudeAgent } = require('./agents/claude.js');
|
|
5
|
-
const {
|
|
5
|
+
const { AgyAgent } = require('./agents/agy.js');
|
|
6
6
|
const { CodexAgent } = require('./agents/codex.js');
|
|
7
7
|
const { discoverAgents } = require('./discovery.js');
|
|
8
8
|
const { fetchPublicStatus } = require('./public-status.js');
|
|
@@ -235,11 +235,11 @@ class AgentTank {
|
|
|
235
235
|
logger.warn('No compatible LLM agents found.');
|
|
236
236
|
logger.info('Requirements:');
|
|
237
237
|
logger.info(' - Claude Code 2.0+ (for /usage command support)');
|
|
238
|
-
logger.info(' -
|
|
238
|
+
logger.info(' - Antigravity CLI as agy (for /usage command support)');
|
|
239
239
|
logger.info(' - Codex CLI');
|
|
240
240
|
logger.info('\nInstall/update with:');
|
|
241
241
|
logger.info(' npm install -g @anthropic-ai/claude-code@latest');
|
|
242
|
-
logger.info('
|
|
242
|
+
logger.info(' Install Antigravity CLI from https://antigravity.google/docs/cli-getting-started');
|
|
243
243
|
throw new Error('No agents found');
|
|
244
244
|
}
|
|
245
245
|
logger.success(`✅ Found agents: ${agentNames.join(', ')}`);
|
|
@@ -326,8 +326,8 @@ class AgentTank {
|
|
|
326
326
|
switch (name) {
|
|
327
327
|
case 'claude':
|
|
328
328
|
return new ClaudeAgent({ useApi: this.claudeApi });
|
|
329
|
-
case '
|
|
330
|
-
return new
|
|
329
|
+
case 'agy':
|
|
330
|
+
return new AgyAgent();
|
|
331
331
|
case 'codex':
|
|
332
332
|
return new CodexAgent();
|
|
333
333
|
default:
|
package/src/logger.js
CHANGED
|
@@ -39,7 +39,7 @@ const ANSI = {
|
|
|
39
39
|
// Agent color mapping
|
|
40
40
|
const AGENT_COLORS = {
|
|
41
41
|
claude: ANSI.magenta,
|
|
42
|
-
|
|
42
|
+
agy: ANSI.cyan,
|
|
43
43
|
codex: ANSI.green,
|
|
44
44
|
};
|
|
45
45
|
|
|
@@ -139,7 +139,7 @@ const logger = {
|
|
|
139
139
|
|
|
140
140
|
/**
|
|
141
141
|
* Log an agent-specific message with mapped color
|
|
142
|
-
* @param {string} agentName - Name of the agent (claude,
|
|
142
|
+
* @param {string} agentName - Name of the agent (claude, agy, codex)
|
|
143
143
|
* @param {...any} args - Message arguments
|
|
144
144
|
*/
|
|
145
145
|
agent(agentName, ...args) {
|
package/src/pace-attachment.js
CHANGED
|
@@ -47,14 +47,14 @@ function attachClaudePace(usage) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
/**
|
|
50
|
-
* Attach pace evaluation to
|
|
51
|
-
* @param {Object} usage -
|
|
50
|
+
* Attach pace evaluation to Antigravity usage metrics.
|
|
51
|
+
* @param {Object} usage - Antigravity usage data
|
|
52
52
|
*/
|
|
53
|
-
function
|
|
53
|
+
function attachAgyPace(usage) {
|
|
54
54
|
if (!usage.models || !Array.isArray(usage.models)) return;
|
|
55
55
|
|
|
56
56
|
for (const model of usage.models) {
|
|
57
|
-
attachPaceToData(model, 'percentUsed', '
|
|
57
|
+
attachPaceToData(model, 'percentUsed', 'sessionAgy');
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -86,8 +86,8 @@ function attachPaceEvaluation(agentName, usage) {
|
|
|
86
86
|
case 'claude':
|
|
87
87
|
attachClaudePace(usage);
|
|
88
88
|
break;
|
|
89
|
-
case '
|
|
90
|
-
|
|
89
|
+
case 'agy':
|
|
90
|
+
attachAgyPace(usage);
|
|
91
91
|
break;
|
|
92
92
|
case 'codex':
|
|
93
93
|
attachCodexPace(usage);
|
|
@@ -98,7 +98,7 @@ function attachPaceEvaluation(agentName, usage) {
|
|
|
98
98
|
module.exports = {
|
|
99
99
|
attachPaceEvaluation,
|
|
100
100
|
attachClaudePace,
|
|
101
|
-
|
|
101
|
+
attachAgyPace,
|
|
102
102
|
attachCodexPace,
|
|
103
103
|
attachPaceToData
|
|
104
104
|
};
|
package/src/public-status.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Fetches status information from public API status pages for Anthropic, OpenAI,
|
|
5
5
|
* and Google Cloud. Uses Statuspage.io JSON API for Anthropic and OpenAI, and
|
|
6
|
-
* Google Cloud's incidents JSON for Google
|
|
6
|
+
* Google Cloud's incidents JSON for Antigravity's Google-backed models.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
const https = require('node:https');
|
|
@@ -20,11 +20,11 @@ const STATUS_PAGES = {
|
|
|
20
20
|
type: 'statuspage',
|
|
21
21
|
name: 'OpenAI',
|
|
22
22
|
},
|
|
23
|
-
|
|
23
|
+
agy: {
|
|
24
24
|
url: 'https://status.cloud.google.com/incidents.json',
|
|
25
25
|
type: 'google-cloud',
|
|
26
26
|
name: 'Google Cloud',
|
|
27
|
-
// Vertex AI Gemini product ID for filtering incidents
|
|
27
|
+
// Vertex AI Gemini product ID for filtering incidents that may affect Antigravity.
|
|
28
28
|
productId: 'Z0FZJAMvEB4j3NbCJs6B',
|
|
29
29
|
},
|
|
30
30
|
};
|
|
@@ -99,7 +99,7 @@ function parseStatuspageResponse(json) {
|
|
|
99
99
|
function parseGoogleCloudResponse(json, productId) {
|
|
100
100
|
const incidents = JSON.parse(json);
|
|
101
101
|
|
|
102
|
-
// Find active incidents affecting the specified product
|
|
102
|
+
// Find active incidents affecting the specified product.
|
|
103
103
|
const now = new Date();
|
|
104
104
|
const activeIncidents = incidents.filter((incident) => {
|
|
105
105
|
// Check if incident is ongoing (no end date or recent)
|
|
@@ -171,7 +171,7 @@ function parseGoogleCloudResponse(json, productId) {
|
|
|
171
171
|
|
|
172
172
|
/**
|
|
173
173
|
* Fetch public status for a single agent
|
|
174
|
-
* @param {string} agentName - Name of the agent (claude,
|
|
174
|
+
* @param {string} agentName - Name of the agent (claude, agy, codex)
|
|
175
175
|
* @returns {Promise<object>} - Status object with status, indicator, description
|
|
176
176
|
*/
|
|
177
177
|
async function fetchAgentPublicStatus(agentName) {
|
package/src/snapshot-metrics.js
CHANGED
|
@@ -20,11 +20,11 @@ function extractClaudeMetrics(usage) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Extract
|
|
24
|
-
* @param {Object} usage -
|
|
23
|
+
* Extract Antigravity metrics from usage data.
|
|
24
|
+
* @param {Object} usage - Antigravity usage data
|
|
25
25
|
* @returns {Object|null} Extracted metrics or null
|
|
26
26
|
*/
|
|
27
|
-
function
|
|
27
|
+
function extractAgyMetrics(usage) {
|
|
28
28
|
if (!usage.models || !Array.isArray(usage.models)) {
|
|
29
29
|
return null;
|
|
30
30
|
}
|
|
@@ -62,8 +62,8 @@ function extractSnapshotMetrics(agentName, usage) {
|
|
|
62
62
|
switch (agentName) {
|
|
63
63
|
case 'claude':
|
|
64
64
|
return extractClaudeMetrics(usage);
|
|
65
|
-
case '
|
|
66
|
-
return
|
|
65
|
+
case 'agy':
|
|
66
|
+
return extractAgyMetrics(usage);
|
|
67
67
|
case 'codex':
|
|
68
68
|
return extractCodexMetrics(usage);
|
|
69
69
|
default:
|
|
@@ -74,6 +74,6 @@ function extractSnapshotMetrics(agentName, usage) {
|
|
|
74
74
|
module.exports = {
|
|
75
75
|
extractSnapshotMetrics,
|
|
76
76
|
extractClaudeMetrics,
|
|
77
|
-
|
|
77
|
+
extractAgyMetrics,
|
|
78
78
|
extractCodexMetrics
|
|
79
79
|
};
|
package/src/status-page.css
CHANGED
|
@@ -671,20 +671,20 @@ body.light-mode .status-badge-grey {
|
|
|
671
671
|
/* Agent icons - neutral low-opacity white/grey for industrial look
|
|
672
672
|
Status dots (LEDs) are now the primary color indicators */
|
|
673
673
|
.agent-claude,
|
|
674
|
-
.agent-
|
|
674
|
+
.agent-agy,
|
|
675
675
|
.agent-codex {
|
|
676
676
|
--agent-bg-subtle: rgba(255, 255, 255, 0.04);
|
|
677
677
|
}
|
|
678
678
|
|
|
679
679
|
.agent-claude .agent-icon,
|
|
680
|
-
.agent-
|
|
680
|
+
.agent-agy .agent-icon,
|
|
681
681
|
.agent-codex .agent-icon {
|
|
682
682
|
color: var(--text-tertiary);
|
|
683
683
|
opacity: 0.7;
|
|
684
684
|
}
|
|
685
685
|
|
|
686
686
|
body.light-mode .agent-claude .agent-icon,
|
|
687
|
-
body.light-mode .agent-
|
|
687
|
+
body.light-mode .agent-agy .agent-icon,
|
|
688
688
|
body.light-mode .agent-codex .agent-icon {
|
|
689
689
|
color: var(--text-secondary);
|
|
690
690
|
opacity: 0.6;
|
|
@@ -692,21 +692,21 @@ body.light-mode .agent-codex .agent-icon {
|
|
|
692
692
|
|
|
693
693
|
/* Agent cards have simple borders - brand colors only on icons */
|
|
694
694
|
.agent-claude,
|
|
695
|
-
.agent-
|
|
695
|
+
.agent-agy,
|
|
696
696
|
.agent-codex {
|
|
697
697
|
background: var(--bg-card);
|
|
698
698
|
}
|
|
699
699
|
|
|
700
700
|
/* Light mode adjustments for subtle backgrounds */
|
|
701
701
|
body.light-mode .agent-claude,
|
|
702
|
-
body.light-mode .agent-
|
|
702
|
+
body.light-mode .agent-agy,
|
|
703
703
|
body.light-mode .agent-codex {
|
|
704
704
|
--agent-bg-subtle: rgba(0, 0, 0, 0.02);
|
|
705
705
|
}
|
|
706
706
|
|
|
707
707
|
/* Agent headings use neutral color, only icons have brand colors */
|
|
708
708
|
.agent-claude h2,
|
|
709
|
-
.agent-
|
|
709
|
+
.agent-agy h2,
|
|
710
710
|
.agent-codex h2 {
|
|
711
711
|
color: var(--text-primary);
|
|
712
712
|
}
|
package/src/status-page.js
CHANGED
|
@@ -8,6 +8,14 @@ const { clientScript } = require('./client-script');
|
|
|
8
8
|
const { getStatusBadgeClass, getStatusText } = require('./public-status');
|
|
9
9
|
|
|
10
10
|
const styles = fs.readFileSync(path.join(__dirname, 'status-page.css'), 'utf8');
|
|
11
|
+
const AGENT_DISPLAY_NAMES = {
|
|
12
|
+
agy: 'Antigravity',
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
function getAgentDisplayName(name) {
|
|
16
|
+
if (AGENT_DISPLAY_NAMES[name]) return AGENT_DISPLAY_NAMES[name];
|
|
17
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
18
|
+
}
|
|
11
19
|
|
|
12
20
|
function escapeHtml(value) {
|
|
13
21
|
return String(value)
|
|
@@ -39,7 +47,7 @@ function statusPage(status) {
|
|
|
39
47
|
const statusClass = data.error ? 'error' : data.isRefreshing ? 'refreshing' : 'ok';
|
|
40
48
|
|
|
41
49
|
const icon = agentIcons[name] || '';
|
|
42
|
-
const displayName =
|
|
50
|
+
const displayName = getAgentDisplayName(name);
|
|
43
51
|
|
|
44
52
|
// Public API status badge
|
|
45
53
|
const publicStatus = data.publicStatus;
|
|
@@ -48,7 +56,7 @@ function statusPage(status) {
|
|
|
48
56
|
const badgeTitle = escapeHtml(publicStatus?.description || 'Unable to fetch status');
|
|
49
57
|
const statusBadgeHtml = `<span class="status-badge ${badgeClass}" title="${badgeTitle}">${badgeText}</span>`;
|
|
50
58
|
|
|
51
|
-
// Version update notice - check usage.version
|
|
59
|
+
// Version update notice - check usage.version or metadata.updateAvailable.
|
|
52
60
|
const version = data.usage?.version || data.metadata?.updateAvailable;
|
|
53
61
|
const updateHtml = (version && version.current && version.latest && version.current !== version.latest)
|
|
54
62
|
? `<div class="version-update-notice">Update available: v${version.current} → v${version.latest}</div>`
|
package/src/usage-formatters.js
CHANGED
|
@@ -4,7 +4,7 @@ const { formatPaceWarning } = require('./pace-evaluator');
|
|
|
4
4
|
// Cycle duration constants in seconds
|
|
5
5
|
const CYCLE_DURATIONS = {
|
|
6
6
|
session: 5 * 60 * 60, // 5 hours = 18000 seconds
|
|
7
|
-
|
|
7
|
+
sessionAgy: 24 * 60 * 60, // 24 hours = 86400 seconds
|
|
8
8
|
weekly: 7 * 24 * 60 * 60, // 7 days = 604800 seconds
|
|
9
9
|
fiveHour: 5 * 60 * 60 // 5 hours = 18000 seconds
|
|
10
10
|
};
|
|
@@ -169,22 +169,21 @@ function formatClaudeUsage(usage) {
|
|
|
169
169
|
return html;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
-
function
|
|
172
|
+
function formatAgyUsage(usage) {
|
|
173
173
|
let html = '';
|
|
174
174
|
if (usage.models && usage.models.length > 0) {
|
|
175
175
|
for (const model of usage.models) {
|
|
176
176
|
const percent = model.percentUsed ?? 0;
|
|
177
177
|
const isZero = percent === 0;
|
|
178
|
-
|
|
179
|
-
const modelName = model.model.toLowerCase();
|
|
178
|
+
const modelName = model.model;
|
|
180
179
|
const resetsIn = model.resetsIn || '';
|
|
181
180
|
|
|
182
181
|
// Use pre-calculated pace data from the usage object
|
|
183
182
|
const paceData = model.pace || null;
|
|
184
183
|
|
|
185
184
|
html += '<div class="model-container">';
|
|
186
|
-
html += usageItem(modelName, percent, '% used', { isZero, isModelName: true, agentName: '
|
|
187
|
-
html += resetInfoItem(model.resetsIn, null, '
|
|
185
|
+
html += usageItem(modelName, percent, '% used', { isZero, isModelName: true, agentName: 'agy', resetsIn });
|
|
186
|
+
html += resetInfoItem(model.resetsIn, null, 'sessionAgy', { isZero, paceData });
|
|
188
187
|
html += '</div>';
|
|
189
188
|
}
|
|
190
189
|
}
|
|
@@ -240,7 +239,7 @@ function formatUsage(agentName, usage) {
|
|
|
240
239
|
return '<p class="usage-item">No data available</p>';
|
|
241
240
|
}
|
|
242
241
|
|
|
243
|
-
const formatters = { claude: formatClaudeUsage,
|
|
242
|
+
const formatters = { claude: formatClaudeUsage, agy: formatAgyUsage, codex: formatCodexUsage };
|
|
244
243
|
const formatter = formatters[agentName];
|
|
245
244
|
const html = formatter ? formatter(usage) : '';
|
|
246
245
|
|
|
@@ -250,7 +249,7 @@ function formatUsage(agentName, usage) {
|
|
|
250
249
|
module.exports = {
|
|
251
250
|
formatUsage,
|
|
252
251
|
formatClaudeUsage,
|
|
253
|
-
|
|
252
|
+
formatAgyUsage,
|
|
254
253
|
formatCodexUsage,
|
|
255
254
|
usageItem,
|
|
256
255
|
resetInfoItem,
|