lazy-gravity 0.0.2 → 0.0.3
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 +224 -0
- package/dist/bin/cli.js +79 -0
- package/dist/bin/commands/doctor.js +156 -0
- package/dist/bin/commands/open.js +145 -0
- package/dist/bin/commands/setup.js +366 -0
- package/dist/bin/commands/start.js +15 -0
- package/dist/bot/index.js +914 -0
- package/dist/commands/chatCommandHandler.js +145 -0
- package/dist/commands/cleanupCommandHandler.js +396 -0
- package/dist/commands/messageParser.js +28 -0
- package/dist/commands/registerSlashCommands.js +149 -0
- package/dist/commands/slashCommandHandler.js +104 -0
- package/dist/commands/workspaceCommandHandler.js +230 -0
- package/dist/database/chatSessionRepository.js +88 -0
- package/dist/database/scheduleRepository.js +119 -0
- package/dist/database/templateRepository.js +103 -0
- package/dist/database/workspaceBindingRepository.js +109 -0
- package/dist/events/interactionCreateHandler.js +286 -0
- package/dist/events/messageCreateHandler.js +154 -0
- package/dist/index.js +10 -0
- package/dist/middleware/auth.js +10 -0
- package/dist/middleware/sanitize.js +20 -0
- package/dist/services/antigravityLauncher.js +89 -0
- package/dist/services/approvalDetector.js +384 -0
- package/dist/services/autoAcceptService.js +80 -0
- package/dist/services/cdpBridgeManager.js +204 -0
- package/dist/services/cdpConnectionPool.js +157 -0
- package/dist/services/cdpService.js +1311 -0
- package/dist/services/channelManager.js +118 -0
- package/dist/services/chatSessionService.js +516 -0
- package/dist/services/modeService.js +73 -0
- package/dist/services/modelService.js +63 -0
- package/dist/services/processManager.js +61 -0
- package/dist/services/progressSender.js +61 -0
- package/dist/services/promptDispatcher.js +17 -0
- package/dist/services/quotaService.js +185 -0
- package/dist/services/responseMonitor.js +645 -0
- package/dist/services/scheduleService.js +134 -0
- package/dist/services/screenshotService.js +85 -0
- package/dist/services/titleGeneratorService.js +113 -0
- package/dist/services/workspaceService.js +64 -0
- package/dist/ui/autoAcceptUi.js +34 -0
- package/dist/ui/modeUi.js +34 -0
- package/dist/ui/modelsUi.js +97 -0
- package/dist/ui/screenshotUi.js +51 -0
- package/dist/ui/templateUi.js +67 -0
- package/dist/utils/cdpPorts.js +5 -0
- package/dist/utils/config.js +20 -0
- package/dist/utils/configLoader.js +160 -0
- package/dist/utils/discordFormatter.js +167 -0
- package/dist/utils/i18n.js +77 -0
- package/dist/utils/imageHandler.js +154 -0
- package/dist/utils/lockfile.js +113 -0
- package/dist/utils/logger.js +32 -0
- package/dist/utils/logo.js +13 -0
- package/dist/utils/metadataExtractor.js +15 -0
- package/dist/utils/processLogBuffer.js +98 -0
- package/dist/utils/streamMessageFormatter.js +90 -0
- package/package.json +73 -5
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ModeService = exports.DEFAULT_MODE = exports.MODE_UI_NAME_REVERSE = exports.MODE_UI_NAMES = exports.MODE_DESCRIPTIONS = exports.MODE_DISPLAY_NAMES = exports.AVAILABLE_MODES = void 0;
|
|
4
|
+
const i18n_1 = require("../utils/i18n");
|
|
5
|
+
/**
|
|
6
|
+
* Available execution modes
|
|
7
|
+
* fast: Fast response mode (for simple tasks)
|
|
8
|
+
* plan: Planning mode (execute complex tasks step by step)
|
|
9
|
+
*/
|
|
10
|
+
exports.AVAILABLE_MODES = ['fast', 'plan'];
|
|
11
|
+
/** Mode display name mapping */
|
|
12
|
+
exports.MODE_DISPLAY_NAMES = {
|
|
13
|
+
fast: '⚡ Fast',
|
|
14
|
+
plan: '📋 Plan',
|
|
15
|
+
};
|
|
16
|
+
/** Mode description mapping */
|
|
17
|
+
exports.MODE_DESCRIPTIONS = {
|
|
18
|
+
fast: (0, i18n_1.t)('Fast Mode — for simple tasks'),
|
|
19
|
+
plan: (0, i18n_1.t)('Plan Mode — for complex step-by-step tasks'),
|
|
20
|
+
};
|
|
21
|
+
/** Antigravity UI display name mapping (internal name -> UI display name) */
|
|
22
|
+
exports.MODE_UI_NAMES = {
|
|
23
|
+
fast: 'Fast',
|
|
24
|
+
plan: 'Planning',
|
|
25
|
+
};
|
|
26
|
+
/** Reverse mapping from UI display name -> internal name */
|
|
27
|
+
exports.MODE_UI_NAME_REVERSE = Object.fromEntries(Object.entries(exports.MODE_UI_NAMES).map(([k, v]) => [v.toLowerCase(), k]));
|
|
28
|
+
/** Default execution mode */
|
|
29
|
+
exports.DEFAULT_MODE = 'fast';
|
|
30
|
+
/**
|
|
31
|
+
* Service class for managing execution modes.
|
|
32
|
+
* Handles mode switching via the /mode command.
|
|
33
|
+
*/
|
|
34
|
+
class ModeService {
|
|
35
|
+
currentMode = exports.DEFAULT_MODE;
|
|
36
|
+
/**
|
|
37
|
+
* Get the current execution mode
|
|
38
|
+
*/
|
|
39
|
+
getCurrentMode() {
|
|
40
|
+
return this.currentMode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Switch execution mode
|
|
44
|
+
* @param modeName Mode name to set (case-insensitive)
|
|
45
|
+
*/
|
|
46
|
+
setMode(modeName) {
|
|
47
|
+
if (!modeName || modeName.trim() === '') {
|
|
48
|
+
return {
|
|
49
|
+
success: false,
|
|
50
|
+
error: (0, i18n_1.t)('⚠️ Mode name not specified. Available modes: ') + exports.AVAILABLE_MODES.join(', '),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const normalized = modeName.trim().toLowerCase();
|
|
54
|
+
if (!exports.AVAILABLE_MODES.includes(normalized)) {
|
|
55
|
+
return {
|
|
56
|
+
success: false,
|
|
57
|
+
error: (0, i18n_1.t)(`⚠️ Invalid mode "${modeName}". Available modes: ${exports.AVAILABLE_MODES.join(', ')}`),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
this.currentMode = normalized;
|
|
61
|
+
return {
|
|
62
|
+
success: true,
|
|
63
|
+
mode: this.currentMode,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get the list of available modes
|
|
68
|
+
*/
|
|
69
|
+
getAvailableModes() {
|
|
70
|
+
return exports.AVAILABLE_MODES;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.ModeService = ModeService;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ModelService = exports.DEFAULT_MODEL = exports.AVAILABLE_MODELS = void 0;
|
|
4
|
+
const i18n_1 = require("../utils/i18n");
|
|
5
|
+
/**
|
|
6
|
+
* Available LLM models
|
|
7
|
+
* Aligned with models selectable in the Antigravity (Cursor fork) UI
|
|
8
|
+
* Note: Models may change with Antigravity version updates
|
|
9
|
+
*/
|
|
10
|
+
exports.AVAILABLE_MODELS = [
|
|
11
|
+
'gemini-3.1-pro-high',
|
|
12
|
+
'gemini-3.1-pro-low',
|
|
13
|
+
'gemini-3-flash',
|
|
14
|
+
'claude-sonnet-4.6-thinking',
|
|
15
|
+
'claude-opus-4.6-thinking',
|
|
16
|
+
'gpt-oss-120b-medium'
|
|
17
|
+
];
|
|
18
|
+
/** Default LLM model */
|
|
19
|
+
exports.DEFAULT_MODEL = 'gemini-3-flash';
|
|
20
|
+
/**
|
|
21
|
+
* Service class for managing LLM models.
|
|
22
|
+
* Handles model switching via the /model command.
|
|
23
|
+
*/
|
|
24
|
+
class ModelService {
|
|
25
|
+
currentModel = exports.DEFAULT_MODEL;
|
|
26
|
+
/**
|
|
27
|
+
* Get the current LLM model
|
|
28
|
+
*/
|
|
29
|
+
getCurrentModel() {
|
|
30
|
+
return this.currentModel;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Switch LLM model
|
|
34
|
+
* @param modelName Model name to set (case-insensitive)
|
|
35
|
+
*/
|
|
36
|
+
setModel(modelName) {
|
|
37
|
+
if (!modelName || modelName.trim() === '') {
|
|
38
|
+
return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: (0, i18n_1.t)('⚠️ Model name not specified. Available models: ') + exports.AVAILABLE_MODELS.join(', '),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const normalized = modelName.trim().toLowerCase();
|
|
44
|
+
if (!exports.AVAILABLE_MODELS.includes(normalized)) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: (0, i18n_1.t)(`⚠️ Invalid model "${modelName}". Available models: ${exports.AVAILABLE_MODELS.join(', ')}`),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
this.currentModel = normalized;
|
|
51
|
+
return {
|
|
52
|
+
success: true,
|
|
53
|
+
model: this.currentModel,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the list of available models
|
|
58
|
+
*/
|
|
59
|
+
getAvailableModels() {
|
|
60
|
+
return exports.AVAILABLE_MODELS;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.ModelService = ModelService;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProcessManager = void 0;
|
|
4
|
+
const child_process_1 = require("child_process");
|
|
5
|
+
class ProcessManager {
|
|
6
|
+
maxConcurrentTasks;
|
|
7
|
+
queue = [];
|
|
8
|
+
runningProcesses = new Map();
|
|
9
|
+
constructor(maxConcurrentTasks = 1) {
|
|
10
|
+
this.maxConcurrentTasks = maxConcurrentTasks;
|
|
11
|
+
}
|
|
12
|
+
async submitTask(options) {
|
|
13
|
+
this.queue.push(options);
|
|
14
|
+
this.runNext();
|
|
15
|
+
}
|
|
16
|
+
runNext() {
|
|
17
|
+
if (this.runningProcesses.size >= this.maxConcurrentTasks) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const nextTask = this.queue.shift();
|
|
21
|
+
if (!nextTask) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const { id, command, args, cwd, onStdout, onStderr, onClose } = nextTask;
|
|
25
|
+
const child = (0, child_process_1.spawn)(command, args, { cwd });
|
|
26
|
+
this.runningProcesses.set(id, child);
|
|
27
|
+
child.stdout?.on('data', (data) => {
|
|
28
|
+
if (onStdout) {
|
|
29
|
+
onStdout(data.toString());
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
child.stderr?.on('data', (data) => {
|
|
33
|
+
if (onStderr) {
|
|
34
|
+
onStderr(data.toString());
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
child.on('close', (code) => {
|
|
38
|
+
this.runningProcesses.delete(id);
|
|
39
|
+
if (onClose) {
|
|
40
|
+
onClose(code ?? 0);
|
|
41
|
+
}
|
|
42
|
+
this.runNext();
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
stopTask(taskId) {
|
|
46
|
+
const child = this.runningProcesses.get(taskId);
|
|
47
|
+
if (child) {
|
|
48
|
+
child.kill();
|
|
49
|
+
this.runningProcesses.delete(taskId);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
// Check if queued
|
|
53
|
+
const indexInQueue = this.queue.findIndex((task) => task.id === taskId);
|
|
54
|
+
if (indexInQueue !== -1) {
|
|
55
|
+
this.queue.splice(indexInQueue, 1);
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.ProcessManager = ProcessManager;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ProgressSender = void 0;
|
|
4
|
+
class ProgressSender {
|
|
5
|
+
throttleMs;
|
|
6
|
+
maxLength;
|
|
7
|
+
wrapInCodeBlock;
|
|
8
|
+
buffer = '';
|
|
9
|
+
timer = null;
|
|
10
|
+
sendContent;
|
|
11
|
+
constructor(options) {
|
|
12
|
+
if (!options.send && !options.message) {
|
|
13
|
+
throw new Error('ProgressSender requires either message or send option');
|
|
14
|
+
}
|
|
15
|
+
this.sendContent = options.send
|
|
16
|
+
? options.send
|
|
17
|
+
: async (content) => options.message.reply({ content });
|
|
18
|
+
this.throttleMs = options.throttleMs ?? 3000;
|
|
19
|
+
this.maxLength = options.maxLength ?? 4000;
|
|
20
|
+
this.wrapInCodeBlock = options.wrapInCodeBlock ?? true;
|
|
21
|
+
}
|
|
22
|
+
append(text) {
|
|
23
|
+
this.buffer += text;
|
|
24
|
+
if (!this.timer) {
|
|
25
|
+
this.timer = setTimeout(() => {
|
|
26
|
+
this.emit();
|
|
27
|
+
}, this.throttleMs);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
forceEmit() {
|
|
31
|
+
this.emit();
|
|
32
|
+
}
|
|
33
|
+
emit() {
|
|
34
|
+
if (this.timer) {
|
|
35
|
+
clearTimeout(this.timer);
|
|
36
|
+
this.timer = null;
|
|
37
|
+
}
|
|
38
|
+
if (!this.buffer)
|
|
39
|
+
return;
|
|
40
|
+
const payload = this.buffer;
|
|
41
|
+
this.buffer = '';
|
|
42
|
+
const chunks = this.splitByLength(payload, this.maxLength);
|
|
43
|
+
for (const chunk of chunks) {
|
|
44
|
+
const content = this.wrapInCodeBlock ? `\`\`\`\n${chunk}\n\`\`\`` : chunk;
|
|
45
|
+
this.sendContent(content).catch(() => { });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
splitByLength(text, maxLength) {
|
|
49
|
+
if (text.length <= maxLength) {
|
|
50
|
+
return [text];
|
|
51
|
+
}
|
|
52
|
+
const result = [];
|
|
53
|
+
let cursor = 0;
|
|
54
|
+
while (cursor < text.length) {
|
|
55
|
+
result.push(text.slice(cursor, cursor + maxLength));
|
|
56
|
+
cursor += maxLength;
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
exports.ProgressSender = ProgressSender;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromptDispatcher = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Dispatcher that calls the existing sendPromptToAntigravity.
|
|
6
|
+
* Unifies dependency injection on the caller side and simplifies event handlers.
|
|
7
|
+
*/
|
|
8
|
+
class PromptDispatcher {
|
|
9
|
+
deps;
|
|
10
|
+
constructor(deps) {
|
|
11
|
+
this.deps = deps;
|
|
12
|
+
}
|
|
13
|
+
async send(req) {
|
|
14
|
+
await this.deps.sendPromptImpl(this.deps.bridge, req.message, req.prompt, req.cdp, this.deps.modeService, this.deps.modelService, req.inboundImages ?? [], req.options);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.PromptDispatcher = PromptDispatcher;
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.QuotaService = void 0;
|
|
37
|
+
const logger_1 = require("../utils/logger");
|
|
38
|
+
const child_process_1 = require("child_process");
|
|
39
|
+
const util_1 = require("util");
|
|
40
|
+
const https = __importStar(require("https"));
|
|
41
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
42
|
+
class QuotaService {
|
|
43
|
+
cachedPort = null;
|
|
44
|
+
cachedCsrfToken = null;
|
|
45
|
+
cachedPid = null;
|
|
46
|
+
async getUnixProcessInfo() {
|
|
47
|
+
try {
|
|
48
|
+
// macOS
|
|
49
|
+
const { stdout } = await execAsync('pgrep -fl language_server');
|
|
50
|
+
const lines = stdout.split('\n');
|
|
51
|
+
for (const line of lines) {
|
|
52
|
+
if (line.includes('--csrf_token')) {
|
|
53
|
+
const parts = line.trim().split(/\s+/);
|
|
54
|
+
const pid = parseInt(parts[0], 10);
|
|
55
|
+
const cmd = line.substring(parts[0].length).trim();
|
|
56
|
+
const tokenMatch = cmd.match(/--csrf_token[=\s]+([a-zA-Z0-9\-]+)/);
|
|
57
|
+
if (pid && tokenMatch && tokenMatch[1]) {
|
|
58
|
+
return { pid, csrf_token: tokenMatch[1] };
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch (e) {
|
|
64
|
+
logger_1.logger.error('Failed to get process info:', e);
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
async getListeningPorts(pid) {
|
|
69
|
+
const ports = [];
|
|
70
|
+
try {
|
|
71
|
+
// macOS
|
|
72
|
+
const { stdout } = await execAsync(`lsof -nP -a -iTCP -sTCP:LISTEN -p ${pid}`);
|
|
73
|
+
const regex = new RegExp(`^\\S+\\s+${pid}\\s+.*?(?:TCP|UDP)\\s+(?:\\*|[\\d.]+|\\[[\\da-f:]+\\]):(\\d+)\\s+\\(LISTEN\\)`, 'gim');
|
|
74
|
+
let match;
|
|
75
|
+
while ((match = regex.exec(stdout)) !== null) {
|
|
76
|
+
const port = parseInt(match[1], 10);
|
|
77
|
+
if (!ports.includes(port)) {
|
|
78
|
+
ports.push(port);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (e) {
|
|
83
|
+
logger_1.logger.error(`Failed to get ports for pid ${pid}:`, e);
|
|
84
|
+
}
|
|
85
|
+
return ports;
|
|
86
|
+
}
|
|
87
|
+
requestApi(port, csrfToken) {
|
|
88
|
+
return new Promise((resolve, reject) => {
|
|
89
|
+
const data = JSON.stringify({
|
|
90
|
+
metadata: { ideName: 'antigravity', extensionName: 'antigravity', locale: 'en' }
|
|
91
|
+
});
|
|
92
|
+
const options = {
|
|
93
|
+
hostname: '127.0.0.1',
|
|
94
|
+
port: port,
|
|
95
|
+
path: '/exa.language_server_pb.LanguageServerService/GetUserStatus',
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: {
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
'Content-Length': Buffer.byteLength(data),
|
|
100
|
+
'Connect-Protocol-Version': '1',
|
|
101
|
+
'X-Codeium-Csrf-Token': csrfToken,
|
|
102
|
+
},
|
|
103
|
+
rejectUnauthorized: false,
|
|
104
|
+
timeout: 2000,
|
|
105
|
+
};
|
|
106
|
+
const req = https.request(options, res => {
|
|
107
|
+
let body = '';
|
|
108
|
+
res.on('data', chunk => body += chunk);
|
|
109
|
+
res.on('end', () => {
|
|
110
|
+
if (res.statusCode !== 200) {
|
|
111
|
+
return reject(new Error(`HTTP ${res.statusCode}`));
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const parsed = JSON.parse(body);
|
|
115
|
+
const cascadeData = parsed?.userStatus?.cascadeModelConfigData;
|
|
116
|
+
const rawConfigs = cascadeData?.clientModelConfigs || [];
|
|
117
|
+
const configs = rawConfigs.map((c) => {
|
|
118
|
+
const label = c.label || c.displayName || c.modelName || c.model || '';
|
|
119
|
+
const model = c.model || c.modelId || '';
|
|
120
|
+
const qi = c.quotaInfo || c.quota || c.usageInfo;
|
|
121
|
+
const quotaInfo = qi ? {
|
|
122
|
+
remainingFraction: qi.remainingFraction ?? qi.remaining ?? 1,
|
|
123
|
+
resetTime: qi.resetTime || qi.resetAt || '',
|
|
124
|
+
} : undefined;
|
|
125
|
+
return { label, model, quotaInfo };
|
|
126
|
+
});
|
|
127
|
+
resolve({ clientModelConfigs: configs });
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
reject(new Error('Invalid JSON response'));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
req.on('error', reject);
|
|
135
|
+
req.on('timeout', () => {
|
|
136
|
+
req.destroy();
|
|
137
|
+
reject(new Error('Request timeout'));
|
|
138
|
+
});
|
|
139
|
+
req.write(data);
|
|
140
|
+
req.end();
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async fetchQuota() {
|
|
144
|
+
let processInfo = await this.getUnixProcessInfo();
|
|
145
|
+
if (!processInfo) {
|
|
146
|
+
logger_1.logger.error('No language_server process found.');
|
|
147
|
+
return [];
|
|
148
|
+
}
|
|
149
|
+
const { pid, csrf_token } = processInfo;
|
|
150
|
+
// If PID or Token changed, invalidate cache
|
|
151
|
+
if (this.cachedPid !== pid || this.cachedCsrfToken !== csrf_token) {
|
|
152
|
+
this.cachedPort = null;
|
|
153
|
+
this.cachedPid = pid;
|
|
154
|
+
this.cachedCsrfToken = csrf_token;
|
|
155
|
+
}
|
|
156
|
+
let targetPort = this.cachedPort;
|
|
157
|
+
if (!targetPort) {
|
|
158
|
+
const ports = await this.getListeningPorts(pid);
|
|
159
|
+
for (const port of ports) {
|
|
160
|
+
try {
|
|
161
|
+
const data = await this.requestApi(port, csrf_token);
|
|
162
|
+
targetPort = port;
|
|
163
|
+
this.cachedPort = port;
|
|
164
|
+
return data.clientModelConfigs || [];
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
continue; // try next port
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
try {
|
|
173
|
+
const data = await this.requestApi(targetPort, csrf_token);
|
|
174
|
+
return data.clientModelConfigs || [];
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
// cache might be invalid
|
|
178
|
+
this.cachedPort = null;
|
|
179
|
+
return this.fetchQuota();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return [];
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
exports.QuotaService = QuotaService;
|