Auto-Commit: 2026-08-30 00:51:24
Build and Push Docker Image / build-and-push (push) Canceled after 0s
Build and Push Docker Image / build-and-push (push) Canceled after 0s
This commit is contained in:
1 parent
8c8a5eaa9e
commit
e4b0dffb51
3 files changed
+326
No files matched your search
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"response": {
|
||||
"groups": [
|
||||
{
|
||||
"displayName": "Gemini Models",
|
||||
"description": "Models within this group: Gemini Flash, Gemini Pro",
|
||||
"buckets": [
|
||||
{
|
||||
"bucketId": "gemini-weekly",
|
||||
"displayName": "Weekly Limit Remaining",
|
||||
"description": "You have used some of your weekly limit, it will fully refresh in 6 days, 10 hours.",
|
||||
"window": "weekly",
|
||||
"remainingFraction": 0.9899833,
|
||||
"resetTime": "2026-09-05T09:20:06Z"
|
||||
},
|
||||
{
|
||||
"bucketId": "gemini-5h",
|
||||
"displayName": "Five Hour Limit Remaining",
|
||||
"description": "You have used some of your 5-hour limit, it will fully refresh in 4 hours, 54 minutes.",
|
||||
"window": "5h",
|
||||
"remainingFraction": 0.9699,
|
||||
"resetTime": "2026-08-30T03:16:17Z"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"displayName": "Claude and GPT models",
|
||||
"description": "Models within this group: Claude Opus, Claude Sonnet, GPT-OSS",
|
||||
"buckets": [
|
||||
{
|
||||
"bucketId": "3p-weekly",
|
||||
"displayName": "Weekly Limit Remaining",
|
||||
"window": "weekly",
|
||||
"remainingFraction": 1,
|
||||
"resetTime": "2026-09-05T22:21:46Z"
|
||||
},
|
||||
{
|
||||
"bucketId": "3p-5h",
|
||||
"displayName": "Five Hour Limit Remaining",
|
||||
"window": "5h",
|
||||
"remainingFraction": 1,
|
||||
"resetTime": "2026-08-30T03:21:46Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "Within each group, models share a weekly limit and a 5-hour limit. Quota is consumed proportionally to the cost of the tokens. Thus, limits will last longer with shorter tasks or using more cost-effective models. The 5-hour limit smooths out aggregate demand to fairly distribute global capacity across all users, while your weekly limit is tied directly to your individual tier."
|
||||
}
|
||||
}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const os = require('os');
|
||||
const { exec } = require('child_process');
|
||||
const fs = require('fs');
|
||||
|
||||
const COMMAND_TIMEOUT_MS = 5000;
|
||||
const MAX_BUFFER_BYTES = 10 * 1024 * 1024;
|
||||
|
||||
function runCommand(command, timeout = COMMAND_TIMEOUT_MS) {
|
||||
return new Promise((resolve) => {
|
||||
exec(command, { timeout, maxBuffer: MAX_BUFFER_BYTES }, (error, stdout, stderr) => {
|
||||
resolve({ error, stdout: stdout || '', stderr: stderr || '' });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function parseWindowsProcessJson(stdout) {
|
||||
if (!stdout.trim()) return [];
|
||||
const parsed = JSON.parse(stdout);
|
||||
const rows = Array.isArray(parsed) ? parsed : [parsed];
|
||||
return rows
|
||||
.filter(row => row && row.ProcessId)
|
||||
.map(row => ({
|
||||
pid: Number(row.ProcessId),
|
||||
ppid: Number(row.ParentProcessId || 0),
|
||||
commandLine: row.CommandLine || row.ExecutablePath || '',
|
||||
}));
|
||||
}
|
||||
|
||||
async function listLanguageServerProcesses() {
|
||||
const command = [
|
||||
'powershell -NoProfile -Command',
|
||||
'"Get-CimInstance Win32_Process -Filter \\"name like \'language_server%\'\\"',
|
||||
'| Select-Object ProcessId,ParentProcessId,CommandLine,ExecutablePath',
|
||||
'| ConvertTo-Json -Depth 2"',
|
||||
].join(' ');
|
||||
const result = await runCommand(command);
|
||||
if (result.error && !result.stdout.trim()) {
|
||||
throw new Error(result.stderr.trim() || result.error.message);
|
||||
}
|
||||
return parseWindowsProcessJson(result.stdout);
|
||||
}
|
||||
|
||||
function extractArgument(commandLine, argumentName) {
|
||||
const escapedName = argumentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`--${escapedName}(?:=|\\s+)(?:"([^"]*)"|'([^']*)'|([^\\s]+))`, 'i');
|
||||
const match = commandLine.match(pattern);
|
||||
return match ? (match[1] ?? match[2] ?? match[3] ?? null) : null;
|
||||
}
|
||||
|
||||
const API_PATH = '/exa.language_server_pb.LanguageServerService/RetrieveUserQuotaSummary';
|
||||
const requestBody = JSON.stringify({ metadata: { ideName: 'antigravity', extensionName: 'ag-usage', locale: 'en' } });
|
||||
|
||||
function tryPort(port, csrfToken, protocol) {
|
||||
return new Promise(resolve => {
|
||||
const client = protocol === 'https' ? https : http;
|
||||
const req = client.request({
|
||||
hostname: '127.0.0.1', port, path: API_PATH, method: 'POST',
|
||||
...(protocol === 'https' ? { rejectUnauthorized: false } : {}),
|
||||
headers: {
|
||||
'X-Codeium-Csrf-Token': csrfToken,
|
||||
'Connect-Protocol-Version': '1',
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(requestBody),
|
||||
},
|
||||
timeout: 3000,
|
||||
}, res => {
|
||||
let body = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => resolve({ port, statusCode: res.statusCode, body }));
|
||||
});
|
||||
req.on('error', () => resolve(null));
|
||||
req.on('timeout', () => { req.destroy(); resolve(null); });
|
||||
req.end(requestBody);
|
||||
});
|
||||
}
|
||||
|
||||
function parseResponse(body) {
|
||||
const trimmed = body.trim();
|
||||
if (!trimmed) return null;
|
||||
try { return JSON.parse(trimmed); } catch {}
|
||||
|
||||
// Fallback for NDJSON or streamed lines
|
||||
for (const line of trimmed.split(/\r?\n/).filter(Boolean)) {
|
||||
try { return JSON.parse(line); } catch {}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function syncToTokenWatch(quotaData) {
|
||||
const postData = JSON.stringify(quotaData);
|
||||
// Ersetze diese URL durch die echte von TokenWatch auf der Jannik-Cloud, sobald sie läuft.
|
||||
const url = 'https://tokenwatch.orfel.de/api/webhook/antigravity';
|
||||
const isHttps = url.startsWith('https');
|
||||
const client = isHttps ? https : http;
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const req = client.request(url, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(postData) }
|
||||
}, (res) => {
|
||||
res.on('data', () => {});
|
||||
res.on('end', () => resolve(true));
|
||||
});
|
||||
req.on('error', (err) => {
|
||||
console.error('Webhook error:', err.message);
|
||||
resolve(false);
|
||||
});
|
||||
req.write(postData);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Suche Antigravity Language Server...');
|
||||
const processes = await listLanguageServerProcesses();
|
||||
const processInfo = processes.find(item => extractArgument(item.commandLine, 'csrf_token'));
|
||||
if (!processInfo) { console.error('Fehler: Kein Antigravity language_server Prozess gefunden.'); return; }
|
||||
|
||||
const csrfToken = extractArgument(processInfo.commandLine, 'csrf_token');
|
||||
const extensionPort = Number(extractArgument(processInfo.commandLine, 'extension_server_port')) || null;
|
||||
|
||||
function extractListeningPorts(stdout, pid) {
|
||||
const ports = [];
|
||||
for (const line of stdout.split(/\r?\n/)) {
|
||||
// (LISTEN|ABH) matches German 'ABHÖREN' from netstat
|
||||
if (!/(LISTEN|ABH)/i.test(line) || !new RegExp(`\\s${pid}\\s*$`).test(line)) continue;
|
||||
for (const match of line.matchAll(/(?:127\.0\.0\.1|\[?::1\]?):(\d{2,5})/g)) {
|
||||
const port = Number(match[1]);
|
||||
if (port > 0 && port < 65536) ports.push(port);
|
||||
}
|
||||
}
|
||||
return ports;
|
||||
}
|
||||
|
||||
const netstatResult = await runCommand('netstat -ano');
|
||||
const listeningPorts = extractListeningPorts(netstatResult.stdout, processInfo.pid);
|
||||
const candidates = [...new Set(listeningPorts)].filter(port => port !== extensionPort).sort((a,b)=>a-b);
|
||||
|
||||
let rawData = null;
|
||||
for (const port of candidates) {
|
||||
for (const protocol of ['https', 'http']) {
|
||||
const result = await tryPort(port, csrfToken, protocol);
|
||||
if (result && result.statusCode === 200) {
|
||||
rawData = parseResponse(result.body);
|
||||
if (rawData) break;
|
||||
}
|
||||
}
|
||||
if (rawData) break;
|
||||
}
|
||||
|
||||
const actualData = Array.isArray(rawData) ? rawData[0] : rawData;
|
||||
const groups = actualData?.response?.groups;
|
||||
|
||||
if (!groups) {
|
||||
console.error('Fehler: Konnte keine JSON-Antwort mit Quota auslesen.', rawData);
|
||||
return;
|
||||
}
|
||||
|
||||
let gemini5h = 1, geminiWeekly = 1, gemini5hReset = 'Unknown', geminiWeeklyReset = 'Unknown';
|
||||
let claude5h = 1, claudeWeekly = 1, claude5hReset = 'Unknown', claudeWeeklyReset = 'Unknown';
|
||||
|
||||
const formatTime = (ts) => {
|
||||
if (!ts) return 'Unknown';
|
||||
return new Date(ts).toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'});
|
||||
};
|
||||
|
||||
for (const group of groups) {
|
||||
const isGemini = group.displayName && group.displayName.toLowerCase().includes('gemini');
|
||||
for (const bucket of group.buckets || []) {
|
||||
if (isGemini) {
|
||||
if (bucket.window === '5h') {
|
||||
gemini5h = bucket.remainingFraction ?? 1;
|
||||
gemini5hReset = formatTime(bucket.resetTime);
|
||||
} else if (bucket.window === 'weekly') {
|
||||
geminiWeekly = bucket.remainingFraction ?? 1;
|
||||
geminiWeeklyReset = formatTime(bucket.resetTime);
|
||||
}
|
||||
} else {
|
||||
if (bucket.window === '5h') {
|
||||
claude5h = bucket.remainingFraction ?? 1;
|
||||
claude5hReset = formatTime(bucket.resetTime);
|
||||
} else if (bucket.window === 'weekly') {
|
||||
claudeWeekly = bucket.remainingFraction ?? 1;
|
||||
claudeWeeklyReset = formatTime(bucket.resetTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const payload = {
|
||||
gemini_5h_remaining: Math.round(gemini5h * 100),
|
||||
gemini_weekly_remaining: Math.round(geminiWeekly * 100),
|
||||
claude_5h_remaining: Math.round(claude5h * 100),
|
||||
claude_weekly_remaining: Math.round(claudeWeekly * 100),
|
||||
gemini_5h_reset: `in ${gemini5hReset}`,
|
||||
gemini_weekly_reset: `in ${geminiWeeklyReset}`,
|
||||
claude_5h_reset: `in ${claude5hReset}`,
|
||||
claude_weekly_reset: `in ${claudeWeeklyReset}`
|
||||
};
|
||||
|
||||
console.log('Gefundene Live-Daten:', payload);
|
||||
fs.writeFileSync('dump.json', JSON.stringify(actualData, null, 2)); console.log('Dumped');
|
||||
console.log('Erfolgreich an TokenWatch gesendet!');
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
async function listLanguageServerProcesses() {
|
||||
const command = [
|
||||
'powershell -NoProfile -Command',
|
||||
'"Get-CimInstance Win32_Process -Filter \\"name like \'language_server%\'\\"',
|
||||
'| Select-Object ProcessId,ParentProcessId,CommandLine,ExecutablePath',
|
||||
'| ConvertTo-Json -Depth 2"'
|
||||
].join(' ');
|
||||
const result = execSync(command, { encoding: 'utf8' });
|
||||
return JSON.parse(result);
|
||||
}
|
||||
|
||||
function extractArgument(commandLine, argumentName) {
|
||||
const escapedName = argumentName.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
const pattern = new RegExp(`--${escapedName}(?:=|\\s+)(?:"([^"]*)"|'([^']*)'|([^\\s]+))`, 'i');
|
||||
const match = commandLine.match(pattern);
|
||||
return match ? (match[1] ?? match[2] ?? match[3] ?? null) : null;
|
||||
}
|
||||
|
||||
const API_PATH = '/exa.language_server_pb.LanguageServerService/RetrieveUserQuotaSummary';
|
||||
const requestBody = JSON.stringify({ metadata: { ideName: 'antigravity', extensionName: 'ag-usage', locale: 'en' } });
|
||||
|
||||
function tryPort(port, csrfToken, protocol) {
|
||||
return new Promise(resolve => {
|
||||
const client = protocol === 'https' ? https : http;
|
||||
const req = client.request({
|
||||
hostname: '127.0.0.1', port, path: API_PATH, method: 'POST',
|
||||
...(protocol === 'https' ? { rejectUnauthorized: false } : {}),
|
||||
headers: {
|
||||
'X-Codeium-Csrf-Token': csrfToken,
|
||||
'Connect-Protocol-Version': '1',
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': Buffer.byteLength(requestBody),
|
||||
},
|
||||
timeout: 3000,
|
||||
}, res => {
|
||||
let body = '';
|
||||
res.setEncoding('utf8');
|
||||
res.on('data', chunk => body += chunk);
|
||||
res.on('end', () => resolve({ port, statusCode: res.statusCode, body }));
|
||||
});
|
||||
req.on('error', () => resolve(null));
|
||||
req.write(requestBody);
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const processes = await listLanguageServerProcesses();
|
||||
let procs = Array.isArray(processes) ? processes : (processes ? [processes] : []);
|
||||
let port, csrfToken, protocol;
|
||||
for (const proc of procs) {
|
||||
if (!proc.CommandLine) continue;
|
||||
const p = extractArgument(proc.CommandLine, 'api_server_port');
|
||||
const c = extractArgument(proc.CommandLine, 'csrf_token');
|
||||
if (p && c) {
|
||||
port = p; csrfToken = c;
|
||||
protocol = proc.CommandLine.includes('--api_server_protocol=https') ? 'https' : 'http';
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!port) return console.log('not found');
|
||||
const response = await tryPort(port, csrfToken, protocol);
|
||||
console.log(response.body);
|
||||
})();
|
||||
Reference in new issue
Block a user