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,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveSafePath = void 0;
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const resolveSafePath = (inputPath, baseDir) => {
|
|
9
|
+
const resolvedPath = path_1.default.resolve(baseDir, inputPath);
|
|
10
|
+
const normalizedBaseDir = path_1.default.resolve(baseDir);
|
|
11
|
+
const relative = path_1.default.relative(normalizedBaseDir, resolvedPath);
|
|
12
|
+
if (relative && (relative.startsWith('..' + path_1.default.sep) || relative === '..')) {
|
|
13
|
+
throw new Error('Path traversal detected');
|
|
14
|
+
}
|
|
15
|
+
if (path_1.default.isAbsolute(relative)) {
|
|
16
|
+
throw new Error('Path traversal detected');
|
|
17
|
+
}
|
|
18
|
+
return resolvedPath;
|
|
19
|
+
};
|
|
20
|
+
exports.resolveSafePath = resolveSafePath;
|
|
@@ -0,0 +1,89 @@
|
|
|
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.ensureAntigravityRunning = ensureAntigravityRunning;
|
|
37
|
+
const logger_1 = require("../utils/logger");
|
|
38
|
+
const cdpPorts_1 = require("../utils/cdpPorts");
|
|
39
|
+
const http = __importStar(require("http"));
|
|
40
|
+
/**
|
|
41
|
+
* Check if CDP responds on the specified port.
|
|
42
|
+
*/
|
|
43
|
+
function checkPort(port) {
|
|
44
|
+
return new Promise((resolve) => {
|
|
45
|
+
const req = http.get(`http://127.0.0.1:${port}/json/list`, (res) => {
|
|
46
|
+
let data = '';
|
|
47
|
+
res.on('data', (chunk) => (data += chunk));
|
|
48
|
+
res.on('end', () => {
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(data);
|
|
51
|
+
resolve(Array.isArray(parsed));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
resolve(false);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
req.on('error', () => resolve(false));
|
|
59
|
+
req.setTimeout(2000, () => {
|
|
60
|
+
req.destroy();
|
|
61
|
+
resolve(false);
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if Antigravity is running with CDP ports.
|
|
67
|
+
* If not running, output a warning log (no auto-start or restart).
|
|
68
|
+
*
|
|
69
|
+
* Called during Bot initialization.
|
|
70
|
+
*/
|
|
71
|
+
async function ensureAntigravityRunning() {
|
|
72
|
+
logger_1.logger.info('[AntigravityLauncher] Checking CDP ports...');
|
|
73
|
+
for (const port of cdpPorts_1.CDP_PORTS) {
|
|
74
|
+
if (await checkPort(port)) {
|
|
75
|
+
logger_1.logger.info(`[AntigravityLauncher] OK — Port ${port} responding`);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
logger_1.logger.warn('');
|
|
80
|
+
logger_1.logger.warn('='.repeat(70));
|
|
81
|
+
logger_1.logger.warn(' Antigravity CDP ports are not responding');
|
|
82
|
+
logger_1.logger.warn('');
|
|
83
|
+
logger_1.logger.warn(' Please run AntigravityDebug.command before starting the Bot');
|
|
84
|
+
logger_1.logger.warn('');
|
|
85
|
+
logger_1.logger.warn(' Or manually:');
|
|
86
|
+
logger_1.logger.warn(' open -a Antigravity --args --remote-debugging-port=9222');
|
|
87
|
+
logger_1.logger.warn('='.repeat(70));
|
|
88
|
+
logger_1.logger.warn('');
|
|
89
|
+
}
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ApprovalDetector = void 0;
|
|
4
|
+
const logger_1 = require("../utils/logger");
|
|
5
|
+
/**
|
|
6
|
+
* Approval button detection script for the Antigravity UI
|
|
7
|
+
*
|
|
8
|
+
* Detects allow/deny button pairs and extracts descriptions with fallbacks.
|
|
9
|
+
*/
|
|
10
|
+
const DETECT_APPROVAL_SCRIPT = `(() => {
|
|
11
|
+
const ALLOW_ONCE_PATTERNS = ['allow once', 'allow one time', '今回のみ許可', '1回のみ許可', '一度許可'];
|
|
12
|
+
const ALWAYS_ALLOW_PATTERNS = [
|
|
13
|
+
'allow this conversation',
|
|
14
|
+
'allow this chat',
|
|
15
|
+
'always allow',
|
|
16
|
+
'常に許可',
|
|
17
|
+
'この会話を許可',
|
|
18
|
+
];
|
|
19
|
+
const ALLOW_PATTERNS = ['allow', 'permit', '許可', '承認', '確認'];
|
|
20
|
+
const DENY_PATTERNS = ['deny', '拒否', 'decline'];
|
|
21
|
+
|
|
22
|
+
const normalize = (text) => (text || '').toLowerCase().replace(/\\s+/g, ' ').trim();
|
|
23
|
+
|
|
24
|
+
const allButtons = Array.from(document.querySelectorAll('button'))
|
|
25
|
+
.filter(btn => btn.offsetParent !== null);
|
|
26
|
+
|
|
27
|
+
let approveBtn = allButtons.find(btn => {
|
|
28
|
+
const t = normalize(btn.textContent || '');
|
|
29
|
+
return ALLOW_ONCE_PATTERNS.some(p => t.includes(p));
|
|
30
|
+
}) || null;
|
|
31
|
+
|
|
32
|
+
if (!approveBtn) {
|
|
33
|
+
approveBtn = allButtons.find(btn => {
|
|
34
|
+
const t = normalize(btn.textContent || '');
|
|
35
|
+
const isAlways = ALWAYS_ALLOW_PATTERNS.some(p => t.includes(p));
|
|
36
|
+
return !isAlways && ALLOW_PATTERNS.some(p => t.includes(p));
|
|
37
|
+
}) || null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!approveBtn) return null;
|
|
41
|
+
|
|
42
|
+
const container = approveBtn.closest('[role="dialog"], .modal, .dialog, .approval-container, .permission-dialog')
|
|
43
|
+
|| approveBtn.parentElement?.parentElement
|
|
44
|
+
|| approveBtn.parentElement
|
|
45
|
+
|| document.body;
|
|
46
|
+
|
|
47
|
+
const containerButtons = Array.from(container.querySelectorAll('button'))
|
|
48
|
+
.filter(btn => btn.offsetParent !== null);
|
|
49
|
+
|
|
50
|
+
const denyBtn = containerButtons.find(btn => {
|
|
51
|
+
const t = normalize(btn.textContent || '');
|
|
52
|
+
return DENY_PATTERNS.some(p => t.includes(p));
|
|
53
|
+
}) || null;
|
|
54
|
+
|
|
55
|
+
if (!denyBtn) return null;
|
|
56
|
+
|
|
57
|
+
const alwaysAllowBtn = containerButtons.find(btn => {
|
|
58
|
+
const t = normalize(btn.textContent || '');
|
|
59
|
+
return ALWAYS_ALLOW_PATTERNS.some(p => t.includes(p));
|
|
60
|
+
}) || null;
|
|
61
|
+
|
|
62
|
+
const approveText = (approveBtn.textContent || '').trim();
|
|
63
|
+
const alwaysAllowText = alwaysAllowBtn ? (alwaysAllowBtn.textContent || '').trim() : '';
|
|
64
|
+
const denyText = (denyBtn.textContent || '').trim();
|
|
65
|
+
|
|
66
|
+
// Description extraction (multiple fallbacks)
|
|
67
|
+
let description = '';
|
|
68
|
+
|
|
69
|
+
// 1. p or .description inside dialog/modal
|
|
70
|
+
const dialog = container;
|
|
71
|
+
if (dialog) {
|
|
72
|
+
const descEl = dialog.querySelector('p, .description, [data-testid="description"]');
|
|
73
|
+
if (descEl) {
|
|
74
|
+
description = (descEl.textContent || '').trim();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 2. Parent element text (excluding button text)
|
|
79
|
+
if (!description) {
|
|
80
|
+
const parent = approveBtn.parentElement?.parentElement || approveBtn.parentElement;
|
|
81
|
+
if (parent) {
|
|
82
|
+
const clone = parent.cloneNode(true);
|
|
83
|
+
const buttons = clone.querySelectorAll('button');
|
|
84
|
+
buttons.forEach(b => b.remove());
|
|
85
|
+
const parentText = (clone.textContent || '').trim();
|
|
86
|
+
if (parentText.length > 5 && parentText.length < 500) {
|
|
87
|
+
description = parentText;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// 3. aria-label fallback
|
|
93
|
+
if (!description) {
|
|
94
|
+
const ariaLabel = approveBtn.getAttribute('aria-label') || '';
|
|
95
|
+
if (ariaLabel) description = ariaLabel;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { approveText, alwaysAllowText, denyText, description };
|
|
99
|
+
})()`;
|
|
100
|
+
/**
|
|
101
|
+
* Press the toggle on the right side of Allow Once to expand the Always Allow dropdown.
|
|
102
|
+
*/
|
|
103
|
+
const EXPAND_ALWAYS_ALLOW_MENU_SCRIPT = `(() => {
|
|
104
|
+
const ALLOW_ONCE_PATTERNS = ['allow once', 'allow one time', '今回のみ許可', '1回のみ許可', '一度許可'];
|
|
105
|
+
const ALWAYS_ALLOW_PATTERNS = [
|
|
106
|
+
'allow this conversation',
|
|
107
|
+
'allow this chat',
|
|
108
|
+
'always allow',
|
|
109
|
+
'常に許可',
|
|
110
|
+
'この会話を許可',
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
const normalize = (text) => (text || '').toLowerCase().replace(/\\s+/g, ' ').trim();
|
|
114
|
+
const visibleButtons = Array.from(document.querySelectorAll('button'))
|
|
115
|
+
.filter(btn => btn.offsetParent !== null);
|
|
116
|
+
|
|
117
|
+
const directAlways = visibleButtons.find(btn => {
|
|
118
|
+
const t = normalize(btn.textContent || '');
|
|
119
|
+
return ALWAYS_ALLOW_PATTERNS.some(p => t.includes(p));
|
|
120
|
+
});
|
|
121
|
+
if (directAlways) return { ok: true, reason: 'already-visible' };
|
|
122
|
+
|
|
123
|
+
const allowOnceBtn = visibleButtons.find(btn => {
|
|
124
|
+
const t = normalize(btn.textContent || '');
|
|
125
|
+
return ALLOW_ONCE_PATTERNS.some(p => t.includes(p));
|
|
126
|
+
});
|
|
127
|
+
if (!allowOnceBtn) return { ok: false, error: 'allow-once button not found' };
|
|
128
|
+
|
|
129
|
+
const container = allowOnceBtn.closest('[role="dialog"], .modal, .dialog, .approval-container, .permission-dialog')
|
|
130
|
+
|| allowOnceBtn.parentElement?.parentElement
|
|
131
|
+
|| allowOnceBtn.parentElement
|
|
132
|
+
|| document.body;
|
|
133
|
+
|
|
134
|
+
const containerButtons = Array.from(container.querySelectorAll('button'))
|
|
135
|
+
.filter(btn => btn.offsetParent !== null);
|
|
136
|
+
|
|
137
|
+
const toggleBtn = containerButtons.find(btn => {
|
|
138
|
+
if (btn === allowOnceBtn) return false;
|
|
139
|
+
const text = normalize(btn.textContent || '');
|
|
140
|
+
const aria = normalize(btn.getAttribute('aria-label') || '');
|
|
141
|
+
const hasPopup = btn.getAttribute('aria-haspopup');
|
|
142
|
+
if (hasPopup === 'menu' || hasPopup === 'listbox') return true;
|
|
143
|
+
if (text === '') return true;
|
|
144
|
+
return /menu|more|expand|options|dropdown|chevron|arrow/.test(aria);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
if (toggleBtn) {
|
|
148
|
+
toggleBtn.click();
|
|
149
|
+
return { ok: true, reason: 'toggle-button' };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const rect = allowOnceBtn.getBoundingClientRect();
|
|
153
|
+
if (!rect || rect.width <= 0 || rect.height <= 0) {
|
|
154
|
+
return { ok: false, error: 'allow-once button rect unavailable' };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const clickX = rect.right - Math.max(4, Math.min(12, rect.width * 0.15));
|
|
158
|
+
const clickY = rect.top + rect.height / 2;
|
|
159
|
+
|
|
160
|
+
const events = ['pointerdown', 'mousedown', 'mouseup', 'click'];
|
|
161
|
+
for (const type of events) {
|
|
162
|
+
allowOnceBtn.dispatchEvent(new MouseEvent(type, {
|
|
163
|
+
bubbles: true,
|
|
164
|
+
cancelable: true,
|
|
165
|
+
view: window,
|
|
166
|
+
clientX: clickX,
|
|
167
|
+
clientY: clickY,
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
return { ok: true, reason: 'allow-once-right-edge' };
|
|
171
|
+
})()`;
|
|
172
|
+
/**
|
|
173
|
+
* Generate a CDP script that clicks a button
|
|
174
|
+
*
|
|
175
|
+
* @param buttonText Text of the button to click
|
|
176
|
+
*/
|
|
177
|
+
function buildClickScript(buttonText) {
|
|
178
|
+
const safeText = JSON.stringify(buttonText);
|
|
179
|
+
return `(() => {
|
|
180
|
+
const normalize = (text) => (text || '').toLowerCase().replace(/\\s+/g, ' ').trim();
|
|
181
|
+
const text = ${safeText};
|
|
182
|
+
const wanted = normalize(text);
|
|
183
|
+
const allButtons = Array.from(document.querySelectorAll('button'));
|
|
184
|
+
const target = allButtons.find(btn => {
|
|
185
|
+
if (!btn.offsetParent) return false;
|
|
186
|
+
const buttonText = normalize(btn.textContent || '');
|
|
187
|
+
const ariaLabel = normalize(btn.getAttribute('aria-label') || '');
|
|
188
|
+
return buttonText === wanted ||
|
|
189
|
+
ariaLabel === wanted ||
|
|
190
|
+
buttonText.includes(wanted) ||
|
|
191
|
+
ariaLabel.includes(wanted);
|
|
192
|
+
});
|
|
193
|
+
if (!target) return { ok: false, error: 'Button not found: ' + text };
|
|
194
|
+
target.click();
|
|
195
|
+
return { ok: true };
|
|
196
|
+
})()`;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Class that detects approval buttons in the Antigravity UI via polling.
|
|
200
|
+
*
|
|
201
|
+
* Notifies detected button info through the onApprovalRequired callback,
|
|
202
|
+
* and performs the actual click operations via approveButton() / denyButton() methods.
|
|
203
|
+
*/
|
|
204
|
+
class ApprovalDetector {
|
|
205
|
+
cdpService;
|
|
206
|
+
pollIntervalMs;
|
|
207
|
+
onApprovalRequired;
|
|
208
|
+
pollTimer = null;
|
|
209
|
+
isRunning = false;
|
|
210
|
+
/** Key of the last detected button info (for duplicate notification prevention) */
|
|
211
|
+
lastDetectedKey = null;
|
|
212
|
+
/** Full ApprovalInfo from the last detection (used for clicking) */
|
|
213
|
+
lastDetectedInfo = null;
|
|
214
|
+
constructor(options) {
|
|
215
|
+
this.cdpService = options.cdpService;
|
|
216
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 1500;
|
|
217
|
+
this.onApprovalRequired = options.onApprovalRequired;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Start monitoring.
|
|
221
|
+
*/
|
|
222
|
+
start() {
|
|
223
|
+
if (this.isRunning)
|
|
224
|
+
return;
|
|
225
|
+
this.isRunning = true;
|
|
226
|
+
this.lastDetectedKey = null;
|
|
227
|
+
this.lastDetectedInfo = null;
|
|
228
|
+
this.schedulePoll();
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Stop monitoring.
|
|
232
|
+
*/
|
|
233
|
+
async stop() {
|
|
234
|
+
this.isRunning = false;
|
|
235
|
+
if (this.pollTimer) {
|
|
236
|
+
clearTimeout(this.pollTimer);
|
|
237
|
+
this.pollTimer = null;
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Return the last detected approval button info.
|
|
242
|
+
* Returns null if nothing has been detected.
|
|
243
|
+
*/
|
|
244
|
+
getLastDetectedInfo() {
|
|
245
|
+
return this.lastDetectedInfo;
|
|
246
|
+
}
|
|
247
|
+
/** Schedule the next poll */
|
|
248
|
+
schedulePoll() {
|
|
249
|
+
if (!this.isRunning)
|
|
250
|
+
return;
|
|
251
|
+
this.pollTimer = setTimeout(async () => {
|
|
252
|
+
await this.poll();
|
|
253
|
+
if (this.isRunning) {
|
|
254
|
+
this.schedulePoll();
|
|
255
|
+
}
|
|
256
|
+
}, this.pollIntervalMs);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Single poll iteration:
|
|
260
|
+
* 1. Get approval button info from DOM (with contextId)
|
|
261
|
+
* 2. Notify via callback only on new detection (prevent duplicates)
|
|
262
|
+
* 3. Reset lastDetectedKey / lastDetectedInfo when buttons disappear
|
|
263
|
+
*/
|
|
264
|
+
async poll() {
|
|
265
|
+
try {
|
|
266
|
+
const contextId = this.cdpService.getPrimaryContextId();
|
|
267
|
+
const callParams = {
|
|
268
|
+
expression: DETECT_APPROVAL_SCRIPT,
|
|
269
|
+
returnByValue: true,
|
|
270
|
+
awaitPromise: false,
|
|
271
|
+
};
|
|
272
|
+
if (contextId !== null) {
|
|
273
|
+
callParams.contextId = contextId;
|
|
274
|
+
}
|
|
275
|
+
const result = await this.cdpService.call('Runtime.evaluate', callParams);
|
|
276
|
+
const info = result?.result?.value ?? null;
|
|
277
|
+
if (info) {
|
|
278
|
+
// Duplicate prevention: use approveText + description combination as key
|
|
279
|
+
const key = `${info.approveText}::${info.description}`;
|
|
280
|
+
if (key !== this.lastDetectedKey) {
|
|
281
|
+
this.lastDetectedKey = key;
|
|
282
|
+
this.lastDetectedInfo = info;
|
|
283
|
+
this.onApprovalRequired(info);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
else {
|
|
287
|
+
// Reset when buttons disappear (prepare for next approval detection)
|
|
288
|
+
this.lastDetectedKey = null;
|
|
289
|
+
this.lastDetectedInfo = null;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
// Ignore CDP errors and continue monitoring
|
|
294
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
295
|
+
if (message.includes('WebSocket is not connected')) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
logger_1.logger.error('[ApprovalDetector] Error during polling:', error);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Click the approve button with the specified text via CDP.
|
|
303
|
+
* @param buttonText Text of the button to click (default: detected approveText or "Allow")
|
|
304
|
+
* @returns true if click succeeded
|
|
305
|
+
*/
|
|
306
|
+
async approveButton(buttonText) {
|
|
307
|
+
const text = buttonText ?? this.lastDetectedInfo?.approveText ?? 'Allow';
|
|
308
|
+
return this.clickButton(text);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Select "Allow This Conversation / Always Allow".
|
|
312
|
+
* If the button is not directly visible, expand the Allow Once dropdown and select it.
|
|
313
|
+
*/
|
|
314
|
+
async alwaysAllowButton() {
|
|
315
|
+
const directCandidates = [
|
|
316
|
+
this.lastDetectedInfo?.alwaysAllowText,
|
|
317
|
+
'Allow This Conversation',
|
|
318
|
+
'Allow This Chat',
|
|
319
|
+
'この会話を許可',
|
|
320
|
+
'Always Allow',
|
|
321
|
+
'常に許可',
|
|
322
|
+
].filter((value) => typeof value === 'string' && value.trim().length > 0);
|
|
323
|
+
for (const candidate of directCandidates) {
|
|
324
|
+
if (await this.clickButton(candidate))
|
|
325
|
+
return true;
|
|
326
|
+
}
|
|
327
|
+
const expanded = await this.runEvaluateScript(EXPAND_ALWAYS_ALLOW_MENU_SCRIPT);
|
|
328
|
+
if (expanded?.ok !== true) {
|
|
329
|
+
return false;
|
|
330
|
+
}
|
|
331
|
+
for (let i = 0; i < 5; i++) {
|
|
332
|
+
for (const candidate of directCandidates) {
|
|
333
|
+
if (await this.clickButton(candidate))
|
|
334
|
+
return true;
|
|
335
|
+
}
|
|
336
|
+
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
337
|
+
}
|
|
338
|
+
return false;
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Click the deny button with the specified text via CDP.
|
|
342
|
+
* @param buttonText Text of the button to click (default: detected denyText or "Deny")
|
|
343
|
+
* @returns true if click succeeded
|
|
344
|
+
*/
|
|
345
|
+
async denyButton(buttonText) {
|
|
346
|
+
const text = buttonText ?? this.lastDetectedInfo?.denyText ?? 'Deny';
|
|
347
|
+
return this.clickButton(text);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Internal click handler (shared implementation for approveButton / denyButton).
|
|
351
|
+
* Specifies contextId to click in the correct execution context.
|
|
352
|
+
*/
|
|
353
|
+
async clickButton(buttonText) {
|
|
354
|
+
try {
|
|
355
|
+
const result = await this.runEvaluateScript(buildClickScript(buttonText));
|
|
356
|
+
return result?.ok === true;
|
|
357
|
+
}
|
|
358
|
+
catch (error) {
|
|
359
|
+
logger_1.logger.error('[ApprovalDetector] Error while clicking button:', error);
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Execute Runtime.evaluate with contextId and return result.value.
|
|
365
|
+
*/
|
|
366
|
+
async runEvaluateScript(expression) {
|
|
367
|
+
const contextId = this.cdpService.getPrimaryContextId();
|
|
368
|
+
const callParams = {
|
|
369
|
+
expression,
|
|
370
|
+
returnByValue: true,
|
|
371
|
+
awaitPromise: false,
|
|
372
|
+
};
|
|
373
|
+
if (contextId !== null) {
|
|
374
|
+
callParams.contextId = contextId;
|
|
375
|
+
}
|
|
376
|
+
const result = await this.cdpService.call('Runtime.evaluate', callParams);
|
|
377
|
+
return result?.result?.value;
|
|
378
|
+
}
|
|
379
|
+
/** Returns whether monitoring is currently active */
|
|
380
|
+
isActive() {
|
|
381
|
+
return this.isRunning;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
exports.ApprovalDetector = ApprovalDetector;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AutoAcceptService = void 0;
|
|
4
|
+
const i18n_1 = require("../utils/i18n");
|
|
5
|
+
class AutoAcceptService {
|
|
6
|
+
enabled;
|
|
7
|
+
constructor(initialEnabled = false) {
|
|
8
|
+
this.enabled = initialEnabled;
|
|
9
|
+
}
|
|
10
|
+
isEnabled() {
|
|
11
|
+
return this.enabled;
|
|
12
|
+
}
|
|
13
|
+
handle(rawAction) {
|
|
14
|
+
const action = this.normalizeAction(rawAction);
|
|
15
|
+
if (!action) {
|
|
16
|
+
return {
|
|
17
|
+
success: false,
|
|
18
|
+
enabled: this.enabled,
|
|
19
|
+
changed: false,
|
|
20
|
+
message: (0, i18n_1.t)('⚠️ Invalid argument. Usage: `/autoaccept [on/off/status]`'),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
if (action === 'status') {
|
|
24
|
+
return {
|
|
25
|
+
success: true,
|
|
26
|
+
enabled: this.enabled,
|
|
27
|
+
changed: false,
|
|
28
|
+
message: (0, i18n_1.t)(`⚙️ Auto-accept mode: **${this.enabled ? 'ON' : 'OFF'}**`),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (action === 'on') {
|
|
32
|
+
if (this.enabled) {
|
|
33
|
+
return {
|
|
34
|
+
success: true,
|
|
35
|
+
enabled: true,
|
|
36
|
+
changed: false,
|
|
37
|
+
message: (0, i18n_1.t)('ℹ️ Auto-accept mode is already **ON**.'),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
this.enabled = true;
|
|
41
|
+
return {
|
|
42
|
+
success: true,
|
|
43
|
+
enabled: true,
|
|
44
|
+
changed: true,
|
|
45
|
+
message: (0, i18n_1.t)('✅ Auto-accept mode turned **ON**. Future dialogs will be auto-allowed.'),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
if (!this.enabled) {
|
|
49
|
+
return {
|
|
50
|
+
success: true,
|
|
51
|
+
enabled: false,
|
|
52
|
+
changed: false,
|
|
53
|
+
message: (0, i18n_1.t)('ℹ️ Auto-accept mode is already **OFF**.'),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
this.enabled = false;
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
enabled: false,
|
|
60
|
+
changed: true,
|
|
61
|
+
message: (0, i18n_1.t)('✅ Auto-accept mode turned **OFF**. Returned to manual approval.'),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
normalizeAction(rawAction) {
|
|
65
|
+
if (!rawAction || rawAction.trim().length === 0)
|
|
66
|
+
return 'status';
|
|
67
|
+
const normalized = rawAction.trim().toLowerCase();
|
|
68
|
+
if (['on', 'enable', 'enabled', 'true', '1'].includes(normalized)) {
|
|
69
|
+
return 'on';
|
|
70
|
+
}
|
|
71
|
+
if (['off', 'disable', 'disabled', 'false', '0'].includes(normalized)) {
|
|
72
|
+
return 'off';
|
|
73
|
+
}
|
|
74
|
+
if (['status', 'state', 'show'].includes(normalized)) {
|
|
75
|
+
return 'status';
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.AutoAcceptService = AutoAcceptService;
|