lazy-gravity 0.5.5 → 0.6.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/bot/index.js +26 -6
- package/dist/bot/telegramMessageHandler.js +1 -1
- package/dist/commands/joinCommandHandler.js +4 -2
- package/dist/events/messageCreateHandler.js +2 -0
- package/dist/services/channelManager.js +4 -4
- package/dist/services/responseMonitor.js +5 -2
- package/dist/utils/configLoader.js +16 -0
- package/package.json +2 -2
package/dist/bot/index.js
CHANGED
|
@@ -97,6 +97,20 @@ const modelButtonAction_1 = require("../handlers/modelButtonAction");
|
|
|
97
97
|
const autoAcceptButtonAction_1 = require("../handlers/autoAcceptButtonAction");
|
|
98
98
|
const templateButtonAction_1 = require("../handlers/templateButtonAction");
|
|
99
99
|
const modeSelectAction_1 = require("../handlers/modeSelectAction");
|
|
100
|
+
const channelManager_2 = require("../services/channelManager");
|
|
101
|
+
/**
|
|
102
|
+
* Normalize a candidate startup channel name for preference checks.
|
|
103
|
+
*/
|
|
104
|
+
function normalizeStartupChannelName(name) {
|
|
105
|
+
return name.trim().replace(/^#/, '').toLowerCase();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Prefer the shared default channel name plus the localized 常规 variant.
|
|
109
|
+
*/
|
|
110
|
+
function isPreferredDiscordStartupChannel(name) {
|
|
111
|
+
const normalized = normalizeStartupChannelName(name);
|
|
112
|
+
return normalized === channelManager_2.DEFAULT_CHANNEL_NAME || normalized === '常规';
|
|
113
|
+
}
|
|
100
114
|
// =============================================================================
|
|
101
115
|
// Embed color palette (color-coded by phase)
|
|
102
116
|
// =============================================================================
|
|
@@ -499,7 +513,7 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
499
513
|
const monitor = new responseMonitor_1.ResponseMonitor({
|
|
500
514
|
cdpService: cdp,
|
|
501
515
|
pollIntervalMs: 2000,
|
|
502
|
-
maxDurationMs:
|
|
516
|
+
maxDurationMs: options?.responseTimeoutMs,
|
|
503
517
|
stopGoneConfirmCount: 3,
|
|
504
518
|
extractionMode: options?.extractionMode,
|
|
505
519
|
onPhaseChange: (_phase, _text) => {
|
|
@@ -663,9 +677,10 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
663
677
|
: lastProgressText;
|
|
664
678
|
const separated = (0, discordFormatter_1.splitOutputAndLogs)(timeoutText || '');
|
|
665
679
|
const sanitizedTimeoutLogs = lastActivityLogText || processLogBuffer.snapshot();
|
|
680
|
+
const timeoutMinutes = Math.round((options?.responseTimeoutMs ?? 900000) / 60000);
|
|
666
681
|
const payload = separated.output && separated.output.trim().length > 0
|
|
667
|
-
? (0, i18n_1.t)(`${separated.output}\n\n[Monitor Ended] Timeout after
|
|
668
|
-
:
|
|
682
|
+
? (0, i18n_1.t)(`${separated.output}\n\n[Monitor Ended] Timeout after ${timeoutMinutes} minutes of inactivity.`)
|
|
683
|
+
: `Monitor ended after ${timeoutMinutes} minutes of inactivity. No text was retrieved.`;
|
|
669
684
|
liveResponseUpdateVersion += 1;
|
|
670
685
|
const responseVersion = liveResponseUpdateVersion;
|
|
671
686
|
await upsertLiveResponseEmbeds(`${PHASE_ICONS.timeout} Timeout`, payload, PHASE_COLORS.timeout, `⏱️ Elapsed: ${elapsed}s | Timeout`, {
|
|
@@ -774,7 +789,7 @@ const startBot = async (cliLogLevel) => {
|
|
|
774
789
|
discord_js_1.GatewayIntentBits.MessageContent,
|
|
775
790
|
]
|
|
776
791
|
});
|
|
777
|
-
const joinHandler = new joinCommandHandler_1.JoinCommandHandler(chatSessionService, chatSessionRepo, workspaceBindingRepo, channelManager, bridge.pool, workspaceService, client, config.extractionMode);
|
|
792
|
+
const joinHandler = new joinCommandHandler_1.JoinCommandHandler(chatSessionService, chatSessionRepo, workspaceBindingRepo, channelManager, bridge.pool, workspaceService, client, config.extractionMode, config.responseTimeoutMs);
|
|
778
793
|
client.once(discord_js_1.Events.ClientReady, async (readyClient) => {
|
|
779
794
|
logger_1.logger.info(`Ready! Logged in as ${readyClient.user.tag} | extractionMode=${config.extractionMode}`);
|
|
780
795
|
try {
|
|
@@ -800,10 +815,14 @@ const startBot = async (cliLogLevel) => {
|
|
|
800
815
|
.addFields({ name: 'Version', value: version, inline: true }, { name: 'Node.js', value: process.versions.node, inline: true }, { name: 'OS', value: `${os.platform()} ${os.release()}`, inline: true }, { name: 'CDP', value: cdpStatus, inline: true }, { name: 'Model', value: modelService.getCurrentModel(), inline: true }, { name: 'Mode', value: modeService.getCurrentMode(), inline: true }, { name: 'Projects', value: `${projects.length} registered`, inline: true }, { name: 'Extraction', value: config.extractionMode, inline: true })
|
|
801
816
|
.setFooter({ text: `Started at ${new Date().toLocaleString()}` })
|
|
802
817
|
.setTimestamp();
|
|
803
|
-
//
|
|
818
|
+
// Prefer the guild's general text channel, then fall back to the first sendable text channel.
|
|
804
819
|
const guild = readyClient.guilds.cache.first();
|
|
805
820
|
if (guild) {
|
|
806
|
-
const
|
|
821
|
+
const sendableTextChannels = guild.channels.cache.filter((ch) => ch.isTextBased()
|
|
822
|
+
&& !ch.isVoiceBased()
|
|
823
|
+
&& ch.permissionsFor(readyClient.user)?.has('SendMessages'));
|
|
824
|
+
const channel = sendableTextChannels.find((ch) => isPreferredDiscordStartupChannel(ch.name))
|
|
825
|
+
?? sendableTextChannels.first();
|
|
807
826
|
if (channel && channel.isTextBased()) {
|
|
808
827
|
await channel.send({ embeds: [dashboardEmbed] });
|
|
809
828
|
logger_1.logger.info('Startup dashboard embed sent.');
|
|
@@ -969,6 +988,7 @@ const startBot = async (cliLogLevel) => {
|
|
|
969
988
|
botToken: config.telegramToken,
|
|
970
989
|
botApi: telegramBot.api,
|
|
971
990
|
chatSessionService,
|
|
991
|
+
responseTimeoutMs: config.responseTimeoutMs,
|
|
972
992
|
});
|
|
973
993
|
// Compose select handlers: project select + mode select
|
|
974
994
|
const projectSelectHandler = (0, telegramProjectCommand_1.createTelegramSelectHandler)({
|
|
@@ -192,7 +192,7 @@ function createTelegramMessageHandler(deps) {
|
|
|
192
192
|
// Send initial status message
|
|
193
193
|
statusMsg = await channel.send({ text: 'Processing...' }).catch(() => null);
|
|
194
194
|
await new Promise((resolve) => {
|
|
195
|
-
const TIMEOUT_MS =
|
|
195
|
+
const TIMEOUT_MS = deps.responseTimeoutMs ?? 900_000;
|
|
196
196
|
let settled = false;
|
|
197
197
|
const settle = () => {
|
|
198
198
|
if (settled)
|
|
@@ -24,9 +24,10 @@ class JoinCommandHandler {
|
|
|
24
24
|
workspaceService;
|
|
25
25
|
client;
|
|
26
26
|
extractionMode;
|
|
27
|
+
responseTimeoutMs;
|
|
27
28
|
/** Active ResponseMonitors per workspace (for AI response mirroring) */
|
|
28
29
|
activeResponseMonitors = new Map();
|
|
29
|
-
constructor(chatSessionService, chatSessionRepo, bindingRepo, channelManager, pool, workspaceService, client, extractionMode) {
|
|
30
|
+
constructor(chatSessionService, chatSessionRepo, bindingRepo, channelManager, pool, workspaceService, client, extractionMode, responseTimeoutMs) {
|
|
30
31
|
this.chatSessionService = chatSessionService;
|
|
31
32
|
this.chatSessionRepo = chatSessionRepo;
|
|
32
33
|
this.bindingRepo = bindingRepo;
|
|
@@ -35,6 +36,7 @@ class JoinCommandHandler {
|
|
|
35
36
|
this.workspaceService = workspaceService;
|
|
36
37
|
this.client = client;
|
|
37
38
|
this.extractionMode = extractionMode;
|
|
39
|
+
this.responseTimeoutMs = responseTimeoutMs;
|
|
38
40
|
}
|
|
39
41
|
/**
|
|
40
42
|
* Resolve a project name (from DB) to its full absolute path.
|
|
@@ -282,7 +284,7 @@ class JoinCommandHandler {
|
|
|
282
284
|
const monitor = new responseMonitor_1.ResponseMonitor({
|
|
283
285
|
cdpService: cdp,
|
|
284
286
|
pollIntervalMs: 2000,
|
|
285
|
-
maxDurationMs:
|
|
287
|
+
maxDurationMs: this.responseTimeoutMs,
|
|
286
288
|
extractionMode: this.extractionMode,
|
|
287
289
|
onComplete: (finalText) => {
|
|
288
290
|
this.activeResponseMonitors.delete(projectName);
|
|
@@ -118,6 +118,7 @@ function createMessageCreateHandler(deps) {
|
|
|
118
118
|
titleGenerator: deps.titleGenerator,
|
|
119
119
|
userPrefRepo: deps.userPrefRepo,
|
|
120
120
|
extractionMode: deps.config.extractionMode,
|
|
121
|
+
responseTimeoutMs: deps.config.responseTimeoutMs,
|
|
121
122
|
});
|
|
122
123
|
}
|
|
123
124
|
else {
|
|
@@ -263,6 +264,7 @@ function createMessageCreateHandler(deps) {
|
|
|
263
264
|
titleGenerator: deps.titleGenerator,
|
|
264
265
|
userPrefRepo: deps.userPrefRepo,
|
|
265
266
|
extractionMode: deps.config.extractionMode,
|
|
267
|
+
responseTimeoutMs: deps.config.responseTimeoutMs,
|
|
266
268
|
onFullCompletion: settle,
|
|
267
269
|
}).catch((err) => {
|
|
268
270
|
// sendPromptToAntigravity rejected before onFullCompletion fired
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ChannelManager = void 0;
|
|
3
|
+
exports.ChannelManager = exports.DEFAULT_CHANNEL_NAME = void 0;
|
|
4
4
|
const discord_js_1 = require("discord.js");
|
|
5
5
|
/** Category name prefix emoji */
|
|
6
6
|
const CATEGORY_PREFIX = '🗂️-';
|
|
7
7
|
/** Default channel name under the category */
|
|
8
|
-
|
|
8
|
+
exports.DEFAULT_CHANNEL_NAME = 'general';
|
|
9
9
|
/**
|
|
10
10
|
* Class that manages Discord categories and channels corresponding to workspace paths.
|
|
11
11
|
* Creates the category/channel if they don't exist for the given workspace name,
|
|
@@ -77,7 +77,7 @@ class ChannelManager {
|
|
|
77
77
|
const existingTextChannel = guild.channels.cache.find((ch) => ch.type === discord_js_1.ChannelType.GuildText &&
|
|
78
78
|
'parentId' in ch &&
|
|
79
79
|
ch.parentId === categoryId &&
|
|
80
|
-
ch.name === DEFAULT_CHANNEL_NAME);
|
|
80
|
+
ch.name === exports.DEFAULT_CHANNEL_NAME);
|
|
81
81
|
if (existingTextChannel) {
|
|
82
82
|
return {
|
|
83
83
|
categoryId,
|
|
@@ -85,7 +85,7 @@ class ChannelManager {
|
|
|
85
85
|
created: false,
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
-
const sessionResult = await this.createSessionChannel(guild, categoryId, DEFAULT_CHANNEL_NAME);
|
|
88
|
+
const sessionResult = await this.createSessionChannel(guild, categoryId, exports.DEFAULT_CHANNEL_NAME);
|
|
89
89
|
return {
|
|
90
90
|
categoryId,
|
|
91
91
|
channelId: sessionResult.channelId,
|
|
@@ -472,7 +472,7 @@ class ResponseMonitor {
|
|
|
472
472
|
constructor(options) {
|
|
473
473
|
this.cdpService = options.cdpService;
|
|
474
474
|
this.pollIntervalMs = options.pollIntervalMs ?? 2000;
|
|
475
|
-
this.maxDurationMs = options.maxDurationMs ??
|
|
475
|
+
this.maxDurationMs = options.maxDurationMs ?? 900000;
|
|
476
476
|
this.stopGoneConfirmCount = options.stopGoneConfirmCount ?? 3;
|
|
477
477
|
this.extractionMode = options.extractionMode ?? 'structured';
|
|
478
478
|
this.onProgress = options.onProgress;
|
|
@@ -861,7 +861,10 @@ class ResponseMonitor {
|
|
|
861
861
|
}
|
|
862
862
|
}
|
|
863
863
|
// Activity-based inactivity timeout (#49)
|
|
864
|
-
|
|
864
|
+
// Guard: never timeout while the stop button is visible — it means
|
|
865
|
+
// Antigravity is still actively generating (extended thinking, long
|
|
866
|
+
// shell commands, large file operations, etc.).
|
|
867
|
+
if (this.maxDurationMs > 0 && !isGenerating && Date.now() - this.lastActivityTime >= this.maxDurationMs) {
|
|
865
868
|
const lastText = this.lastText ?? '';
|
|
866
869
|
this.setPhase('timeout', lastText);
|
|
867
870
|
await this.stop();
|
|
@@ -102,6 +102,7 @@ function mergeConfig(persisted) {
|
|
|
102
102
|
const autoApproveFileEdits = resolveBoolean(process.env.AUTO_APPROVE_FILE_EDITS, persisted.autoApproveFileEdits, false);
|
|
103
103
|
const logLevel = resolveLogLevel(process.env.LOG_LEVEL, persisted.logLevel);
|
|
104
104
|
const extractionMode = resolveExtractionMode(process.env.EXTRACTION_MODE, persisted.extractionMode);
|
|
105
|
+
const responseTimeoutMs = resolvePositiveInt(process.env.RESPONSE_TIMEOUT_MS, persisted.responseTimeoutMs, 900000);
|
|
105
106
|
// Telegram credentials — only required when Telegram is an active platform
|
|
106
107
|
const telegramToken = process.env.TELEGRAM_BOT_TOKEN ?? persisted.telegramToken ?? undefined;
|
|
107
108
|
const telegramAllowedUserIds = resolveTelegramAllowedUserIds(persisted);
|
|
@@ -117,6 +118,7 @@ function mergeConfig(persisted) {
|
|
|
117
118
|
autoApproveFileEdits,
|
|
118
119
|
logLevel,
|
|
119
120
|
extractionMode,
|
|
121
|
+
responseTimeoutMs,
|
|
120
122
|
telegramToken,
|
|
121
123
|
telegramAllowedUserIds,
|
|
122
124
|
platforms,
|
|
@@ -186,6 +188,20 @@ function resolveBoolean(envValue, persistedValue, defaultValue) {
|
|
|
186
188
|
return persistedValue;
|
|
187
189
|
return defaultValue;
|
|
188
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Resolve a non-negative integer value from env var > persisted config > default.
|
|
193
|
+
* Returns the default if the env/persisted value is not a valid non-negative integer.
|
|
194
|
+
*/
|
|
195
|
+
function resolvePositiveInt(envValue, persistedValue, defaultValue) {
|
|
196
|
+
if (envValue !== undefined) {
|
|
197
|
+
const parsed = parseInt(envValue, 10);
|
|
198
|
+
if (!isNaN(parsed) && parsed >= 0)
|
|
199
|
+
return parsed;
|
|
200
|
+
}
|
|
201
|
+
if (persistedValue !== undefined && persistedValue >= 0)
|
|
202
|
+
return persistedValue;
|
|
203
|
+
return defaultValue;
|
|
204
|
+
}
|
|
189
205
|
// ---------------------------------------------------------------------------
|
|
190
206
|
// Public API (ConfigLoader namespace)
|
|
191
207
|
// ---------------------------------------------------------------------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazy-gravity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Control Antigravity from anywhere — a local, secure bot (Discord + Telegram) that lets you remotely operate Antigravity on your home PC from your smartphone.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@types/ws": "^8.18.1",
|
|
66
66
|
"jest": "^30.2.0",
|
|
67
67
|
"jest-environment-jsdom": "^30.2.0",
|
|
68
|
-
"jsdom": "^
|
|
68
|
+
"jsdom": "^29.0.0",
|
|
69
69
|
"minimatch": "^10.2.1",
|
|
70
70
|
"semantic-release": "^25.0.3",
|
|
71
71
|
"ts-jest": "^29.4.6",
|