@deadragdoll/tellymcp 0.0.12 → 0.0.14
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/.env.example.client +5 -1
- package/.env.example.gateway +16 -1
- package/CHANGELOG.md +30 -0
- package/README-ru.md +33 -0
- package/README.md +33 -0
- package/TOOLS.md +384 -9
- package/config/templates/env.both.template +15 -2
- package/config/templates/env.client.template +5 -2
- package/config/templates/env.gateway.template +15 -2
- package/dist/cli.js +137 -2
- package/dist/services/features/telegram-mcp/browser.service.js +56 -1
- package/dist/services/features/telegram-mcp/file-content.service.js +94 -0
- package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
- package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
- package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
- package/dist/services/features/telegram-mcp/mcp-server.service.js +30 -0
- package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +23 -1
- package/dist/services/features/telegram-mcp/src/app/config/env.js +132 -2
- package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
- package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
- package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
- package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
- package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +205 -17
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserInjectScriptTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStartTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStatusTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStopTool.js +28 -0
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
- package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +890 -23
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +536 -0
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +80 -0
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +736 -0
- package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +188 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
- package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
- package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +4 -1
- package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +33 -35
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
- package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
- package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +5 -1
- package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +28 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +3 -0
- package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +6 -3
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +29 -6
- package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
- package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
- package/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
- package/docs/CHAT_CONNECTOR.md +134 -0
- package/docs/STANDALONE-ru.md +55 -6
- package/docs/STANDALONE.md +55 -6
- package/package.json +10 -5
- package/packages/chrome-attach-extension/dist/background.js +1735 -0
- package/packages/chrome-attach-extension/dist/icon.svg +6 -0
- package/packages/chrome-attach-extension/dist/manifest.json +37 -0
- package/packages/chrome-attach-extension/dist/options.html +312 -0
- package/packages/chrome-attach-extension/dist/options.js +606 -0
- package/packages/chrome-attach-extension/dist/popup.html +93 -0
- package/packages/chrome-attach-extension/dist/popup.js +79 -0
- package/packages/chrome-attach-extension/dist/recorder-content.js +96 -0
- package/packages/chrome-attach-extension/dist/recorder-page.js +282 -0
- package/packages/firefox-attach-extension/README.md +13 -0
- package/packages/firefox-attach-extension/dist/background.js +1622 -0
- package/packages/firefox-attach-extension/dist/icon.svg +6 -0
- package/packages/firefox-attach-extension/dist/manifest.json +44 -0
- package/packages/firefox-attach-extension/dist/options.html +312 -0
- package/packages/firefox-attach-extension/dist/options.js +540 -0
- package/packages/firefox-attach-extension/dist/popup.html +93 -0
- package/packages/firefox-attach-extension/dist/popup.js +64 -0
- package/packages/firefox-attach-extension/dist/recorder-content.js +90 -0
- package/packages/firefox-attach-extension/dist/recorder-page.js +318 -0
|
@@ -4,7 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.BrowserService = void 0;
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
7
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const bodyLimits_1 = require("../../../shared/lib/bodyLimits");
|
|
10
|
+
const localTimestamp_1 = require("../../../shared/lib/time/localTimestamp");
|
|
8
11
|
const gatewayClientAccess_1 = require("../../distributed-client/model/gatewayClientAccess");
|
|
9
12
|
function pushBounded(list, entry, max) {
|
|
10
13
|
list.push(entry);
|
|
@@ -21,7 +24,7 @@ function trimList(list, limit) {
|
|
|
21
24
|
function sanitizeScreenshotName(fileName) {
|
|
22
25
|
const trimmed = fileName?.trim();
|
|
23
26
|
if (!trimmed) {
|
|
24
|
-
const timestamp = new Date()
|
|
27
|
+
const timestamp = (0, localTimestamp_1.formatLocalTimestampForFileName)(new Date());
|
|
25
28
|
return `browser-screenshot-${timestamp}.png`;
|
|
26
29
|
}
|
|
27
30
|
const parsed = node_path_1.default.parse(trimmed);
|
|
@@ -30,8 +33,8 @@ function sanitizeScreenshotName(fileName) {
|
|
|
30
33
|
return `${base}${extension}`;
|
|
31
34
|
}
|
|
32
35
|
function buildDatedRelativePath(fileName, date = new Date()) {
|
|
33
|
-
const dateSegment =
|
|
34
|
-
const timeSegment =
|
|
36
|
+
const dateSegment = (0, localTimestamp_1.formatLocalDateSegment)(date);
|
|
37
|
+
const timeSegment = (0, localTimestamp_1.formatLocalTimeSegment)(date);
|
|
35
38
|
return `${dateSegment}/${timeSegment}/${fileName}`;
|
|
36
39
|
}
|
|
37
40
|
function isAbsoluteBrowserUrl(value) {
|
|
@@ -60,10 +63,11 @@ class BrowserService {
|
|
|
60
63
|
logger;
|
|
61
64
|
projectIdentityResolver;
|
|
62
65
|
remoteConsoleInvoker;
|
|
66
|
+
firefoxAttachRegistry;
|
|
63
67
|
playwrightModulePromise;
|
|
64
68
|
browserPromise;
|
|
65
69
|
sessionStates = new Map();
|
|
66
|
-
constructor(config, sessionStore, maintenanceStore, bindingStore, xchangeFileMetaStore, objectStore, telegramTransport, logger, projectIdentityResolver, remoteConsoleInvoker) {
|
|
70
|
+
constructor(config, sessionStore, maintenanceStore, bindingStore, xchangeFileMetaStore, objectStore, telegramTransport, logger, projectIdentityResolver, remoteConsoleInvoker, firefoxAttachRegistry) {
|
|
67
71
|
this.config = config;
|
|
68
72
|
this.sessionStore = sessionStore;
|
|
69
73
|
this.maintenanceStore = maintenanceStore;
|
|
@@ -74,6 +78,304 @@ class BrowserService {
|
|
|
74
78
|
this.logger = logger;
|
|
75
79
|
this.projectIdentityResolver = projectIdentityResolver;
|
|
76
80
|
this.remoteConsoleInvoker = remoteConsoleInvoker;
|
|
81
|
+
this.firefoxAttachRegistry = firefoxAttachRegistry;
|
|
82
|
+
}
|
|
83
|
+
async listAttachedInstances(input) {
|
|
84
|
+
const resolvedSessionId = await this.resolveOptionalSessionIdForRemote(input);
|
|
85
|
+
if (resolvedSessionId) {
|
|
86
|
+
const remote = await this.invokeRemote(resolvedSessionId, "telegramMcp.browser.listAttachedInstancesRemote", {
|
|
87
|
+
...input,
|
|
88
|
+
session_id: resolvedSessionId,
|
|
89
|
+
});
|
|
90
|
+
if (remote) {
|
|
91
|
+
return remote;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const instances = this.listFirefoxAttachInstances();
|
|
95
|
+
return {
|
|
96
|
+
...(resolvedSessionId ? { session_id: resolvedSessionId } : {}),
|
|
97
|
+
total: instances.length,
|
|
98
|
+
instances: instances.map((instance) => ({
|
|
99
|
+
instance_id: instance.instanceId,
|
|
100
|
+
browser: instance.browser,
|
|
101
|
+
extension_version: instance.extensionVersion,
|
|
102
|
+
...(instance.profileName ? { profile_name: instance.profileName } : {}),
|
|
103
|
+
connected_at: instance.connectedAt,
|
|
104
|
+
last_seen_at: instance.lastSeenAt,
|
|
105
|
+
capabilities: [...instance.capabilities],
|
|
106
|
+
tab_count: instance.tabs.length,
|
|
107
|
+
...(instance.activeTab
|
|
108
|
+
? {
|
|
109
|
+
active_tab: {
|
|
110
|
+
tab_id: instance.activeTab.tab_id,
|
|
111
|
+
...(typeof instance.activeTab.window_id === "number"
|
|
112
|
+
? { window_id: instance.activeTab.window_id }
|
|
113
|
+
: {}),
|
|
114
|
+
active: instance.activeTab.active,
|
|
115
|
+
title: instance.activeTab.title,
|
|
116
|
+
url: instance.activeTab.url,
|
|
117
|
+
...(instance.activeTab.status
|
|
118
|
+
? { status: instance.activeTab.status }
|
|
119
|
+
: {}),
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
: {}),
|
|
123
|
+
})),
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
async listTabs(input) {
|
|
127
|
+
const resolvedSessionId = await this.resolveOptionalSessionIdForRemote(input);
|
|
128
|
+
if (resolvedSessionId) {
|
|
129
|
+
const remote = await this.invokeRemote(resolvedSessionId, "telegramMcp.browser.listTabsRemote", {
|
|
130
|
+
...input,
|
|
131
|
+
session_id: resolvedSessionId,
|
|
132
|
+
});
|
|
133
|
+
if (remote) {
|
|
134
|
+
return remote;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const instances = this.listFirefoxAttachInstances();
|
|
138
|
+
const instance = input.instance_id?.trim()
|
|
139
|
+
? instances.find((item) => item.instanceId === input.instance_id?.trim())
|
|
140
|
+
: instances.length === 1
|
|
141
|
+
? instances[0]
|
|
142
|
+
: undefined;
|
|
143
|
+
if (!instance) {
|
|
144
|
+
if (input.instance_id?.trim()) {
|
|
145
|
+
throw new Error(`Attached browser instance '${input.instance_id.trim()}' was not found.`);
|
|
146
|
+
}
|
|
147
|
+
throw new Error("Attached browser instance is ambiguous. Pass instance_id or keep exactly one connected browser instance.");
|
|
148
|
+
}
|
|
149
|
+
const selectedAttachment = resolvedSessionId
|
|
150
|
+
? await this.getAttachedBrowserAttachment(resolvedSessionId)
|
|
151
|
+
: null;
|
|
152
|
+
return {
|
|
153
|
+
...(resolvedSessionId ? { session_id: resolvedSessionId } : {}),
|
|
154
|
+
instance_id: instance.instanceId,
|
|
155
|
+
total: instance.tabs.length,
|
|
156
|
+
tabs: instance.tabs.map((tab) => ({
|
|
157
|
+
tab_id: tab.tab_id,
|
|
158
|
+
...(typeof tab.window_id === "number" ? { window_id: tab.window_id } : {}),
|
|
159
|
+
active: tab.active,
|
|
160
|
+
...(selectedAttachment &&
|
|
161
|
+
selectedAttachment.instanceId === instance.instanceId &&
|
|
162
|
+
selectedAttachment.tabId === tab.tab_id
|
|
163
|
+
? { selected: true }
|
|
164
|
+
: {}),
|
|
165
|
+
title: tab.title,
|
|
166
|
+
url: tab.url,
|
|
167
|
+
...(tab.status ? { status: tab.status } : {}),
|
|
168
|
+
})),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
async attachActiveTab(input) {
|
|
172
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
173
|
+
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
174
|
+
const remote = await this.invokeRemote(normalizedSessionId, "telegramMcp.browser.attachActiveTabRemote", {
|
|
175
|
+
...input,
|
|
176
|
+
session_id: normalizedSessionId,
|
|
177
|
+
});
|
|
178
|
+
if (remote) {
|
|
179
|
+
return remote;
|
|
180
|
+
}
|
|
181
|
+
const instances = this.listFirefoxAttachInstances();
|
|
182
|
+
const instance = input.instance_id?.trim()
|
|
183
|
+
? instances.find((item) => item.instanceId === input.instance_id?.trim())
|
|
184
|
+
: instances.length === 1
|
|
185
|
+
? instances[0]
|
|
186
|
+
: undefined;
|
|
187
|
+
if (!instance) {
|
|
188
|
+
if (input.instance_id?.trim()) {
|
|
189
|
+
throw new Error(`Attached browser instance '${input.instance_id.trim()}' was not found.`);
|
|
190
|
+
}
|
|
191
|
+
throw new Error("Attached browser instance is ambiguous. Pass instance_id or keep exactly one connected browser instance.");
|
|
192
|
+
}
|
|
193
|
+
const activeTab = instance.activeTab;
|
|
194
|
+
if (!activeTab) {
|
|
195
|
+
throw new Error(`Attached browser instance '${instance.instanceId}' does not expose an active tab.`);
|
|
196
|
+
}
|
|
197
|
+
return await this.attachTab({
|
|
198
|
+
session_id: normalizedSessionId,
|
|
199
|
+
instance_id: instance.instanceId,
|
|
200
|
+
tab_id: activeTab.tab_id,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
async attachTab(input) {
|
|
204
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
205
|
+
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
206
|
+
const remote = await this.invokeRemote(normalizedSessionId, "telegramMcp.browser.attachTabRemote", {
|
|
207
|
+
...input,
|
|
208
|
+
session_id: normalizedSessionId,
|
|
209
|
+
});
|
|
210
|
+
if (remote) {
|
|
211
|
+
return remote;
|
|
212
|
+
}
|
|
213
|
+
if (!this.firefoxAttachRegistry) {
|
|
214
|
+
throw new Error("Attached browser backend is not available.");
|
|
215
|
+
}
|
|
216
|
+
const instances = this.listFirefoxAttachInstances();
|
|
217
|
+
const instance = input.instance_id?.trim()
|
|
218
|
+
? instances.find((item) => item.instanceId === input.instance_id?.trim())
|
|
219
|
+
: instances.length === 1
|
|
220
|
+
? instances[0]
|
|
221
|
+
: undefined;
|
|
222
|
+
if (!instance) {
|
|
223
|
+
if (input.instance_id?.trim()) {
|
|
224
|
+
throw new Error(`Attached browser instance '${input.instance_id.trim()}' was not found.`);
|
|
225
|
+
}
|
|
226
|
+
throw new Error("Attached browser instance is ambiguous. Pass instance_id or keep exactly one connected browser instance.");
|
|
227
|
+
}
|
|
228
|
+
const tab = instance.tabs.find((item) => item.tab_id === input.tab_id);
|
|
229
|
+
if (!tab) {
|
|
230
|
+
throw new Error(`Tab '${input.tab_id}' was not found in attached browser instance '${instance.instanceId}'.`);
|
|
231
|
+
}
|
|
232
|
+
const result = await this.firefoxAttachRegistry.invokeTabAction({
|
|
233
|
+
instanceId: instance.instanceId,
|
|
234
|
+
tabId: tab.tab_id,
|
|
235
|
+
action: "attach",
|
|
236
|
+
});
|
|
237
|
+
const attachedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
238
|
+
await this.maintenanceStore.setBrowserAttachment({
|
|
239
|
+
sessionId: normalizedSessionId,
|
|
240
|
+
backend: "firefox-attached",
|
|
241
|
+
instanceId: instance.instanceId,
|
|
242
|
+
tabId: tab.tab_id,
|
|
243
|
+
attachedAt,
|
|
244
|
+
...(result?.title
|
|
245
|
+
? { title: String(result.title) }
|
|
246
|
+
: tab.title
|
|
247
|
+
? { title: tab.title }
|
|
248
|
+
: {}),
|
|
249
|
+
...(result?.url
|
|
250
|
+
? { url: String(result.url) }
|
|
251
|
+
: tab.url
|
|
252
|
+
? { url: tab.url }
|
|
253
|
+
: {}),
|
|
254
|
+
});
|
|
255
|
+
return {
|
|
256
|
+
session_id: normalizedSessionId,
|
|
257
|
+
backend: "firefox-attached",
|
|
258
|
+
instance_id: instance.instanceId,
|
|
259
|
+
tab_id: tab.tab_id,
|
|
260
|
+
attached_at: attachedAt,
|
|
261
|
+
...(result?.title
|
|
262
|
+
? { title: String(result.title) }
|
|
263
|
+
: tab.title
|
|
264
|
+
? { title: tab.title }
|
|
265
|
+
: {}),
|
|
266
|
+
...(result?.url
|
|
267
|
+
? { url: String(result.url) }
|
|
268
|
+
: tab.url
|
|
269
|
+
? { url: tab.url }
|
|
270
|
+
: {}),
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
async detachTab(input) {
|
|
274
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
275
|
+
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
276
|
+
const remote = await this.invokeRemote(normalizedSessionId, "telegramMcp.browser.detachTabRemote", {
|
|
277
|
+
...input,
|
|
278
|
+
session_id: normalizedSessionId,
|
|
279
|
+
});
|
|
280
|
+
if (remote) {
|
|
281
|
+
return remote;
|
|
282
|
+
}
|
|
283
|
+
const attachment = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
284
|
+
if (attachment && this.firefoxAttachRegistry) {
|
|
285
|
+
try {
|
|
286
|
+
await this.firefoxAttachRegistry.invokeTabAction({
|
|
287
|
+
instanceId: attachment.instanceId,
|
|
288
|
+
tabId: attachment.tabId,
|
|
289
|
+
action: "detach",
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
catch {
|
|
293
|
+
// Clearing local state is still the source of truth.
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
await this.maintenanceStore.clearBrowserAttachment(normalizedSessionId);
|
|
297
|
+
return {
|
|
298
|
+
session_id: normalizedSessionId,
|
|
299
|
+
detached: true,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
async startRecording(input) {
|
|
303
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
304
|
+
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
305
|
+
const remote = await this.invokeRemote(normalizedSessionId, "telegramMcp.browser.startRecordingRemote", {
|
|
306
|
+
...input,
|
|
307
|
+
session_id: normalizedSessionId,
|
|
308
|
+
});
|
|
309
|
+
if (remote) {
|
|
310
|
+
return remote;
|
|
311
|
+
}
|
|
312
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
313
|
+
if (!attached) {
|
|
314
|
+
throw new Error("No attached browser tab is selected for this session. Select a tab in the browser attach panel first.");
|
|
315
|
+
}
|
|
316
|
+
if (!this.firefoxAttachRegistry) {
|
|
317
|
+
throw new Error("Firefox attach backend is not available.");
|
|
318
|
+
}
|
|
319
|
+
const record = await this.firefoxAttachRegistry.startRecording({
|
|
320
|
+
sessionId: normalizedSessionId,
|
|
321
|
+
instanceId: input.instance_id?.trim() || attached.instanceId,
|
|
322
|
+
tabId: attached.tabId,
|
|
323
|
+
tabTitle: attached.title?.trim() || "attached-tab",
|
|
324
|
+
...(attached.url ? { tabUrl: attached.url } : {}),
|
|
325
|
+
});
|
|
326
|
+
return this.mapRecordingRecord(normalizedSessionId, record);
|
|
327
|
+
}
|
|
328
|
+
async stopRecording(input) {
|
|
329
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
330
|
+
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
331
|
+
const remote = await this.invokeRemote(normalizedSessionId, "telegramMcp.browser.stopRecordingRemote", {
|
|
332
|
+
...input,
|
|
333
|
+
session_id: normalizedSessionId,
|
|
334
|
+
});
|
|
335
|
+
if (remote) {
|
|
336
|
+
return remote;
|
|
337
|
+
}
|
|
338
|
+
if (!this.firefoxAttachRegistry) {
|
|
339
|
+
throw new Error("Firefox attach backend is not available.");
|
|
340
|
+
}
|
|
341
|
+
const record = await this.firefoxAttachRegistry.stopRecording({
|
|
342
|
+
sessionId: normalizedSessionId,
|
|
343
|
+
});
|
|
344
|
+
if (!record) {
|
|
345
|
+
return {
|
|
346
|
+
session_id: normalizedSessionId,
|
|
347
|
+
stopped: false,
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
return {
|
|
351
|
+
session_id: normalizedSessionId,
|
|
352
|
+
stopped: true,
|
|
353
|
+
recording_id: record.recordingId,
|
|
354
|
+
bundle_dir_name: record.bundleDirName,
|
|
355
|
+
bundle_relative_path: record.bundleRelativePath,
|
|
356
|
+
bundle_path: record.bundlePath,
|
|
357
|
+
...(record.stoppedAt ? { stopped_at: record.stoppedAt } : {}),
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
async getRecordingStatus(input) {
|
|
361
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
362
|
+
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
363
|
+
const remote = await this.invokeRemote(normalizedSessionId, "telegramMcp.browser.getRecordingStatusRemote", {
|
|
364
|
+
...input,
|
|
365
|
+
session_id: normalizedSessionId,
|
|
366
|
+
});
|
|
367
|
+
if (remote) {
|
|
368
|
+
return remote;
|
|
369
|
+
}
|
|
370
|
+
if (!this.firefoxAttachRegistry) {
|
|
371
|
+
throw new Error("Firefox attach backend is not available.");
|
|
372
|
+
}
|
|
373
|
+
const record = await this.firefoxAttachRegistry.getRecordingStatus(normalizedSessionId);
|
|
374
|
+
return {
|
|
375
|
+
session_id: normalizedSessionId,
|
|
376
|
+
active: record?.status === "recording",
|
|
377
|
+
...(record ? { recording: this.mapRecordingStatusRecord(record) } : {}),
|
|
378
|
+
};
|
|
77
379
|
}
|
|
78
380
|
async open(input) {
|
|
79
381
|
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
@@ -115,7 +417,7 @@ class BrowserService {
|
|
|
115
417
|
});
|
|
116
418
|
state.currentUrl = state.page.url();
|
|
117
419
|
state.title = await state.page.title();
|
|
118
|
-
state.lastUsedAt = new Date()
|
|
420
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
119
421
|
this.logger.info("Browser page opened", {
|
|
120
422
|
sessionId: resolved.sessionId,
|
|
121
423
|
normalizedSessionId,
|
|
@@ -149,8 +451,40 @@ class BrowserService {
|
|
|
149
451
|
return remote;
|
|
150
452
|
}
|
|
151
453
|
this.ensureEnabled();
|
|
454
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
455
|
+
if (attached) {
|
|
456
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
457
|
+
action: "get_logs",
|
|
458
|
+
payload: {
|
|
459
|
+
limit: input.limit,
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
const consoleMessages = Array.isArray(result?.console_messages)
|
|
463
|
+
? result.console_messages
|
|
464
|
+
: [];
|
|
465
|
+
return {
|
|
466
|
+
session_id: normalizedSessionId,
|
|
467
|
+
total: typeof result?.console_total === "number"
|
|
468
|
+
? result.console_total
|
|
469
|
+
: consoleMessages.length,
|
|
470
|
+
messages: consoleMessages.map((message) => ({
|
|
471
|
+
type: typeof message?.type === "string"
|
|
472
|
+
? message.type
|
|
473
|
+
: "log",
|
|
474
|
+
text: typeof message?.text === "string"
|
|
475
|
+
? message.text
|
|
476
|
+
: "",
|
|
477
|
+
...(typeof message?.location === "string"
|
|
478
|
+
? { location: message.location }
|
|
479
|
+
: {}),
|
|
480
|
+
timestamp: typeof message?.timestamp === "string"
|
|
481
|
+
? message.timestamp
|
|
482
|
+
: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
483
|
+
})),
|
|
484
|
+
};
|
|
485
|
+
}
|
|
152
486
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
153
|
-
state.lastUsedAt = new Date()
|
|
487
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
154
488
|
return {
|
|
155
489
|
session_id: sessionId,
|
|
156
490
|
total: state.consoleMessages.length,
|
|
@@ -173,6 +507,22 @@ class BrowserService {
|
|
|
173
507
|
return remote;
|
|
174
508
|
}
|
|
175
509
|
this.ensureEnabled();
|
|
510
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
511
|
+
if (attached) {
|
|
512
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
513
|
+
action: "click",
|
|
514
|
+
payload: this.buildAttachedLocatorPayload(input),
|
|
515
|
+
});
|
|
516
|
+
return {
|
|
517
|
+
session_id: normalizedSessionId,
|
|
518
|
+
clicked: true,
|
|
519
|
+
...(input.ai_tag ? { ai_tag: input.ai_tag } : {}),
|
|
520
|
+
...(input.selector ? { selector: input.selector } : {}),
|
|
521
|
+
...(input.text ? { text: input.text } : {}),
|
|
522
|
+
url: String(result?.url || attached.url || ""),
|
|
523
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
524
|
+
};
|
|
525
|
+
}
|
|
176
526
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
177
527
|
const locator = this.resolveLocator(state.page, input);
|
|
178
528
|
await locator.click({
|
|
@@ -180,7 +530,7 @@ class BrowserService {
|
|
|
180
530
|
});
|
|
181
531
|
state.currentUrl = state.page.url();
|
|
182
532
|
state.title = await state.page.title().catch(() => state.title);
|
|
183
|
-
state.lastUsedAt = new Date()
|
|
533
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
184
534
|
return {
|
|
185
535
|
session_id: sessionId,
|
|
186
536
|
clicked: true,
|
|
@@ -202,6 +552,26 @@ class BrowserService {
|
|
|
202
552
|
return remote;
|
|
203
553
|
}
|
|
204
554
|
this.ensureEnabled();
|
|
555
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
556
|
+
if (attached) {
|
|
557
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
558
|
+
action: "fill",
|
|
559
|
+
payload: {
|
|
560
|
+
...this.buildAttachedLocatorPayload(input),
|
|
561
|
+
value: input.value,
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
return {
|
|
565
|
+
session_id: normalizedSessionId,
|
|
566
|
+
filled: true,
|
|
567
|
+
...(input.ai_tag ? { ai_tag: input.ai_tag } : {}),
|
|
568
|
+
...(input.selector ? { selector: input.selector } : {}),
|
|
569
|
+
...(input.text ? { text: input.text } : {}),
|
|
570
|
+
value_length: input.value.length,
|
|
571
|
+
url: String(result?.url || attached.url || ""),
|
|
572
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
573
|
+
};
|
|
574
|
+
}
|
|
205
575
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
206
576
|
const locator = this.resolveLocator(state.page, input);
|
|
207
577
|
await locator.fill(input.value, {
|
|
@@ -209,7 +579,7 @@ class BrowserService {
|
|
|
209
579
|
});
|
|
210
580
|
state.currentUrl = state.page.url();
|
|
211
581
|
state.title = await state.page.title().catch(() => state.title);
|
|
212
|
-
state.lastUsedAt = new Date()
|
|
582
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
213
583
|
return {
|
|
214
584
|
session_id: sessionId,
|
|
215
585
|
filled: true,
|
|
@@ -232,6 +602,26 @@ class BrowserService {
|
|
|
232
602
|
return remote;
|
|
233
603
|
}
|
|
234
604
|
this.ensureEnabled();
|
|
605
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
606
|
+
if (attached) {
|
|
607
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
608
|
+
action: "press",
|
|
609
|
+
payload: {
|
|
610
|
+
...this.buildAttachedLocatorPayload(input),
|
|
611
|
+
key: input.key,
|
|
612
|
+
},
|
|
613
|
+
});
|
|
614
|
+
return {
|
|
615
|
+
session_id: normalizedSessionId,
|
|
616
|
+
pressed: true,
|
|
617
|
+
key: input.key,
|
|
618
|
+
...(input.ai_tag ? { ai_tag: input.ai_tag } : {}),
|
|
619
|
+
...(input.selector ? { selector: input.selector } : {}),
|
|
620
|
+
...(input.text ? { text: input.text } : {}),
|
|
621
|
+
url: String(result?.url || attached.url || ""),
|
|
622
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
623
|
+
};
|
|
624
|
+
}
|
|
235
625
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
236
626
|
if (input.selector || input.text) {
|
|
237
627
|
const locator = this.resolveLocator(state.page, input);
|
|
@@ -244,7 +634,7 @@ class BrowserService {
|
|
|
244
634
|
}
|
|
245
635
|
state.currentUrl = state.page.url();
|
|
246
636
|
state.title = await state.page.title().catch(() => state.title);
|
|
247
|
-
state.lastUsedAt = new Date()
|
|
637
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
248
638
|
return {
|
|
249
639
|
session_id: sessionId,
|
|
250
640
|
pressed: true,
|
|
@@ -256,6 +646,72 @@ class BrowserService {
|
|
|
256
646
|
...(state.title ? { title: state.title } : {}),
|
|
257
647
|
};
|
|
258
648
|
}
|
|
649
|
+
async injectScript(input) {
|
|
650
|
+
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
651
|
+
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
652
|
+
const remote = await this.invokeRemote(normalizedSessionId, "telegramMcp.browser.injectScriptRemote", {
|
|
653
|
+
...input,
|
|
654
|
+
session_id: normalizedSessionId,
|
|
655
|
+
});
|
|
656
|
+
if (remote) {
|
|
657
|
+
return remote;
|
|
658
|
+
}
|
|
659
|
+
this.ensureEnabled();
|
|
660
|
+
const namespace = input.namespace?.trim() || "TELLY";
|
|
661
|
+
const sourcePath = input.file_path?.trim()
|
|
662
|
+
? node_path_1.default.resolve(input.file_path.trim())
|
|
663
|
+
: null;
|
|
664
|
+
if (!input.source?.trim().length && sourcePath) {
|
|
665
|
+
const sourceStats = await (0, promises_1.stat)(sourcePath);
|
|
666
|
+
(0, bodyLimits_1.assertBodySize)(sourceStats.size);
|
|
667
|
+
}
|
|
668
|
+
const source = input.source?.trim().length
|
|
669
|
+
? input.source
|
|
670
|
+
: sourcePath
|
|
671
|
+
? await (0, promises_1.readFile)(sourcePath, "utf8")
|
|
672
|
+
: null;
|
|
673
|
+
if (!source) {
|
|
674
|
+
throw new Error("Provide source or file_path.");
|
|
675
|
+
}
|
|
676
|
+
(0, bodyLimits_1.assertStringBodySize)(source);
|
|
677
|
+
const sourceType = input.source?.trim().length ? "inline" : "file";
|
|
678
|
+
const wrappedSource = this.wrapInjectedScript(source, namespace);
|
|
679
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
680
|
+
if (attached) {
|
|
681
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
682
|
+
action: "inject_script",
|
|
683
|
+
payload: {
|
|
684
|
+
namespace,
|
|
685
|
+
source,
|
|
686
|
+
},
|
|
687
|
+
});
|
|
688
|
+
return {
|
|
689
|
+
session_id: normalizedSessionId,
|
|
690
|
+
injected: true,
|
|
691
|
+
namespace,
|
|
692
|
+
source_type: sourceType,
|
|
693
|
+
bytes: Buffer.byteLength(source, "utf8"),
|
|
694
|
+
url: String(result?.url || attached.url || ""),
|
|
695
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
const { sessionId, state } = await this.requireSessionState(input);
|
|
699
|
+
await state.page.addScriptTag({
|
|
700
|
+
content: wrappedSource,
|
|
701
|
+
});
|
|
702
|
+
state.currentUrl = state.page.url();
|
|
703
|
+
state.title = await state.page.title().catch(() => state.title);
|
|
704
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
705
|
+
return {
|
|
706
|
+
session_id: sessionId,
|
|
707
|
+
injected: true,
|
|
708
|
+
namespace,
|
|
709
|
+
source_type: sourceType,
|
|
710
|
+
bytes: Buffer.byteLength(source, "utf8"),
|
|
711
|
+
url: state.currentUrl,
|
|
712
|
+
...(state.title ? { title: state.title } : {}),
|
|
713
|
+
};
|
|
714
|
+
}
|
|
259
715
|
async reload(input) {
|
|
260
716
|
const resolved = this.projectIdentityResolver.resolveSessionDefaults(input);
|
|
261
717
|
const normalizedSessionId = await this.normalizeSessionIdForAccess(resolved.sessionId);
|
|
@@ -267,6 +723,18 @@ class BrowserService {
|
|
|
267
723
|
return remote;
|
|
268
724
|
}
|
|
269
725
|
this.ensureEnabled();
|
|
726
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
727
|
+
if (attached) {
|
|
728
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
729
|
+
action: "reload",
|
|
730
|
+
});
|
|
731
|
+
return {
|
|
732
|
+
session_id: normalizedSessionId,
|
|
733
|
+
reloaded: true,
|
|
734
|
+
url: String(result?.url || attached.url || ""),
|
|
735
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
736
|
+
};
|
|
737
|
+
}
|
|
270
738
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
271
739
|
const waitUntil = (input.wait_until ??
|
|
272
740
|
this.config.browser.waitUntil);
|
|
@@ -276,7 +744,7 @@ class BrowserService {
|
|
|
276
744
|
});
|
|
277
745
|
state.currentUrl = state.page.url();
|
|
278
746
|
state.title = await state.page.title().catch(() => state.title);
|
|
279
|
-
state.lastUsedAt = new Date()
|
|
747
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
280
748
|
this.logger.info("Browser page reloaded", {
|
|
281
749
|
sessionId,
|
|
282
750
|
url: state.currentUrl,
|
|
@@ -301,6 +769,30 @@ class BrowserService {
|
|
|
301
769
|
return remote;
|
|
302
770
|
}
|
|
303
771
|
this.ensureEnabled();
|
|
772
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
773
|
+
if (attached) {
|
|
774
|
+
const waitState = input.state ?? "visible";
|
|
775
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
776
|
+
action: "wait_for",
|
|
777
|
+
payload: {
|
|
778
|
+
...this.buildAttachedLocatorPayload(input),
|
|
779
|
+
state: waitState,
|
|
780
|
+
...(typeof input.timeout_ms === "number"
|
|
781
|
+
? { timeout_ms: input.timeout_ms }
|
|
782
|
+
: {}),
|
|
783
|
+
},
|
|
784
|
+
});
|
|
785
|
+
return {
|
|
786
|
+
session_id: normalizedSessionId,
|
|
787
|
+
waited: true,
|
|
788
|
+
state: waitState,
|
|
789
|
+
...(input.ai_tag ? { ai_tag: input.ai_tag } : {}),
|
|
790
|
+
...(input.selector ? { selector: input.selector } : {}),
|
|
791
|
+
...(input.text ? { text: input.text } : {}),
|
|
792
|
+
url: String(result?.url || attached.url || ""),
|
|
793
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
794
|
+
};
|
|
795
|
+
}
|
|
304
796
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
305
797
|
const locator = this.resolveLocator(state.page, input);
|
|
306
798
|
const waitState = input.state ?? "visible";
|
|
@@ -310,7 +802,7 @@ class BrowserService {
|
|
|
310
802
|
});
|
|
311
803
|
state.currentUrl = state.page.url();
|
|
312
804
|
state.title = await state.page.title().catch(() => state.title);
|
|
313
|
-
state.lastUsedAt = new Date()
|
|
805
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
314
806
|
return {
|
|
315
807
|
session_id: sessionId,
|
|
316
808
|
waited: true,
|
|
@@ -333,6 +825,32 @@ class BrowserService {
|
|
|
333
825
|
return remote;
|
|
334
826
|
}
|
|
335
827
|
this.ensureEnabled();
|
|
828
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
829
|
+
if (attached) {
|
|
830
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
831
|
+
action: "wait_for_url",
|
|
832
|
+
payload: {
|
|
833
|
+
...(input.url?.trim() ? { url: input.url.trim() } : {}),
|
|
834
|
+
...(input.url_contains?.trim()
|
|
835
|
+
? { url_contains: input.url_contains.trim() }
|
|
836
|
+
: {}),
|
|
837
|
+
...(typeof input.timeout_ms === "number"
|
|
838
|
+
? { timeout_ms: input.timeout_ms }
|
|
839
|
+
: {}),
|
|
840
|
+
},
|
|
841
|
+
});
|
|
842
|
+
return {
|
|
843
|
+
session_id: normalizedSessionId,
|
|
844
|
+
waited: true,
|
|
845
|
+
matched: input.url?.trim() ? "url" : "url_contains",
|
|
846
|
+
...(input.url?.trim() ? { url: input.url.trim() } : {}),
|
|
847
|
+
...(input.url_contains?.trim()
|
|
848
|
+
? { url_contains: input.url_contains.trim() }
|
|
849
|
+
: {}),
|
|
850
|
+
current_url: String(result?.url || attached.url || ""),
|
|
851
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
852
|
+
};
|
|
853
|
+
}
|
|
336
854
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
337
855
|
const timeout = this.resolveTimeoutMs(input.timeout_ms);
|
|
338
856
|
if (input.url?.trim()) {
|
|
@@ -351,7 +869,7 @@ class BrowserService {
|
|
|
351
869
|
}
|
|
352
870
|
state.currentUrl = state.page.url();
|
|
353
871
|
state.title = await state.page.title().catch(() => state.title);
|
|
354
|
-
state.lastUsedAt = new Date()
|
|
872
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
355
873
|
return {
|
|
356
874
|
session_id: sessionId,
|
|
357
875
|
waited: true,
|
|
@@ -375,8 +893,37 @@ class BrowserService {
|
|
|
375
893
|
return remote;
|
|
376
894
|
}
|
|
377
895
|
this.ensureEnabled();
|
|
896
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
897
|
+
if (attached) {
|
|
898
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
899
|
+
action: "get_logs",
|
|
900
|
+
payload: {
|
|
901
|
+
limit: input.limit,
|
|
902
|
+
},
|
|
903
|
+
});
|
|
904
|
+
const pageErrors = Array.isArray(result?.page_errors)
|
|
905
|
+
? result.page_errors
|
|
906
|
+
: [];
|
|
907
|
+
return {
|
|
908
|
+
session_id: normalizedSessionId,
|
|
909
|
+
total: typeof result?.page_error_total === "number"
|
|
910
|
+
? result.page_error_total
|
|
911
|
+
: pageErrors.length,
|
|
912
|
+
errors: pageErrors.map((error) => ({
|
|
913
|
+
message: typeof error?.message === "string"
|
|
914
|
+
? error.message
|
|
915
|
+
: "",
|
|
916
|
+
...(typeof error?.stack === "string"
|
|
917
|
+
? { stack: error.stack }
|
|
918
|
+
: {}),
|
|
919
|
+
timestamp: typeof error?.timestamp === "string"
|
|
920
|
+
? error.timestamp
|
|
921
|
+
: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
922
|
+
})),
|
|
923
|
+
};
|
|
924
|
+
}
|
|
378
925
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
379
|
-
state.lastUsedAt = new Date()
|
|
926
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
380
927
|
return {
|
|
381
928
|
session_id: sessionId,
|
|
382
929
|
total: state.pageErrors.length,
|
|
@@ -398,8 +945,46 @@ class BrowserService {
|
|
|
398
945
|
return remote;
|
|
399
946
|
}
|
|
400
947
|
this.ensureEnabled();
|
|
948
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
949
|
+
if (attached) {
|
|
950
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
951
|
+
action: "get_logs",
|
|
952
|
+
payload: {
|
|
953
|
+
limit: input.limit,
|
|
954
|
+
},
|
|
955
|
+
});
|
|
956
|
+
const failures = Array.isArray(result?.network_failures)
|
|
957
|
+
? result.network_failures
|
|
958
|
+
: [];
|
|
959
|
+
return {
|
|
960
|
+
session_id: normalizedSessionId,
|
|
961
|
+
total: typeof result?.network_failure_total === "number"
|
|
962
|
+
? result.network_failure_total
|
|
963
|
+
: failures.length,
|
|
964
|
+
failures: failures.map((failure) => ({
|
|
965
|
+
url: typeof failure?.url === "string"
|
|
966
|
+
? failure.url
|
|
967
|
+
: "",
|
|
968
|
+
method: typeof failure?.method === "string"
|
|
969
|
+
? failure.method
|
|
970
|
+
: "GET",
|
|
971
|
+
...(typeof failure?.status === "number"
|
|
972
|
+
? { status: failure.status }
|
|
973
|
+
: {}),
|
|
974
|
+
...(typeof failure?.error_text === "string"
|
|
975
|
+
? { error_text: failure.error_text }
|
|
976
|
+
: {}),
|
|
977
|
+
...(typeof failure?.resource_type === "string"
|
|
978
|
+
? { resource_type: failure.resource_type }
|
|
979
|
+
: {}),
|
|
980
|
+
timestamp: typeof failure?.timestamp === "string"
|
|
981
|
+
? failure.timestamp
|
|
982
|
+
: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
983
|
+
})),
|
|
984
|
+
};
|
|
985
|
+
}
|
|
401
986
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
402
|
-
state.lastUsedAt = new Date()
|
|
987
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
403
988
|
return {
|
|
404
989
|
session_id: sessionId,
|
|
405
990
|
total: state.networkFailures.length,
|
|
@@ -426,6 +1011,25 @@ class BrowserService {
|
|
|
426
1011
|
return remote;
|
|
427
1012
|
}
|
|
428
1013
|
this.ensureEnabled();
|
|
1014
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
1015
|
+
if (attached) {
|
|
1016
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
1017
|
+
action: "clear_logs",
|
|
1018
|
+
});
|
|
1019
|
+
return {
|
|
1020
|
+
session_id: normalizedSessionId,
|
|
1021
|
+
cleared: true,
|
|
1022
|
+
console_messages_cleared: typeof result?.console_messages_cleared === "number"
|
|
1023
|
+
? result.console_messages_cleared
|
|
1024
|
+
: 0,
|
|
1025
|
+
page_errors_cleared: typeof result?.page_errors_cleared === "number"
|
|
1026
|
+
? result.page_errors_cleared
|
|
1027
|
+
: 0,
|
|
1028
|
+
network_failures_cleared: typeof result?.network_failures_cleared === "number"
|
|
1029
|
+
? result.network_failures_cleared
|
|
1030
|
+
: 0,
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
429
1033
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
430
1034
|
const consoleMessagesCleared = state.consoleMessages.length;
|
|
431
1035
|
const pageErrorsCleared = state.pageErrors.length;
|
|
@@ -433,7 +1037,7 @@ class BrowserService {
|
|
|
433
1037
|
state.consoleMessages = [];
|
|
434
1038
|
state.pageErrors = [];
|
|
435
1039
|
state.networkFailures = [];
|
|
436
|
-
state.lastUsedAt = new Date()
|
|
1040
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
437
1041
|
return {
|
|
438
1042
|
session_id: sessionId,
|
|
439
1043
|
cleared: true,
|
|
@@ -453,6 +1057,37 @@ class BrowserService {
|
|
|
453
1057
|
return remote;
|
|
454
1058
|
}
|
|
455
1059
|
this.ensureEnabled();
|
|
1060
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
1061
|
+
if (attached) {
|
|
1062
|
+
const selector = input.selector?.trim() || "body";
|
|
1063
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
1064
|
+
action: "dom",
|
|
1065
|
+
payload: {
|
|
1066
|
+
selector,
|
|
1067
|
+
include_html: input.include_html !== false,
|
|
1068
|
+
include_text: input.include_text !== false,
|
|
1069
|
+
},
|
|
1070
|
+
});
|
|
1071
|
+
return {
|
|
1072
|
+
session_id: normalizedSessionId,
|
|
1073
|
+
selector,
|
|
1074
|
+
found: result?.found === true,
|
|
1075
|
+
...(result?.url ? { url: String(result.url) } : {}),
|
|
1076
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
1077
|
+
...(typeof result?.outer_html === "string"
|
|
1078
|
+
? { outer_html: result.outer_html }
|
|
1079
|
+
: {}),
|
|
1080
|
+
...(typeof result?.text_content === "string"
|
|
1081
|
+
? { text_content: result.text_content }
|
|
1082
|
+
: {}),
|
|
1083
|
+
...(typeof result?.visible === "boolean"
|
|
1084
|
+
? { visible: result.visible }
|
|
1085
|
+
: {}),
|
|
1086
|
+
...(result?.attributes && typeof result.attributes === "object"
|
|
1087
|
+
? { attributes: result.attributes }
|
|
1088
|
+
: {}),
|
|
1089
|
+
};
|
|
1090
|
+
}
|
|
456
1091
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
457
1092
|
const selector = input.selector?.trim() || "body";
|
|
458
1093
|
const snapshot = await state.page
|
|
@@ -487,7 +1122,7 @@ class BrowserService {
|
|
|
487
1122
|
includeText: input.include_text !== false,
|
|
488
1123
|
})
|
|
489
1124
|
.catch(() => ({ found: false }));
|
|
490
|
-
state.lastUsedAt = new Date()
|
|
1125
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
491
1126
|
state.currentUrl = state.page.url();
|
|
492
1127
|
state.title = await state.page.title().catch(() => state.title);
|
|
493
1128
|
return {
|
|
@@ -517,6 +1152,57 @@ class BrowserService {
|
|
|
517
1152
|
return remote;
|
|
518
1153
|
}
|
|
519
1154
|
this.ensureEnabled();
|
|
1155
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
1156
|
+
if (attached) {
|
|
1157
|
+
const properties = input.properties?.length
|
|
1158
|
+
? input.properties
|
|
1159
|
+
: [
|
|
1160
|
+
"display",
|
|
1161
|
+
"position",
|
|
1162
|
+
"visibility",
|
|
1163
|
+
"opacity",
|
|
1164
|
+
"color",
|
|
1165
|
+
"background-color",
|
|
1166
|
+
"font-size",
|
|
1167
|
+
"z-index",
|
|
1168
|
+
"overflow",
|
|
1169
|
+
];
|
|
1170
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
1171
|
+
action: "computed_style",
|
|
1172
|
+
payload: {
|
|
1173
|
+
selector: input.selector,
|
|
1174
|
+
properties,
|
|
1175
|
+
},
|
|
1176
|
+
});
|
|
1177
|
+
return {
|
|
1178
|
+
session_id: normalizedSessionId,
|
|
1179
|
+
selector: input.selector,
|
|
1180
|
+
found: result?.found === true,
|
|
1181
|
+
...(result?.url ? { url: String(result.url) } : {}),
|
|
1182
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
1183
|
+
...(typeof result?.visible === "boolean"
|
|
1184
|
+
? { visible: result.visible }
|
|
1185
|
+
: {}),
|
|
1186
|
+
...(result?.styles && typeof result.styles === "object"
|
|
1187
|
+
? { styles: result.styles }
|
|
1188
|
+
: {}),
|
|
1189
|
+
...(result?.box &&
|
|
1190
|
+
typeof result.box === "object" &&
|
|
1191
|
+
typeof result.box.x === "number" &&
|
|
1192
|
+
typeof result.box.y === "number" &&
|
|
1193
|
+
typeof result.box.width === "number" &&
|
|
1194
|
+
typeof result.box.height === "number"
|
|
1195
|
+
? {
|
|
1196
|
+
box: {
|
|
1197
|
+
x: Number(result.box.x),
|
|
1198
|
+
y: Number(result.box.y),
|
|
1199
|
+
width: Number(result.box.width),
|
|
1200
|
+
height: Number(result.box.height),
|
|
1201
|
+
},
|
|
1202
|
+
}
|
|
1203
|
+
: {}),
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
520
1206
|
const { sessionId, state } = await this.requireSessionState(input);
|
|
521
1207
|
const properties = input.properties?.length
|
|
522
1208
|
? input.properties
|
|
@@ -556,7 +1242,7 @@ class BrowserService {
|
|
|
556
1242
|
};
|
|
557
1243
|
}, properties)
|
|
558
1244
|
.catch(() => ({ found: false }));
|
|
559
|
-
state.lastUsedAt = new Date()
|
|
1245
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
560
1246
|
state.currentUrl = state.page.url();
|
|
561
1247
|
state.title = await state.page.title().catch(() => state.title);
|
|
562
1248
|
return {
|
|
@@ -583,6 +1269,75 @@ class BrowserService {
|
|
|
583
1269
|
return remote;
|
|
584
1270
|
}
|
|
585
1271
|
this.ensureEnabled();
|
|
1272
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
1273
|
+
if (attached) {
|
|
1274
|
+
const session = await this.sessionStore.getSession(normalizedSessionId);
|
|
1275
|
+
const fileName = sanitizeScreenshotName(input.file_name);
|
|
1276
|
+
const result = await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
1277
|
+
action: "screenshot",
|
|
1278
|
+
});
|
|
1279
|
+
const pngBase64 = typeof result?.png_base64 === "string" ? result.png_base64 : "";
|
|
1280
|
+
if (!pngBase64) {
|
|
1281
|
+
throw new Error("Attached browser screenshot did not return PNG data.");
|
|
1282
|
+
}
|
|
1283
|
+
const pngBuffer = Buffer.from(pngBase64, "base64");
|
|
1284
|
+
const workspaceDir = this.objectStore.resolveWorkspaceDir(session);
|
|
1285
|
+
const exchangeDir = node_path_1.default.resolve(workspaceDir, this.config.exchange.dir);
|
|
1286
|
+
const storedFile = await this.objectStore.storeFile({
|
|
1287
|
+
session,
|
|
1288
|
+
sessionId: normalizedSessionId,
|
|
1289
|
+
source: "browser-screenshot",
|
|
1290
|
+
relativePath: buildDatedRelativePath(fileName),
|
|
1291
|
+
content: pngBuffer,
|
|
1292
|
+
mimeType: "image/png",
|
|
1293
|
+
});
|
|
1294
|
+
await this.xchangeFileMetaStore.setXchangeFileMeta({
|
|
1295
|
+
sessionId: normalizedSessionId,
|
|
1296
|
+
filePath: storedFile.filePath,
|
|
1297
|
+
relativePath: storedFile.relativePath,
|
|
1298
|
+
source: "browser-screenshot",
|
|
1299
|
+
uploadedAt: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
1300
|
+
storageRef: storedFile.storageRef,
|
|
1301
|
+
bucketName: storedFile.bucketName,
|
|
1302
|
+
objectName: storedFile.objectName,
|
|
1303
|
+
vfsNodeId: storedFile.vfsNodeId,
|
|
1304
|
+
vfsPublicUrl: storedFile.vfsPublicUrl,
|
|
1305
|
+
vfsParentId: storedFile.vfsParentId,
|
|
1306
|
+
mimeType: "image/png",
|
|
1307
|
+
sizeBytes: storedFile.sizeBytes,
|
|
1308
|
+
...(input.caption ? { caption: input.caption } : {}),
|
|
1309
|
+
});
|
|
1310
|
+
let telegramMessageId;
|
|
1311
|
+
if (input.send_to_telegram === true) {
|
|
1312
|
+
if (this.config.distributed.mode === "client") {
|
|
1313
|
+
telegramMessageId = await this.sendScreenshotToGatewayTelegramRoute({
|
|
1314
|
+
sessionId: normalizedSessionId,
|
|
1315
|
+
fileName,
|
|
1316
|
+
pngBuffer,
|
|
1317
|
+
...(input.caption ? { caption: input.caption } : {}),
|
|
1318
|
+
});
|
|
1319
|
+
}
|
|
1320
|
+
else {
|
|
1321
|
+
const binding = await this.bindingStore.getBinding(normalizedSessionId);
|
|
1322
|
+
if (!binding) {
|
|
1323
|
+
throw new Error("Session is not linked to Telegram, so screenshot cannot be sent there.");
|
|
1324
|
+
}
|
|
1325
|
+
const sent = await this.telegramTransport.sendDocumentToChat(binding.telegramChatId, storedFile.filePath, input.caption);
|
|
1326
|
+
telegramMessageId = sent.messageId;
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
return {
|
|
1330
|
+
session_id: normalizedSessionId,
|
|
1331
|
+
file_path: storedFile.filePath,
|
|
1332
|
+
workspace_dir: workspaceDir,
|
|
1333
|
+
exchange_dir: exchangeDir,
|
|
1334
|
+
...(typeof telegramMessageId === "number"
|
|
1335
|
+
? { telegram_message_id: telegramMessageId }
|
|
1336
|
+
: {}),
|
|
1337
|
+
...(result?.url ? { url: String(result.url) } : {}),
|
|
1338
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
1339
|
+
};
|
|
1340
|
+
}
|
|
586
1341
|
const { sessionId, state, session } = await this.requireSessionState(input);
|
|
587
1342
|
const fileName = sanitizeScreenshotName(input.file_name);
|
|
588
1343
|
const pngBuffer = input.selector?.trim()
|
|
@@ -609,7 +1364,7 @@ class BrowserService {
|
|
|
609
1364
|
mimeType: "image/png",
|
|
610
1365
|
});
|
|
611
1366
|
const filePath = storedFile.filePath;
|
|
612
|
-
state.lastUsedAt = new Date()
|
|
1367
|
+
state.lastUsedAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
613
1368
|
state.currentUrl = state.page.url();
|
|
614
1369
|
state.title = await state.page.title().catch(() => state.title);
|
|
615
1370
|
this.logger.info("Browser screenshot captured", {
|
|
@@ -623,7 +1378,7 @@ class BrowserService {
|
|
|
623
1378
|
filePath,
|
|
624
1379
|
relativePath: storedFile.relativePath,
|
|
625
1380
|
source: "browser-screenshot",
|
|
626
|
-
uploadedAt: new Date()
|
|
1381
|
+
uploadedAt: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
627
1382
|
storageRef: storedFile.storageRef,
|
|
628
1383
|
bucketName: storedFile.bucketName,
|
|
629
1384
|
objectName: storedFile.objectName,
|
|
@@ -676,6 +1431,17 @@ class BrowserService {
|
|
|
676
1431
|
return remote;
|
|
677
1432
|
}
|
|
678
1433
|
this.ensureEnabled();
|
|
1434
|
+
const attached = await this.getAttachedBrowserAttachment(normalizedSessionId);
|
|
1435
|
+
if (attached) {
|
|
1436
|
+
await this.runAttachedTabAction(normalizedSessionId, attached, {
|
|
1437
|
+
action: "close",
|
|
1438
|
+
});
|
|
1439
|
+
await this.maintenanceStore.clearBrowserAttachment(normalizedSessionId);
|
|
1440
|
+
return {
|
|
1441
|
+
session_id: normalizedSessionId,
|
|
1442
|
+
closed: true,
|
|
1443
|
+
};
|
|
1444
|
+
}
|
|
679
1445
|
const state = this.sessionStates.get(normalizedSessionId);
|
|
680
1446
|
if (state) {
|
|
681
1447
|
await this.closeState(normalizedSessionId, state);
|
|
@@ -749,7 +1515,7 @@ class BrowserService {
|
|
|
749
1515
|
const browser = await this.ensureBrowser();
|
|
750
1516
|
const context = await browser.newContext(this.buildContextOptions(openInput));
|
|
751
1517
|
const page = await context.newPage();
|
|
752
|
-
const createdAt = new Date()
|
|
1518
|
+
const createdAt = (0, localTimestamp_1.formatLocalTimestamp)(new Date());
|
|
753
1519
|
const state = {
|
|
754
1520
|
context,
|
|
755
1521
|
page,
|
|
@@ -766,14 +1532,14 @@ class BrowserService {
|
|
|
766
1532
|
...(formatConsoleLocation(message)
|
|
767
1533
|
? { location: formatConsoleLocation(message) }
|
|
768
1534
|
: {}),
|
|
769
|
-
timestamp: new Date()
|
|
1535
|
+
timestamp: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
770
1536
|
}, this.config.browser.maxEvents);
|
|
771
1537
|
});
|
|
772
1538
|
page.on("pageerror", (error) => {
|
|
773
1539
|
pushBounded(state.pageErrors, {
|
|
774
1540
|
message: error.message,
|
|
775
1541
|
...(error.stack ? { stack: error.stack } : {}),
|
|
776
|
-
timestamp: new Date()
|
|
1542
|
+
timestamp: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
777
1543
|
}, this.config.browser.maxEvents);
|
|
778
1544
|
});
|
|
779
1545
|
page.on("requestfailed", (request) => {
|
|
@@ -823,7 +1589,7 @@ class BrowserService {
|
|
|
823
1589
|
: {}),
|
|
824
1590
|
...(failure?.errorText ? { errorText: failure.errorText } : {}),
|
|
825
1591
|
resourceType: request.resourceType(),
|
|
826
|
-
timestamp: new Date()
|
|
1592
|
+
timestamp: (0, localTimestamp_1.formatLocalTimestamp)(new Date()),
|
|
827
1593
|
}, this.config.browser.maxEvents);
|
|
828
1594
|
}
|
|
829
1595
|
async requireSessionState(input) {
|
|
@@ -875,6 +1641,107 @@ class BrowserService {
|
|
|
875
1641
|
const localSession = await this.sessionStore.getSession(localSessionId);
|
|
876
1642
|
return localSession ? localSessionId : trimmed;
|
|
877
1643
|
}
|
|
1644
|
+
async resolveOptionalSessionIdForRemote(input) {
|
|
1645
|
+
const trimmed = input.session_id?.trim();
|
|
1646
|
+
if (!trimmed) {
|
|
1647
|
+
return undefined;
|
|
1648
|
+
}
|
|
1649
|
+
return await this.normalizeSessionIdForAccess(trimmed);
|
|
1650
|
+
}
|
|
1651
|
+
listFirefoxAttachInstances() {
|
|
1652
|
+
if (!this.firefoxAttachRegistry) {
|
|
1653
|
+
return [];
|
|
1654
|
+
}
|
|
1655
|
+
return this.firefoxAttachRegistry.listInstances();
|
|
1656
|
+
}
|
|
1657
|
+
buildAttachedLocatorPayload(input) {
|
|
1658
|
+
return {
|
|
1659
|
+
...(input.ai_tag?.trim() ? { ai_tag: input.ai_tag.trim() } : {}),
|
|
1660
|
+
...(input.selector?.trim() ? { selector: input.selector.trim() } : {}),
|
|
1661
|
+
...(input.text?.trim() ? { text: input.text.trim() } : {}),
|
|
1662
|
+
...(typeof input.exact === "boolean" ? { exact: input.exact } : {}),
|
|
1663
|
+
};
|
|
1664
|
+
}
|
|
1665
|
+
async getAttachedBrowserAttachment(sessionId) {
|
|
1666
|
+
return await this.maintenanceStore.getBrowserAttachment(sessionId);
|
|
1667
|
+
}
|
|
1668
|
+
mapRecordingRecord(sessionId, record) {
|
|
1669
|
+
return {
|
|
1670
|
+
session_id: sessionId,
|
|
1671
|
+
started: true,
|
|
1672
|
+
backend: "firefox-attached",
|
|
1673
|
+
recording_id: record.recordingId,
|
|
1674
|
+
instance_id: record.instanceId,
|
|
1675
|
+
tab_id: record.tabId,
|
|
1676
|
+
...(record.tabTitle ? { tab_title: record.tabTitle } : {}),
|
|
1677
|
+
...(record.tabUrl ? { tab_url: record.tabUrl } : {}),
|
|
1678
|
+
bundle_dir_name: record.bundleDirName,
|
|
1679
|
+
bundle_relative_path: record.bundleRelativePath,
|
|
1680
|
+
bundle_path: record.bundlePath,
|
|
1681
|
+
started_at: record.startedAt,
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
mapRecordingStatusRecord(record) {
|
|
1685
|
+
return {
|
|
1686
|
+
backend: "firefox-attached",
|
|
1687
|
+
recording_id: record.recordingId,
|
|
1688
|
+
instance_id: record.instanceId,
|
|
1689
|
+
tab_id: record.tabId,
|
|
1690
|
+
...(record.tabTitle ? { tab_title: record.tabTitle } : {}),
|
|
1691
|
+
...(record.tabUrl ? { tab_url: record.tabUrl } : {}),
|
|
1692
|
+
bundle_dir_name: record.bundleDirName,
|
|
1693
|
+
bundle_relative_path: record.bundleRelativePath,
|
|
1694
|
+
bundle_path: record.bundlePath,
|
|
1695
|
+
started_at: record.startedAt,
|
|
1696
|
+
...(record.stoppedAt ? { stopped_at: record.stoppedAt } : {}),
|
|
1697
|
+
status: record.status,
|
|
1698
|
+
event_count: record.eventCount,
|
|
1699
|
+
...(record.lastEventAt ? { last_event_at: record.lastEventAt } : {}),
|
|
1700
|
+
};
|
|
1701
|
+
}
|
|
1702
|
+
async runAttachedTabAction(sessionId, attachment, input) {
|
|
1703
|
+
if (!this.firefoxAttachRegistry) {
|
|
1704
|
+
throw new Error("Attached browser backend is not available.");
|
|
1705
|
+
}
|
|
1706
|
+
const result = await this.firefoxAttachRegistry.invokeTabAction({
|
|
1707
|
+
instanceId: attachment.instanceId,
|
|
1708
|
+
tabId: attachment.tabId,
|
|
1709
|
+
action: input.action,
|
|
1710
|
+
payload: input.payload,
|
|
1711
|
+
});
|
|
1712
|
+
await this.maintenanceStore.setBrowserAttachment({
|
|
1713
|
+
...attachment,
|
|
1714
|
+
...(result?.title ? { title: String(result.title) } : {}),
|
|
1715
|
+
...(result?.url ? { url: String(result.url) } : {}),
|
|
1716
|
+
});
|
|
1717
|
+
this.logger.info("Attached browser tab action executed", {
|
|
1718
|
+
sessionId,
|
|
1719
|
+
instanceId: attachment.instanceId,
|
|
1720
|
+
tabId: attachment.tabId,
|
|
1721
|
+
action: input.action,
|
|
1722
|
+
});
|
|
1723
|
+
return result;
|
|
1724
|
+
}
|
|
1725
|
+
wrapInjectedScript(source, namespace) {
|
|
1726
|
+
return `const __tellyNamespace = ${JSON.stringify(namespace)};
|
|
1727
|
+
window[__tellyNamespace] = window[__tellyNamespace] || {};
|
|
1728
|
+
var TELLY = window[__tellyNamespace];
|
|
1729
|
+
const __tellyBeforeKeys = new Set(Object.getOwnPropertyNames(window));
|
|
1730
|
+
${source}
|
|
1731
|
+
for (const __tellyKey of Object.getOwnPropertyNames(window)) {
|
|
1732
|
+
if (__tellyBeforeKeys.has(__tellyKey)) {
|
|
1733
|
+
continue;
|
|
1734
|
+
}
|
|
1735
|
+
if (__tellyKey === __tellyNamespace) {
|
|
1736
|
+
continue;
|
|
1737
|
+
}
|
|
1738
|
+
try {
|
|
1739
|
+
window[__tellyNamespace][__tellyKey] = window[__tellyKey];
|
|
1740
|
+
} catch {
|
|
1741
|
+
// ignore unassignable globals
|
|
1742
|
+
}
|
|
1743
|
+
}`;
|
|
1744
|
+
}
|
|
878
1745
|
async sendScreenshotToGatewayTelegramRoute(input) {
|
|
879
1746
|
if (!this.config.distributed.gatewayPublicUrl) {
|
|
880
1747
|
throw new Error("send_to_telegram on client nodes requires GATEWAY_PUBLIC_URL.");
|