lazy-gravity 0.10.0 → 0.11.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/bin/commands/doctor.js +45 -7
- package/dist/bot/index.js +191 -22
- package/dist/commands/chatCommandHandler.js +11 -41
- package/dist/events/interactionCreateHandler.js +158 -27
- package/dist/events/messageCreateHandler.js +14 -4
- package/dist/handlers/fileChangeButtonAction.js +12 -24
- package/dist/handlers/genericActionButtonAction.js +16 -18
- package/dist/handlers/planningButtonAction.js +105 -17
- package/dist/handlers/planningModalSubmitAction.js +38 -0
- package/dist/handlers/questionSelectAction.js +8 -7
- package/dist/handlers/questionSkipAction.js +7 -6
- package/dist/platform/discord/wrappers.js +76 -0
- package/dist/services/approvalDetector.js +43 -14
- package/dist/services/artifactService.js +61 -21
- package/dist/services/assistantDomExtractor.js +23 -3
- package/dist/services/cdpBridgeManager.js +35 -2
- package/dist/services/cdpConnectionPool.js +7 -2
- package/dist/services/cdpService.js +113 -8
- package/dist/services/chatSessionService.js +15 -8
- package/dist/services/notificationSender.js +12 -2
- package/dist/services/planningDetector.js +1 -1
- package/dist/services/promptDispatcher.js +21 -1
- package/dist/services/questionDetector.js +128 -76
- package/dist/services/responseMonitor.js +17 -1
- package/dist/utils/fileOpenCache.js +2 -6
- package/dist/utils/questionActionUtils.js +22 -0
- package/package.json +1 -1
|
@@ -61,24 +61,37 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
61
61
|
const callParams = {
|
|
62
62
|
expression: `
|
|
63
63
|
(() => {
|
|
64
|
+
const getInteractiveItems = (elContainer) => {
|
|
65
|
+
return Array.from(elContainer.querySelectorAll('li, label, a, [role="radio"], [role="option"], [class*="cursor-pointer"]'))
|
|
66
|
+
.filter(el => {
|
|
67
|
+
if (el.tagName === 'BUTTON' || el.closest('button')) return false;
|
|
68
|
+
const role = el.getAttribute('role');
|
|
69
|
+
if (['radio', 'option', 'checkbox', 'button', 'menuitem'].includes(role)) return true;
|
|
70
|
+
if (el.tagName === 'A' || el.tagName === 'LABEL') return true;
|
|
71
|
+
const style = window.getComputedStyle(el);
|
|
72
|
+
return style.cursor === 'pointer';
|
|
73
|
+
});
|
|
74
|
+
};
|
|
64
75
|
const containers = Array.from(document.querySelectorAll('div, form, dialog')).reverse();
|
|
65
76
|
let targetList = null;
|
|
66
77
|
let submitBtn = null;
|
|
67
78
|
|
|
68
79
|
for (const container of containers) {
|
|
80
|
+
const hasTextInput = container.querySelector('textarea, input:not([type="hidden"]):not([type="radio"]):not([type="checkbox"]), [contenteditable], [role="textbox"], .monaco-editor, .editor-container, .inputarea, vscode-text-field, vscode-text-area');
|
|
81
|
+
const hasExplicitQuestionItems = container.querySelector('[role="radio"], input[type="radio"], input[type="checkbox"], [role="option"], [role="checkbox"]');
|
|
82
|
+
if (hasTextInput && !hasExplicitQuestionItems) continue;
|
|
69
83
|
const buttons = Array.from(container.querySelectorAll('button'));
|
|
70
84
|
let possibleSubmitBtn = null;
|
|
71
85
|
for (const btn of buttons) {
|
|
72
86
|
const text = btn.textContent?.toLowerCase() || '';
|
|
73
|
-
if (text.includes('submit')) {
|
|
87
|
+
if (text.includes('submit') || text.includes('continue')) {
|
|
74
88
|
possibleSubmitBtn = btn;
|
|
75
89
|
break;
|
|
76
90
|
}
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
if (possibleSubmitBtn) {
|
|
80
|
-
const items =
|
|
81
|
-
.filter(el => el.tagName !== 'BUTTON' && !el.closest('button'));
|
|
94
|
+
const items = getInteractiveItems(container);
|
|
82
95
|
|
|
83
96
|
if (items.length > 1) {
|
|
84
97
|
targetList = container;
|
|
@@ -90,30 +103,38 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
90
103
|
|
|
91
104
|
if (!targetList || !submitBtn) return { found: false };
|
|
92
105
|
|
|
93
|
-
const items =
|
|
94
|
-
.filter(el => el.tagName !== 'BUTTON' && !el.closest('button'));
|
|
106
|
+
const items = getInteractiveItems(targetList);
|
|
95
107
|
if (items.length <= ${index}) return { found: false };
|
|
96
108
|
|
|
97
109
|
const targetOption = items[${index}];
|
|
98
110
|
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
+
const clickElement = (el) => {
|
|
112
|
+
const rect = el.getBoundingClientRect();
|
|
113
|
+
const clickX = rect.left + rect.width / 2;
|
|
114
|
+
const clickY = rect.top + rect.height / 2;
|
|
115
|
+
const events = ['pointerdown', 'mousedown', 'mouseup', 'click'];
|
|
116
|
+
for (const type of events) {
|
|
117
|
+
el.dispatchEvent(new MouseEvent(type, {
|
|
118
|
+
bubbles: true,
|
|
119
|
+
cancelable: true,
|
|
120
|
+
view: window,
|
|
121
|
+
clientX: clickX,
|
|
122
|
+
clientY: clickY,
|
|
123
|
+
}));
|
|
111
124
|
}
|
|
112
125
|
};
|
|
126
|
+
|
|
127
|
+
return new Promise(resolve => {
|
|
128
|
+
clickElement(targetOption);
|
|
129
|
+
setTimeout(() => {
|
|
130
|
+
clickElement(submitBtn);
|
|
131
|
+
resolve({ found: true });
|
|
132
|
+
}, 50);
|
|
133
|
+
});
|
|
113
134
|
})()
|
|
114
135
|
`,
|
|
115
136
|
returnByValue: true,
|
|
116
|
-
awaitPromise:
|
|
137
|
+
awaitPromise: true,
|
|
117
138
|
};
|
|
118
139
|
if (contextId !== null) {
|
|
119
140
|
callParams.contextId = contextId;
|
|
@@ -124,37 +145,6 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
124
145
|
this.logger.warn(`[QuestionDetector:${this.projectName}] Could not find question modal elements during submission.`);
|
|
125
146
|
return false;
|
|
126
147
|
}
|
|
127
|
-
// Click the option
|
|
128
|
-
await this.cdp.call('Input.dispatchMouseEvent', {
|
|
129
|
-
type: 'mousePressed',
|
|
130
|
-
x: result.option.x,
|
|
131
|
-
y: result.option.y,
|
|
132
|
-
button: 'left',
|
|
133
|
-
clickCount: 1,
|
|
134
|
-
});
|
|
135
|
-
await this.cdp.call('Input.dispatchMouseEvent', {
|
|
136
|
-
type: 'mouseReleased',
|
|
137
|
-
x: result.option.x,
|
|
138
|
-
y: result.option.y,
|
|
139
|
-
button: 'left',
|
|
140
|
-
clickCount: 1,
|
|
141
|
-
});
|
|
142
|
-
await new Promise(resolve => setTimeout(resolve, 50));
|
|
143
|
-
// Click the submit button
|
|
144
|
-
await this.cdp.call('Input.dispatchMouseEvent', {
|
|
145
|
-
type: 'mousePressed',
|
|
146
|
-
x: result.button.x,
|
|
147
|
-
y: result.button.y,
|
|
148
|
-
button: 'left',
|
|
149
|
-
clickCount: 1,
|
|
150
|
-
});
|
|
151
|
-
await this.cdp.call('Input.dispatchMouseEvent', {
|
|
152
|
-
type: 'mouseReleased',
|
|
153
|
-
x: result.button.x,
|
|
154
|
-
y: result.button.y,
|
|
155
|
-
button: 'left',
|
|
156
|
-
clickCount: 1,
|
|
157
|
-
});
|
|
158
148
|
this.lastQuestionDetected = false;
|
|
159
149
|
return true;
|
|
160
150
|
}
|
|
@@ -170,12 +160,26 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
170
160
|
const callParams = {
|
|
171
161
|
expression: `
|
|
172
162
|
(() => {
|
|
163
|
+
const getInteractiveItems = (elContainer) => {
|
|
164
|
+
return Array.from(elContainer.querySelectorAll('li, label, a, [role="radio"], [role="option"], [class*="cursor-pointer"]'))
|
|
165
|
+
.filter(el => {
|
|
166
|
+
if (el.tagName === 'BUTTON' || el.closest('button')) return false;
|
|
167
|
+
const role = el.getAttribute('role');
|
|
168
|
+
if (['radio', 'option', 'checkbox', 'button', 'menuitem'].includes(role)) return true;
|
|
169
|
+
if (el.tagName === 'A' || el.tagName === 'LABEL') return true;
|
|
170
|
+
const style = window.getComputedStyle(el);
|
|
171
|
+
return style.cursor === 'pointer';
|
|
172
|
+
});
|
|
173
|
+
};
|
|
174
|
+
|
|
173
175
|
const containers = Array.from(document.querySelectorAll('div, form, dialog')).reverse();
|
|
174
176
|
let skipBtn = null;
|
|
175
177
|
|
|
176
178
|
for (const container of containers) {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
+
const hasTextInput = container.querySelector('textarea, input:not([type="hidden"]):not([type="radio"]):not([type="checkbox"]), [contenteditable="true"], [contenteditable=""], [role="textbox"]');
|
|
180
|
+
const hasExplicitQuestionItems = container.querySelector('[role="radio"], input[type="radio"], input[type="checkbox"], [role="option"], [role="checkbox"]');
|
|
181
|
+
if (hasTextInput && !hasExplicitQuestionItems) continue;
|
|
182
|
+
const items = getInteractiveItems(container);
|
|
179
183
|
const hasList = items.length > 1;
|
|
180
184
|
|
|
181
185
|
const buttons = Array.from(container.querySelectorAll('button'));
|
|
@@ -191,14 +195,24 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
191
195
|
|
|
192
196
|
if (!skipBtn) return { found: false };
|
|
193
197
|
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
const clickElement = (el) => {
|
|
199
|
+
const rect = el.getBoundingClientRect();
|
|
200
|
+
const clickX = rect.left + rect.width / 2;
|
|
201
|
+
const clickY = rect.top + rect.height / 2;
|
|
202
|
+
const events = ['pointerdown', 'mousedown', 'mouseup', 'click'];
|
|
203
|
+
for (const type of events) {
|
|
204
|
+
el.dispatchEvent(new MouseEvent(type, {
|
|
205
|
+
bubbles: true,
|
|
206
|
+
cancelable: true,
|
|
207
|
+
view: window,
|
|
208
|
+
clientX: clickX,
|
|
209
|
+
clientY: clickY,
|
|
210
|
+
}));
|
|
200
211
|
}
|
|
201
212
|
};
|
|
213
|
+
|
|
214
|
+
clickElement(skipBtn);
|
|
215
|
+
return { found: true };
|
|
202
216
|
})()
|
|
203
217
|
`,
|
|
204
218
|
returnByValue: true,
|
|
@@ -212,20 +226,6 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
212
226
|
this.logger.warn(`[QuestionDetector:${this.projectName}] Could not find skip button.`);
|
|
213
227
|
return false;
|
|
214
228
|
}
|
|
215
|
-
await this.cdp.call('Input.dispatchMouseEvent', {
|
|
216
|
-
type: 'mousePressed',
|
|
217
|
-
x: result.button.x,
|
|
218
|
-
y: result.button.y,
|
|
219
|
-
button: 'left',
|
|
220
|
-
clickCount: 1,
|
|
221
|
-
});
|
|
222
|
-
await this.cdp.call('Input.dispatchMouseEvent', {
|
|
223
|
-
type: 'mouseReleased',
|
|
224
|
-
x: result.button.x,
|
|
225
|
-
y: result.button.y,
|
|
226
|
-
button: 'left',
|
|
227
|
-
clickCount: 1,
|
|
228
|
-
});
|
|
229
229
|
this.lastQuestionDetected = false;
|
|
230
230
|
return true;
|
|
231
231
|
}
|
|
@@ -241,24 +241,73 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
241
241
|
const callParams = {
|
|
242
242
|
expression: `
|
|
243
243
|
(() => {
|
|
244
|
+
const STOP_PATTERNS = [
|
|
245
|
+
/^stop$/,
|
|
246
|
+
/^stop generating$/,
|
|
247
|
+
/^stop response$/,
|
|
248
|
+
/^停止$/,
|
|
249
|
+
/^生成を停止$/,
|
|
250
|
+
/^応答を停止$/,
|
|
251
|
+
];
|
|
252
|
+
const normalize = (value) => (value || '').toLowerCase().replace(/\\s+/g, ' ').trim();
|
|
253
|
+
const isStopLabel = (value) => {
|
|
254
|
+
const normalized = normalize(value);
|
|
255
|
+
if (!normalized) return false;
|
|
256
|
+
return STOP_PATTERNS.some((re) => re.test(normalized));
|
|
257
|
+
};
|
|
258
|
+
const isGenerating = Array.from(document.querySelectorAll('button, [role="button"]')).some(btn => {
|
|
259
|
+
const labels = [
|
|
260
|
+
btn.textContent || '',
|
|
261
|
+
btn.getAttribute('aria-label') || '',
|
|
262
|
+
btn.getAttribute('title') || '',
|
|
263
|
+
];
|
|
264
|
+
return labels.some(isStopLabel);
|
|
265
|
+
}) || (() => {
|
|
266
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
267
|
+
if (panel) {
|
|
268
|
+
const panelText = (panel.textContent || '').trim();
|
|
269
|
+
return /Working\.\s*$/i.test(panelText);
|
|
270
|
+
}
|
|
271
|
+
return false;
|
|
272
|
+
})();
|
|
273
|
+
if (isGenerating) {
|
|
274
|
+
return { detected: false, reason: "IDE is generating" };
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const getInteractiveItems = (elContainer) => {
|
|
278
|
+
return Array.from(elContainer.querySelectorAll('li, label, a, [role="radio"], [role="option"], [class*="cursor-pointer"]'))
|
|
279
|
+
.filter(el => {
|
|
280
|
+
if (el.tagName === 'BUTTON' || el.closest('button')) return false;
|
|
281
|
+
const text = (el.innerText || el.textContent || '').trim();
|
|
282
|
+
if (!text) return false;
|
|
283
|
+
const role = el.getAttribute('role');
|
|
284
|
+
if (['radio', 'option', 'checkbox', 'button', 'menuitem'].includes(role)) return true;
|
|
285
|
+
if (el.tagName === 'A' || el.tagName === 'LABEL') return true;
|
|
286
|
+
const style = window.getComputedStyle(el);
|
|
287
|
+
return style.cursor === 'pointer';
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
|
|
244
291
|
const containers = Array.from(document.querySelectorAll('div, form, dialog')).reverse();
|
|
245
292
|
let targetList = null;
|
|
246
293
|
let submitBtn = null;
|
|
247
294
|
|
|
248
295
|
for (const container of containers) {
|
|
296
|
+
const hasTextInput = container.querySelector('textarea, input:not([type="hidden"]):not([type="radio"]):not([type="checkbox"]), [contenteditable], [role="textbox"], .monaco-editor, .editor-container, .inputarea, vscode-text-field, vscode-text-area');
|
|
297
|
+
const hasExplicitQuestionItems = container.querySelector('[role="radio"], input[type="radio"], input[type="checkbox"], [role="option"], [role="checkbox"]');
|
|
298
|
+
if (hasTextInput && !hasExplicitQuestionItems) continue;
|
|
249
299
|
const buttons = Array.from(container.querySelectorAll('button'));
|
|
250
300
|
let possibleSubmitBtn = null;
|
|
251
301
|
for (const btn of buttons) {
|
|
252
302
|
const text = btn.textContent?.toLowerCase() || '';
|
|
253
|
-
if (text.includes('submit')) {
|
|
303
|
+
if (text.includes('submit') || text.includes('continue')) {
|
|
254
304
|
possibleSubmitBtn = btn;
|
|
255
305
|
break;
|
|
256
306
|
}
|
|
257
307
|
}
|
|
258
308
|
|
|
259
309
|
if (possibleSubmitBtn) {
|
|
260
|
-
const items =
|
|
261
|
-
.filter(el => el.tagName !== 'BUTTON' && !el.closest('button'));
|
|
310
|
+
const items = getInteractiveItems(container);
|
|
262
311
|
|
|
263
312
|
if (items.length > 1) {
|
|
264
313
|
targetList = container;
|
|
@@ -280,8 +329,7 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
280
329
|
}
|
|
281
330
|
const title = titleEl ? (titleEl.innerText || titleEl.textContent || '').trim() : 'Question';
|
|
282
331
|
|
|
283
|
-
const items =
|
|
284
|
-
.filter(el => el.tagName !== 'BUTTON' && !el.closest('button'));
|
|
332
|
+
const items = getInteractiveItems(targetList);
|
|
285
333
|
const options = items.map(n => {
|
|
286
334
|
const rect = n.getBoundingClientRect();
|
|
287
335
|
const finalLabel = n.innerText || n.textContent || 'Option';
|
|
@@ -321,6 +369,8 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
321
369
|
this.logger.debug(`[QuestionDetector] Error evaluating on ctx ${contextId}: ${e.message}`);
|
|
322
370
|
return null;
|
|
323
371
|
});
|
|
372
|
+
if (!this._isStarted)
|
|
373
|
+
return;
|
|
324
374
|
if (response?.exceptionDetails) {
|
|
325
375
|
this.logger.debug(`[QuestionDetector] Exception on ctx ${contextId}: ${response.exceptionDetails.exception?.description || response.exceptionDetails.text}`);
|
|
326
376
|
}
|
|
@@ -336,6 +386,8 @@ class QuestionDetector extends events_1.EventEmitter {
|
|
|
336
386
|
lastReason = result.reason;
|
|
337
387
|
}
|
|
338
388
|
}
|
|
389
|
+
if (!this._isStarted)
|
|
390
|
+
return;
|
|
339
391
|
const result = detectedResult;
|
|
340
392
|
if (result && result.detected) {
|
|
341
393
|
this.emptyPollCount = 0;
|
|
@@ -146,6 +146,13 @@ exports.RESPONSE_SELECTORS = {
|
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
if (panel) {
|
|
150
|
+
const panelText = (panel.textContent || '').trim();
|
|
151
|
+
if (/Working\.\s*$/i.test(panelText)) {
|
|
152
|
+
return { isGenerating: true };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
149
156
|
return { isGenerating: false };
|
|
150
157
|
})()`,
|
|
151
158
|
/** Click stop button via tooltip-id + text fallback */
|
|
@@ -629,6 +636,7 @@ class ResponseMonitor {
|
|
|
629
636
|
onCdpReconnectFailed = null;
|
|
630
637
|
// Activity-based timeout (#49)
|
|
631
638
|
lastActivityTime = 0;
|
|
639
|
+
startTime = 0;
|
|
632
640
|
constructor(options) {
|
|
633
641
|
this.cdpService = options.cdpService;
|
|
634
642
|
this.pollIntervalMs = options.pollIntervalMs ?? 2000;
|
|
@@ -731,6 +739,7 @@ class ResponseMonitor {
|
|
|
731
739
|
}
|
|
732
740
|
// Activity-based timeout: track last activity time instead of fixed timer (#49)
|
|
733
741
|
this.lastActivityTime = Date.now();
|
|
742
|
+
this.startTime = Date.now();
|
|
734
743
|
// Register CDP connection event listeners (#48)
|
|
735
744
|
this.registerCdpConnectionListeners();
|
|
736
745
|
const mode = passive ? 'Passive monitoring' : 'Monitoring';
|
|
@@ -1109,7 +1118,14 @@ class ResponseMonitor {
|
|
|
1109
1118
|
// Guard: never timeout while the stop button is visible — it means
|
|
1110
1119
|
// Antigravity is still actively generating (extended thinking, long
|
|
1111
1120
|
// shell commands, large file operations, etc.).
|
|
1112
|
-
|
|
1121
|
+
// Hard timeout limit (20 minutes absolute hard timeout)
|
|
1122
|
+
const hardTimeoutLimit = 1200000;
|
|
1123
|
+
const totalElapsed = Date.now() - this.startTime;
|
|
1124
|
+
const isHardTimeout = totalElapsed >= hardTimeoutLimit;
|
|
1125
|
+
if (isHardTimeout || (this.maxDurationMs > 0 && !isGenerating && Date.now() - this.lastActivityTime >= this.maxDurationMs)) {
|
|
1126
|
+
if (isHardTimeout) {
|
|
1127
|
+
logger_1.logger.warn(`[ResponseMonitor] Hard timeout of 20 minutes reached.`);
|
|
1128
|
+
}
|
|
1113
1129
|
const lastText = this.lastText ?? '';
|
|
1114
1130
|
this.setPhase('timeout', lastText);
|
|
1115
1131
|
await this.stop();
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.fileOpenCache = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* In-memory cache to map button customIds to file URLs.
|
|
6
|
-
* Bounded to a maximum size to prevent memory leaks,
|
|
6
|
+
* Bounded to a maximum size to prevent memory leaks, using a FIFO eviction policy when the cache size limit is reached.
|
|
7
7
|
*/
|
|
8
8
|
class BoundedCache {
|
|
9
9
|
max_size;
|
|
@@ -23,11 +23,7 @@ class BoundedCache {
|
|
|
23
23
|
this.map.set(key, value);
|
|
24
24
|
}
|
|
25
25
|
get(key) {
|
|
26
|
-
|
|
27
|
-
if (value !== undefined) {
|
|
28
|
-
this.map.delete(key); // Remove upon consumption
|
|
29
|
-
}
|
|
30
|
-
return value;
|
|
26
|
+
return this.map.get(key);
|
|
31
27
|
}
|
|
32
28
|
}
|
|
33
29
|
exports.fileOpenCache = new BoundedCache(1000);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.parseQuestionCustomId = parseQuestionCustomId;
|
|
4
|
+
exports.executeBrowserClick = executeBrowserClick;
|
|
5
|
+
const logger_1 = require("./logger");
|
|
6
|
+
const approvalDetector_1 = require("../services/approvalDetector");
|
|
4
7
|
function parseQuestionCustomId(customId, expectedPrefix) {
|
|
5
8
|
if (customId !== expectedPrefix && !customId.startsWith(expectedPrefix + ':'))
|
|
6
9
|
return null;
|
|
@@ -23,3 +26,22 @@ function parseQuestionCustomId(customId, expectedPrefix) {
|
|
|
23
26
|
}
|
|
24
27
|
return result;
|
|
25
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* Executes a click on an element matching the target text in the IDE.
|
|
31
|
+
* Returns true if the click was successfully dispatched, false otherwise.
|
|
32
|
+
*/
|
|
33
|
+
async function executeBrowserClick(cdp, buttonText) {
|
|
34
|
+
try {
|
|
35
|
+
const script = (0, approvalDetector_1.buildClickScript)(buttonText);
|
|
36
|
+
const evalResult = await cdp.call('Runtime.evaluate', {
|
|
37
|
+
expression: script,
|
|
38
|
+
returnByValue: true,
|
|
39
|
+
awaitPromise: true,
|
|
40
|
+
});
|
|
41
|
+
return !!evalResult?.result?.value?.ok;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
logger_1.logger.error(`[executeBrowserClick] Error clicking button "${buttonText}":`, error);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazy-gravity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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": {
|