@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.
Files changed (83) hide show
  1. package/.env.example.client +5 -1
  2. package/.env.example.gateway +16 -1
  3. package/CHANGELOG.md +30 -0
  4. package/README-ru.md +33 -0
  5. package/README.md +33 -0
  6. package/TOOLS.md +384 -9
  7. package/config/templates/env.both.template +15 -2
  8. package/config/templates/env.client.template +5 -2
  9. package/config/templates/env.gateway.template +15 -2
  10. package/dist/cli.js +137 -2
  11. package/dist/services/features/telegram-mcp/browser.service.js +56 -1
  12. package/dist/services/features/telegram-mcp/file-content.service.js +94 -0
  13. package/dist/services/features/telegram-mcp/gateway-delivery.service.js +5 -1
  14. package/dist/services/features/telegram-mcp/gateway-socket.service.js +43 -11
  15. package/dist/services/features/telegram-mcp/mcp-http.service.js +1 -0
  16. package/dist/services/features/telegram-mcp/mcp-server.service.js +30 -0
  17. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +23 -1
  18. package/dist/services/features/telegram-mcp/src/app/config/env.js +132 -2
  19. package/dist/services/features/telegram-mcp/src/app/http.js +139 -99
  20. package/dist/services/features/telegram-mcp/src/app/oauthFacade.js +642 -0
  21. package/dist/services/features/telegram-mcp/src/app/webapp/assets.js +151 -170
  22. package/dist/services/features/telegram-mcp/src/app/webapp/auth.js +96 -99
  23. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +205 -17
  24. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachActiveTabTool.js +28 -0
  25. package/dist/services/features/telegram-mcp/src/features/browser/model/browserAttachTabTool.js +28 -0
  26. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +1 -1
  27. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDetachTabTool.js +28 -0
  28. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +1 -1
  29. package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +1 -1
  30. package/dist/services/features/telegram-mcp/src/features/browser/model/browserInjectScriptTool.js +28 -0
  31. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +33 -0
  32. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +33 -0
  33. package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +1 -1
  34. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStartTool.js +28 -0
  35. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStatusTool.js +28 -0
  36. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStopTool.js +28 -0
  37. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
  38. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +890 -23
  39. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +536 -0
  40. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +80 -0
  41. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +736 -0
  42. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +188 -0
  43. package/dist/services/features/telegram-mcp/src/features/collaboration/model/collaborationService.js +2 -0
  44. package/dist/services/features/telegram-mcp/src/features/collaboration/model/sendPartnerFileService.js +6 -3
  45. package/dist/services/features/telegram-mcp/src/features/distributed-client/model/gatewayClientAccess.js +4 -1
  46. package/dist/services/features/telegram-mcp/src/features/distributed-gateway/model/gatewayHttpService.js +33 -35
  47. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileListTool.js +33 -0
  48. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileService.js +327 -0
  49. package/dist/services/features/telegram-mcp/src/features/file-content/model/getFileTool.js +81 -0
  50. package/dist/services/features/telegram-mcp/src/features/file-content/model/temporaryFileLinkStore.js +307 -0
  51. package/dist/services/features/telegram-mcp/src/features/file-content/model/workspaceFilePolicy.js +115 -0
  52. package/dist/services/features/telegram-mcp/src/features/notify/model/notifyService.js +5 -1
  53. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +28 -0
  54. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transport.js +3 -0
  55. package/dist/services/features/telegram-mcp/src/shared/integrations/telegram/transportFileHandoffActions.js +6 -3
  56. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/client.js +29 -6
  57. package/dist/services/features/telegram-mcp/src/shared/integrations/terminal/ptyRegistry.js +100 -2
  58. package/dist/services/features/telegram-mcp/src/shared/lib/bodyLimits.js +63 -0
  59. package/dist/services/features/telegram-mcp/src/shared/lib/gatewayAuth.js +13 -0
  60. package/dist/services/features/telegram-mcp/src/shared/lib/time/localTimestamp.js +21 -0
  61. package/docs/CHAT_CONNECTOR.md +134 -0
  62. package/docs/STANDALONE-ru.md +55 -6
  63. package/docs/STANDALONE.md +55 -6
  64. package/package.json +10 -5
  65. package/packages/chrome-attach-extension/dist/background.js +1735 -0
  66. package/packages/chrome-attach-extension/dist/icon.svg +6 -0
  67. package/packages/chrome-attach-extension/dist/manifest.json +37 -0
  68. package/packages/chrome-attach-extension/dist/options.html +312 -0
  69. package/packages/chrome-attach-extension/dist/options.js +606 -0
  70. package/packages/chrome-attach-extension/dist/popup.html +93 -0
  71. package/packages/chrome-attach-extension/dist/popup.js +79 -0
  72. package/packages/chrome-attach-extension/dist/recorder-content.js +96 -0
  73. package/packages/chrome-attach-extension/dist/recorder-page.js +282 -0
  74. package/packages/firefox-attach-extension/README.md +13 -0
  75. package/packages/firefox-attach-extension/dist/background.js +1622 -0
  76. package/packages/firefox-attach-extension/dist/icon.svg +6 -0
  77. package/packages/firefox-attach-extension/dist/manifest.json +44 -0
  78. package/packages/firefox-attach-extension/dist/options.html +312 -0
  79. package/packages/firefox-attach-extension/dist/options.js +540 -0
  80. package/packages/firefox-attach-extension/dist/popup.html +93 -0
  81. package/packages/firefox-attach-extension/dist/popup.js +64 -0
  82. package/packages/firefox-attach-extension/dist/recorder-content.js +90 -0
  83. package/packages/firefox-attach-extension/dist/recorder-page.js +318 -0
@@ -124,27 +124,42 @@ function normalizeTelegramWebAppInitData(rawInitData) {
124
124
  }
125
125
  return normalized.trim();
126
126
  }
127
- function normalizeUnsafeUser(user) {
128
- if (!user || typeof user !== "object" || typeof user.id !== "number") {
127
+ function normalizeTelegramWebAppUser(user) {
128
+ if (!user || typeof user !== "object") {
129
+ return null;
130
+ }
131
+ const record = user;
132
+ if (typeof record.id !== "number" ||
133
+ !Number.isSafeInteger(record.id) ||
134
+ record.id <= 0) {
129
135
  return null;
130
136
  }
131
137
  return {
132
- id: user.id,
133
- ...(typeof user.first_name === "string" ? { first_name: user.first_name } : {}),
134
- ...(typeof user.last_name === "string" ? { last_name: user.last_name } : {}),
135
- ...(typeof user.username === "string" ? { username: user.username } : {}),
136
- ...(typeof user.language_code === "string"
137
- ? { language_code: user.language_code }
138
+ id: record.id,
139
+ ...(typeof record.first_name === "string"
140
+ ? { first_name: record.first_name }
141
+ : {}),
142
+ ...(typeof record.last_name === "string"
143
+ ? { last_name: record.last_name }
144
+ : {}),
145
+ ...(typeof record.username === "string"
146
+ ? { username: record.username }
147
+ : {}),
148
+ ...(typeof record.language_code === "string"
149
+ ? { language_code: record.language_code }
150
+ : {}),
151
+ ...(typeof record.photo_url === "string"
152
+ ? { photo_url: record.photo_url }
138
153
  : {}),
139
- ...(typeof user.photo_url === "string" ? { photo_url: user.photo_url } : {}),
140
154
  };
141
155
  }
142
- function buildOfficialRawValidation(rawInitData, botToken) {
156
+ function validateOfficialRawHash(rawInitData, botToken) {
143
157
  const params = new URLSearchParams(rawInitData);
144
- const receivedHash = params.get("hash");
145
- if (!receivedHash) {
146
- return null;
158
+ const receivedHashes = params.getAll("hash");
159
+ if (receivedHashes.length !== 1 || !receivedHashes[0]) {
160
+ throw new Error("Telegram WebApp initData hash validation failed.");
147
161
  }
162
+ const receivedHash = receivedHashes[0];
148
163
  const checkString = [...params.entries()]
149
164
  .filter(([key, value]) => key !== "hash" && value !== undefined)
150
165
  .sort((left, right) => left[0].localeCompare(right[0]))
@@ -158,83 +173,86 @@ function buildOfficialRawValidation(rawInitData, botToken) {
158
173
  .digest("hex");
159
174
  const matches = computed.length === receivedHash.length &&
160
175
  (0, node_crypto_1.timingSafeEqual)(Buffer.from(computed, "utf8"), Buffer.from(receivedHash, "utf8"));
161
- return {
162
- checkString,
163
- computedHash: computed,
164
- providedHash: receivedHash,
165
- matches,
166
- };
176
+ if (!matches) {
177
+ throw new Error("Telegram WebApp initData hash validation failed.");
178
+ }
179
+ return params;
167
180
  }
168
- function buildUserFieldsValidation(unsafe, botToken) {
169
- if (!unsafe) {
170
- return null;
181
+ function readSignedParam(params, key, required) {
182
+ const values = params.getAll(key);
183
+ if (values.length > 1) {
184
+ throw new Error(`Telegram WebApp initData contains duplicate ${key}.`);
171
185
  }
172
- const user = normalizeUnsafeUser(unsafe.user);
173
- const authDateValue = typeof unsafe.auth_date === "number" || typeof unsafe.auth_date === "string"
174
- ? unsafe.auth_date
175
- : undefined;
176
- const receivedHash = typeof unsafe.hash === "string" && unsafe.hash.length > 0
177
- ? unsafe.hash
186
+ const value = values[0];
187
+ if (required && (value === undefined || value.length === 0)) {
188
+ throw new Error(`Telegram WebApp initData is missing ${key}.`);
189
+ }
190
+ return value;
191
+ }
192
+ function parseSignedUser(value) {
193
+ let parsed;
194
+ try {
195
+ parsed = JSON.parse(value);
196
+ }
197
+ catch {
198
+ throw new Error("Telegram WebApp signed user is malformed.");
199
+ }
200
+ const user = normalizeTelegramWebAppUser(parsed);
201
+ if (!user) {
202
+ throw new Error("Telegram WebApp signed user is invalid.");
203
+ }
204
+ return user;
205
+ }
206
+ function assertUnsafeDataMatchesSigned(unsafe, signed) {
207
+ const unsafeUser = normalizeTelegramWebAppUser(unsafe.user);
208
+ if (!unsafeUser ||
209
+ JSON.stringify(unsafeUser) !== JSON.stringify(signed.user)) {
210
+ throw new Error("Telegram WebApp unsafe user does not match signed initData.");
211
+ }
212
+ const unsafeAuthDate = typeof unsafe.auth_date === "number" || typeof unsafe.auth_date === "string"
213
+ ? String(unsafe.auth_date)
178
214
  : undefined;
179
- if (!user || authDateValue === undefined || !receivedHash) {
180
- return null;
215
+ if (unsafeAuthDate !== String(signed.authDate)) {
216
+ throw new Error("Telegram WebApp unsafe auth_date does not match signed initData.");
217
+ }
218
+ if (unsafe.query_id !== signed.queryId) {
219
+ throw new Error("Telegram WebApp unsafe query_id does not match signed initData.");
220
+ }
221
+ if (unsafe.start_param !== signed.startParam) {
222
+ throw new Error("Telegram WebApp unsafe start_param does not match signed initData.");
223
+ }
224
+ if (unsafe.hash !== undefined && unsafe.hash !== signed.hash) {
225
+ throw new Error("Telegram WebApp unsafe hash does not match signed initData.");
181
226
  }
182
- const params = {
183
- id: user.id,
184
- first_name: user.first_name,
185
- last_name: user.last_name,
186
- username: user.username,
187
- photo_url: user.photo_url,
188
- auth_date: authDateValue,
189
- };
190
- const checkString = Object.keys(params)
191
- .sort()
192
- .filter((key) => params[key] !== undefined)
193
- .map((key) => `${key}=${params[key]}`)
194
- .join("\n");
195
- const secretKey = (0, node_crypto_1.createHash)("sha256")
196
- .update(botToken.trim())
197
- .digest();
198
- const computed = (0, node_crypto_1.createHmac)("sha256", secretKey)
199
- .update(checkString)
200
- .digest("hex");
201
- const matches = computed.length === receivedHash.length &&
202
- (0, node_crypto_1.timingSafeEqual)(Buffer.from(computed, "utf8"), Buffer.from(receivedHash, "utf8"));
203
- return {
204
- checkString,
205
- computedHash: computed,
206
- providedHash: receivedHash,
207
- matches,
208
- };
209
227
  }
210
228
  function validateTelegramWebAppInitData(rawInitData, unsafeInitData, botToken, maxAgeSeconds) {
211
229
  const normalizedRaw = rawInitData
212
230
  ? normalizeTelegramWebAppInitData(rawInitData)
213
231
  : "";
214
- const normalizedUnsafe = unsafeInitData ?? null;
215
- if (!normalizedRaw && !normalizedUnsafe) {
232
+ if (!normalizedRaw) {
216
233
  throw new Error("Telegram WebApp initData is empty.");
217
234
  }
218
- const officialRawValidation = buildOfficialRawValidation(normalizedRaw, botToken);
219
- const userFieldsValidation = buildUserFieldsValidation(normalizedUnsafe, botToken);
220
- if (!officialRawValidation?.matches && !userFieldsValidation?.matches) {
221
- throw new Error("Telegram WebApp initData hash validation failed.");
222
- }
223
- if (!normalizedUnsafe) {
224
- throw new Error("Telegram WebApp unsafe init data is missing.");
225
- }
226
- const user = normalizeUnsafeUser(normalizedUnsafe.user);
227
- const authDateRaw = typeof normalizedUnsafe.auth_date === "number" ||
228
- typeof normalizedUnsafe.auth_date === "string"
229
- ? String(normalizedUnsafe.auth_date)
230
- : null;
231
- if (!authDateRaw || !user) {
232
- throw new Error("Telegram WebApp initData is missing auth_date or user.");
235
+ const signedParams = validateOfficialRawHash(normalizedRaw, botToken);
236
+ const user = parseSignedUser(readSignedParam(signedParams, "user", true));
237
+ const authDateRaw = readSignedParam(signedParams, "auth_date", true);
238
+ if (!/^\d+$/.test(authDateRaw)) {
239
+ throw new Error("Telegram WebApp auth_date is invalid.");
233
240
  }
234
- const authDate = Number.parseInt(authDateRaw, 10);
235
- if (!Number.isFinite(authDate) || authDate <= 0) {
241
+ const authDate = Number(authDateRaw);
242
+ if (!Number.isSafeInteger(authDate) || authDate <= 0) {
236
243
  throw new Error("Telegram WebApp auth_date is invalid.");
237
244
  }
245
+ const queryId = readSignedParam(signedParams, "query_id", false);
246
+ const startParam = readSignedParam(signedParams, "start_param", false);
247
+ if (unsafeInitData) {
248
+ assertUnsafeDataMatchesSigned(unsafeInitData, {
249
+ user,
250
+ authDate,
251
+ ...(queryId !== undefined ? { queryId } : {}),
252
+ ...(startParam !== undefined ? { startParam } : {}),
253
+ hash: readSignedParam(signedParams, "hash", true),
254
+ });
255
+ }
238
256
  const nowSeconds = Math.floor(Date.now() / 1000);
239
257
  if (nowSeconds - authDate > maxAgeSeconds) {
240
258
  throw new Error("Telegram WebApp initData is expired.");
@@ -243,28 +261,7 @@ function validateTelegramWebAppInitData(rawInitData, unsafeInitData, botToken, m
243
261
  user,
244
262
  authDate,
245
263
  rawInitData: normalizedRaw,
246
- ...(typeof normalizedUnsafe.query_id === "string"
247
- ? { queryId: normalizedUnsafe.query_id }
248
- : {}),
249
- ...(typeof normalizedUnsafe.start_param === "string"
250
- ? { startParam: normalizedUnsafe.start_param }
251
- : {}),
252
- validationDebug: {
253
- providedHash: officialRawValidation?.providedHash ??
254
- userFieldsValidation?.providedHash ??
255
- "",
256
- officialRaw: {
257
- checkString: officialRawValidation?.checkString ?? "",
258
- computedHash: officialRawValidation?.computedHash ?? "",
259
- matches: officialRawValidation?.matches ?? false,
260
- },
261
- userFields: userFieldsValidation
262
- ? {
263
- checkString: userFieldsValidation.checkString,
264
- computedHash: userFieldsValidation.computedHash,
265
- matches: userFieldsValidation.matches,
266
- }
267
- : null,
268
- },
264
+ ...(queryId !== undefined ? { queryId } : {}),
265
+ ...(startParam !== undefined ? { startParam } : {}),
269
266
  };
270
267
  }
@@ -33,9 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.sendPartnerFileInputSchema = exports.sendPartnerNoteInputSchema = exports.browserCloseOutputSchema = exports.browserCloseInputSchema = exports.browserScreenshotOutputSchema = exports.browserScreenshotInputSchema = exports.browserComputedStyleOutputSchema = exports.browserComputedStyleInputSchema = exports.browserDomOutputSchema = exports.browserDomInputSchema = exports.browserClearLogsOutputSchema = exports.browserClearLogsInputSchema = exports.browserNetworkFailuresOutputSchema = exports.browserNetworkFailuresInputSchema = exports.browserErrorsOutputSchema = exports.browserErrorsInputSchema = exports.browserConsoleOutputSchema = exports.browserConsoleInputSchema = exports.browserWaitForUrlOutputSchema = exports.browserWaitForUrlInputSchema = exports.browserWaitForOutputSchema = exports.browserWaitForInputSchema = exports.browserPressOutputSchema = exports.browserPressInputSchema = exports.browserFillOutputSchema = exports.browserFillInputSchema = exports.browserClickOutputSchema = exports.browserClickInputSchema = exports.browserReloadOutputSchema = exports.browserReloadInputSchema = exports.browserOpenOutputSchema = exports.browserOpenInputSchema = exports.clearSessionContextOutputSchema = exports.clearSessionContextInputSchema = exports.getSessionContextOutputSchema = exports.getSessionContextInputSchema = exports.setSessionContextOutputSchema = exports.renameSessionOutputSchema = exports.renameSessionInputSchema = exports.setSessionContextInputSchema = exports.refreshToolsMarkdownOutputSchema = exports.refreshToolsMarkdownInputSchema = exports.listGatewaySessionsOutputSchema = exports.listGatewaySessionsInputSchema = exports.sendFileToTelegramOutputSchema = exports.sendFileToTelegramInputSchema = exports.notifyTelegramOutputSchema = exports.notifyTelegramInputSchema = exports.askUserTelegramOutputSchema = exports.askUserTelegramInputSchema = void 0;
37
- exports.markXchangeRecordReadOutputSchema = exports.markXchangeRecordReadInputSchema = exports.getXchangeRecordOutputSchema = exports.getXchangeRecordInputSchema = exports.listXchangeRecordsOutputSchema = exports.listXchangeRecordsInputSchema = exports.sendPartnerNoteOutputSchema = void 0;
36
+ exports.browserWaitForInputSchema = exports.browserInjectScriptOutputSchema = exports.browserInjectScriptInputSchema = exports.browserPressOutputSchema = exports.browserPressInputSchema = exports.browserFillOutputSchema = exports.browserFillInputSchema = exports.browserClickOutputSchema = exports.browserClickInputSchema = exports.browserReloadOutputSchema = exports.browserReloadInputSchema = exports.browserRecordingStatusOutputSchema = exports.browserRecordingStatusInputSchema = exports.browserRecordingStopOutputSchema = exports.browserRecordingStopInputSchema = exports.browserRecordingStartOutputSchema = exports.browserRecordingStartInputSchema = exports.browserDetachTabOutputSchema = exports.browserDetachTabInputSchema = exports.browserAttachTabOutputSchema = exports.browserAttachTabInputSchema = exports.browserAttachActiveTabInputSchema = exports.browserListTabsOutputSchema = exports.browserListTabsInputSchema = exports.browserListAttachedInstancesOutputSchema = exports.browserListAttachedInstancesInputSchema = exports.browserOpenOutputSchema = exports.browserOpenInputSchema = exports.clearSessionContextOutputSchema = exports.clearSessionContextInputSchema = exports.getSessionContextOutputSchema = exports.getSessionContextInputSchema = exports.setSessionContextOutputSchema = exports.renameSessionOutputSchema = exports.renameSessionInputSchema = exports.setSessionContextInputSchema = exports.refreshToolsMarkdownOutputSchema = exports.refreshToolsMarkdownInputSchema = exports.listGatewaySessionsOutputSchema = exports.listGatewaySessionsInputSchema = exports.getFileListOutputSchema = exports.getFileListInputSchema = exports.getFileOutputSchema = exports.getFileInputSchema = exports.sendFileToTelegramOutputSchema = exports.sendFileToTelegramInputSchema = exports.notifyTelegramOutputSchema = exports.notifyTelegramInputSchema = exports.askUserTelegramOutputSchema = exports.askUserTelegramInputSchema = void 0;
37
+ exports.markXchangeRecordReadOutputSchema = exports.markXchangeRecordReadInputSchema = exports.getXchangeRecordOutputSchema = exports.getXchangeRecordInputSchema = exports.listXchangeRecordsOutputSchema = exports.listXchangeRecordsInputSchema = exports.sendPartnerNoteOutputSchema = exports.sendPartnerFileInputSchema = exports.sendPartnerNoteInputSchema = exports.browserCloseOutputSchema = exports.browserCloseInputSchema = exports.browserScreenshotOutputSchema = exports.browserScreenshotInputSchema = exports.browserComputedStyleOutputSchema = exports.browserComputedStyleInputSchema = exports.browserDomOutputSchema = exports.browserDomInputSchema = exports.browserClearLogsOutputSchema = exports.browserClearLogsInputSchema = exports.browserNetworkFailuresOutputSchema = exports.browserNetworkFailuresInputSchema = exports.browserErrorsOutputSchema = exports.browserErrorsInputSchema = exports.browserConsoleOutputSchema = exports.browserConsoleInputSchema = exports.browserWaitForUrlOutputSchema = exports.browserWaitForUrlInputSchema = exports.browserWaitForOutputSchema = void 0;
38
38
  const z = __importStar(require("zod/v4"));
39
+ const bodyLimits_1 = require("../../../shared/lib/bodyLimits");
40
+ const bodyStringSchema = z.string().max(bodyLimits_1.MAX_BODY_SIZE_BYTES);
39
41
  const partnerNoteKindSchema = z.enum([
40
42
  "share",
41
43
  "question",
@@ -87,6 +89,45 @@ exports.sendFileToTelegramOutputSchema = z.object({
87
89
  sent: z.boolean(),
88
90
  message_id: z.number().int().positive().optional(),
89
91
  });
92
+ exports.getFileInputSchema = z
93
+ .object({
94
+ session_id: z.string().trim().min(1).optional(),
95
+ file_path: z.string().trim().min(1).optional(),
96
+ selector: z.enum(["latest_screenshot"]).optional(),
97
+ type: z.enum(["url", "image", "text", "base64"]).optional(),
98
+ })
99
+ .refine((input) => Boolean(input.file_path) !== Boolean(input.selector), {
100
+ message: "Provide exactly one of file_path or selector.",
101
+ });
102
+ exports.getFileOutputSchema = z.object({
103
+ type: z.enum(["url", "image", "text", "base64"]),
104
+ data: bodyStringSchema,
105
+ mimetype: z.string().trim().min(1),
106
+ filename: z.string().trim().min(1),
107
+ size_bytes: z.number().int().nonnegative(),
108
+ expires_at: z.string().trim().min(1).optional(),
109
+ });
110
+ const getFileListSourceSchema = z.enum([
111
+ "telegram-upload",
112
+ "browser-screenshot",
113
+ "partner-artifact",
114
+ ]);
115
+ exports.getFileListInputSchema = z.object({
116
+ session_id: z.string().trim().min(1).optional(),
117
+ source: getFileListSourceSchema.optional(),
118
+ limit: z.number().int().positive().max(200).optional(),
119
+ });
120
+ exports.getFileListOutputSchema = z.object({
121
+ total: z.number().int().nonnegative(),
122
+ files: z.array(z.object({
123
+ file_path: z.string().trim().min(1),
124
+ filename: z.string().trim().min(1),
125
+ mimetype: z.string().trim().min(1),
126
+ source: getFileListSourceSchema,
127
+ size_bytes: z.number().int().nonnegative().optional(),
128
+ created_at: z.string().trim().min(1),
129
+ })),
130
+ });
90
131
  exports.listGatewaySessionsInputSchema = z.object({
91
132
  client_uuid: z.string().trim().min(1).optional(),
92
133
  connected_only: z.boolean().optional(),
@@ -210,6 +251,131 @@ exports.browserOpenOutputSchema = z.object({
210
251
  viewport_width: z.number().int().positive().optional(),
211
252
  viewport_height: z.number().int().positive().optional(),
212
253
  });
254
+ exports.browserListAttachedInstancesInputSchema = z.object({
255
+ session_id: z.string().trim().min(1).optional(),
256
+ });
257
+ exports.browserListAttachedInstancesOutputSchema = z.object({
258
+ session_id: z.string().trim().min(1).optional(),
259
+ total: z.number().int().nonnegative(),
260
+ instances: z.array(z.object({
261
+ instance_id: z.string().trim().min(1),
262
+ browser: z.enum(["firefox", "chrome"]),
263
+ extension_version: z.string().trim().min(1),
264
+ profile_name: z.string().trim().min(1).optional(),
265
+ connected_at: z.string().trim().min(1),
266
+ last_seen_at: z.string().trim().min(1),
267
+ capabilities: z.array(z.string().trim().min(1)),
268
+ tab_count: z.number().int().nonnegative(),
269
+ active_tab: z
270
+ .object({
271
+ tab_id: z.number().int().nonnegative(),
272
+ window_id: z.number().int().nonnegative().optional(),
273
+ active: z.boolean(),
274
+ title: z.string(),
275
+ url: z.string(),
276
+ status: z.string().optional(),
277
+ })
278
+ .optional(),
279
+ })),
280
+ });
281
+ exports.browserListTabsInputSchema = z.object({
282
+ session_id: z.string().trim().min(1).optional(),
283
+ instance_id: z.string().trim().min(1).optional(),
284
+ });
285
+ exports.browserListTabsOutputSchema = z.object({
286
+ session_id: z.string().trim().min(1).optional(),
287
+ instance_id: z.string().trim().min(1),
288
+ total: z.number().int().nonnegative(),
289
+ tabs: z.array(z.object({
290
+ tab_id: z.number().int().nonnegative(),
291
+ window_id: z.number().int().nonnegative().optional(),
292
+ active: z.boolean(),
293
+ selected: z.boolean().optional(),
294
+ title: z.string(),
295
+ url: z.string(),
296
+ status: z.string().optional(),
297
+ })),
298
+ });
299
+ exports.browserAttachActiveTabInputSchema = z.object({
300
+ session_id: z.string().trim().min(1).optional(),
301
+ instance_id: z.string().trim().min(1).optional(),
302
+ });
303
+ exports.browserAttachTabInputSchema = z.object({
304
+ session_id: z.string().trim().min(1).optional(),
305
+ instance_id: z.string().trim().min(1).optional(),
306
+ tab_id: z.number().int().nonnegative(),
307
+ });
308
+ exports.browserAttachTabOutputSchema = z.object({
309
+ session_id: z.string().trim().min(1),
310
+ backend: z.literal("firefox-attached"),
311
+ instance_id: z.string().trim().min(1),
312
+ tab_id: z.number().int().nonnegative(),
313
+ attached_at: z.string().trim().min(1),
314
+ title: z.string().optional(),
315
+ url: z.string().optional(),
316
+ });
317
+ exports.browserDetachTabInputSchema = z.object({
318
+ session_id: z.string().trim().min(1).optional(),
319
+ });
320
+ exports.browserDetachTabOutputSchema = z.object({
321
+ session_id: z.string().trim().min(1),
322
+ detached: z.boolean(),
323
+ });
324
+ exports.browserRecordingStartInputSchema = z.object({
325
+ session_id: z.string().trim().min(1).optional(),
326
+ instance_id: z.string().trim().min(1).optional(),
327
+ });
328
+ exports.browserRecordingStartOutputSchema = z.object({
329
+ session_id: z.string().trim().min(1),
330
+ backend: z.literal("firefox-attached"),
331
+ started: z.boolean(),
332
+ recording_id: z.string().trim().min(1),
333
+ instance_id: z.string().trim().min(1),
334
+ tab_id: z.number().int().nonnegative(),
335
+ tab_title: z.string().optional(),
336
+ tab_url: z.string().optional(),
337
+ bundle_dir_name: z.string().trim().min(1),
338
+ bundle_relative_path: z.string().trim().min(1),
339
+ bundle_path: z.string().trim().min(1),
340
+ started_at: z.string().trim().min(1),
341
+ });
342
+ exports.browserRecordingStopInputSchema = z.object({
343
+ session_id: z.string().trim().min(1).optional(),
344
+ });
345
+ exports.browserRecordingStopOutputSchema = z.object({
346
+ session_id: z.string().trim().min(1),
347
+ stopped: z.boolean(),
348
+ recording_id: z.string().trim().min(1).optional(),
349
+ bundle_dir_name: z.string().trim().min(1).optional(),
350
+ bundle_relative_path: z.string().trim().min(1).optional(),
351
+ bundle_path: z.string().trim().min(1).optional(),
352
+ stopped_at: z.string().trim().min(1).optional(),
353
+ });
354
+ exports.browserRecordingStatusInputSchema = z.object({
355
+ session_id: z.string().trim().min(1).optional(),
356
+ });
357
+ exports.browserRecordingStatusOutputSchema = z.object({
358
+ session_id: z.string().trim().min(1),
359
+ active: z.boolean(),
360
+ recording: z
361
+ .object({
362
+ backend: z.literal("firefox-attached"),
363
+ recording_id: z.string().trim().min(1),
364
+ instance_id: z.string().trim().min(1),
365
+ tab_id: z.number().int().nonnegative(),
366
+ tab_title: z.string().optional(),
367
+ tab_url: z.string().optional(),
368
+ bundle_dir_name: z.string().trim().min(1),
369
+ bundle_relative_path: z.string().trim().min(1),
370
+ bundle_path: z.string().trim().min(1),
371
+ started_at: z.string().trim().min(1),
372
+ stopped_at: z.string().trim().min(1).optional(),
373
+ status: z.enum(["recording", "stopped"]),
374
+ event_count: z.number().int().nonnegative(),
375
+ last_event_at: z.string().trim().min(1).optional(),
376
+ })
377
+ .optional(),
378
+ });
213
379
  exports.browserReloadInputSchema = z.object({
214
380
  session_id: z.string().trim().min(1).optional(),
215
381
  wait_until: z
@@ -219,7 +385,7 @@ exports.browserReloadInputSchema = z.object({
219
385
  exports.browserReloadOutputSchema = z.object({
220
386
  session_id: z.string(),
221
387
  reloaded: z.boolean(),
222
- url: z.string().url(),
388
+ url: z.string(),
223
389
  title: z.string().optional(),
224
390
  });
225
391
  const browserLocatorInputShape = {
@@ -242,7 +408,7 @@ exports.browserClickOutputSchema = z.object({
242
408
  ai_tag: z.string().optional(),
243
409
  selector: z.string().optional(),
244
410
  text: z.string().optional(),
245
- url: z.string().url(),
411
+ url: z.string(),
246
412
  title: z.string().optional(),
247
413
  });
248
414
  exports.browserFillInputSchema = z
@@ -261,7 +427,7 @@ exports.browserFillOutputSchema = z.object({
261
427
  selector: z.string().optional(),
262
428
  text: z.string().optional(),
263
429
  value_length: z.number().int().nonnegative(),
264
- url: z.string().url(),
430
+ url: z.string(),
265
431
  title: z.string().optional(),
266
432
  });
267
433
  exports.browserPressInputSchema = z.object({
@@ -275,7 +441,27 @@ exports.browserPressOutputSchema = z.object({
275
441
  ai_tag: z.string().optional(),
276
442
  selector: z.string().optional(),
277
443
  text: z.string().optional(),
278
- url: z.string().url(),
444
+ url: z.string(),
445
+ title: z.string().optional(),
446
+ });
447
+ exports.browserInjectScriptInputSchema = z
448
+ .object({
449
+ session_id: z.string().trim().min(1).optional(),
450
+ source: z.string().optional(),
451
+ file_path: z.string().trim().min(1).optional(),
452
+ namespace: z.string().trim().min(1).optional(),
453
+ })
454
+ .refine((input) => Boolean(input.source?.trim() || input.file_path?.trim()), {
455
+ message: "Provide source or file_path.",
456
+ path: ["source"],
457
+ });
458
+ exports.browserInjectScriptOutputSchema = z.object({
459
+ session_id: z.string(),
460
+ injected: z.boolean(),
461
+ namespace: z.string(),
462
+ source_type: z.enum(["inline", "file"]),
463
+ bytes: z.number().int().nonnegative(),
464
+ url: z.string(),
279
465
  title: z.string().optional(),
280
466
  });
281
467
  exports.browserWaitForInputSchema = z
@@ -294,7 +480,7 @@ exports.browserWaitForOutputSchema = z.object({
294
480
  ai_tag: z.string().optional(),
295
481
  selector: z.string().optional(),
296
482
  text: z.string().optional(),
297
- url: z.string().url(),
483
+ url: z.string(),
298
484
  title: z.string().optional(),
299
485
  });
300
486
  exports.browserWaitForUrlInputSchema = z
@@ -314,7 +500,7 @@ exports.browserWaitForUrlOutputSchema = z.object({
314
500
  matched: z.enum(["url", "url_contains"]),
315
501
  url: z.string().optional(),
316
502
  url_contains: z.string().optional(),
317
- current_url: z.string().url(),
503
+ current_url: z.string(),
318
504
  title: z.string().optional(),
319
505
  });
320
506
  exports.browserConsoleInputSchema = z.object({
@@ -352,7 +538,7 @@ exports.browserNetworkFailuresOutputSchema = z.object({
352
538
  session_id: z.string(),
353
539
  total: z.number().int().nonnegative(),
354
540
  failures: z.array(z.object({
355
- url: z.string().url(),
541
+ url: z.string(),
356
542
  method: z.string(),
357
543
  status: z.number().int().optional(),
358
544
  error_text: z.string().optional(),
@@ -440,9 +626,9 @@ exports.sendPartnerNoteInputSchema = z.object({
440
626
  target_local_session_id: z.string().trim().min(1).optional(),
441
627
  project_uuid: z.string().trim().min(1).optional(),
442
628
  kind: partnerNoteKindSchema,
443
- summary: z.string().trim().min(1),
444
- message: z.string().trim().min(1),
445
- expected_reply: z.string().trim().min(1).optional(),
629
+ summary: bodyStringSchema.trim().min(1),
630
+ message: bodyStringSchema.trim().min(1),
631
+ expected_reply: bodyStringSchema.trim().min(1).optional(),
446
632
  requires_reply: z.boolean().optional(),
447
633
  in_reply_to: z.string().trim().min(1).optional(),
448
634
  artifacts: z.array(z.string().trim().min(1)).optional(),
@@ -454,7 +640,7 @@ exports.sendPartnerNoteInputSchema = z.object({
454
640
  mime_type: z.string().trim().min(1).optional(),
455
641
  size_bytes: z.number().int().nonnegative().optional(),
456
642
  storage_ref: z.string().trim().min(1).optional(),
457
- content_base64: z.string().trim().min(1).optional(),
643
+ content_base64: bodyStringSchema.trim().min(1).optional(),
458
644
  }))
459
645
  .optional(),
460
646
  });
@@ -467,9 +653,9 @@ exports.sendPartnerFileInputSchema = z.object({
467
653
  cwd: z.string().trim().min(1).optional(),
468
654
  file_path: z.string().trim().min(1),
469
655
  kind: partnerNoteKindSchema.optional(),
470
- summary: z.string().trim().min(1).optional(),
471
- message: z.string().trim().min(1).optional(),
472
- expected_reply: z.string().trim().min(1).optional(),
656
+ summary: bodyStringSchema.trim().min(1).optional(),
657
+ message: bodyStringSchema.trim().min(1).optional(),
658
+ expected_reply: bodyStringSchema.trim().min(1).optional(),
473
659
  requires_reply: z.boolean().optional(),
474
660
  in_reply_to: z.string().trim().min(1).optional(),
475
661
  });
@@ -533,7 +719,9 @@ const xchangeRecordSchema = z.object({
533
719
  exports.listXchangeRecordsInputSchema = z.object({
534
720
  session_id: z.string().trim().min(1).optional(),
535
721
  status: z.enum(["new", "read", "archived"]).optional(),
536
- category: z.enum(["partner_note", "local_handoff", "telegram_message"]).optional(),
722
+ category: z
723
+ .enum(["partner_note", "local_handoff", "telegram_message"])
724
+ .optional(),
537
725
  direction: z.enum(["incoming", "outgoing", "local"]).optional(),
538
726
  limit: z.number().int().positive().max(200).optional(),
539
727
  });
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrowserAttachActiveTabTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ function createContent(output) {
6
+ return [{ type: "text", text: JSON.stringify(output, null, 2) }];
7
+ }
8
+ class BrowserAttachActiveTabTool {
9
+ browserService;
10
+ constructor(browserService) {
11
+ this.browserService = browserService;
12
+ }
13
+ register(server) {
14
+ server.registerTool("browser_attach_active_tab", {
15
+ title: "Browser Attach Active Tab",
16
+ description: "Attach the current MCP session to the active tab of a connected attached browser instance. If only one instance is connected, instance_id can be omitted.",
17
+ inputSchema: schema_1.browserAttachActiveTabInputSchema,
18
+ outputSchema: schema_1.browserAttachTabOutputSchema,
19
+ }, async (args) => {
20
+ const output = await this.browserService.attachActiveTab(args);
21
+ return {
22
+ content: createContent(output),
23
+ structuredContent: output,
24
+ };
25
+ });
26
+ }
27
+ }
28
+ exports.BrowserAttachActiveTabTool = BrowserAttachActiveTabTool;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrowserAttachTabTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ function createContent(output) {
6
+ return [{ type: "text", text: JSON.stringify(output, null, 2) }];
7
+ }
8
+ class BrowserAttachTabTool {
9
+ browserService;
10
+ constructor(browserService) {
11
+ this.browserService = browserService;
12
+ }
13
+ register(server) {
14
+ server.registerTool("browser_attach_tab", {
15
+ title: "Browser Attach Tab",
16
+ description: "Attach the current MCP session to a specific tab in a connected attached browser instance by tab_id.",
17
+ inputSchema: schema_1.browserAttachTabInputSchema,
18
+ outputSchema: schema_1.browserAttachTabOutputSchema,
19
+ }, async (args) => {
20
+ const output = await this.browserService.attachTab(args);
21
+ return {
22
+ content: createContent(output),
23
+ structuredContent: output,
24
+ };
25
+ });
26
+ }
27
+ }
28
+ exports.BrowserAttachTabTool = BrowserAttachTabTool;
@@ -13,7 +13,7 @@ class BrowserClickTool {
13
13
  register(server) {
14
14
  server.registerTool("browser_click", {
15
15
  title: "Browser Click",
16
- description: "Click an element in the session browser page by CSS selector or visible text.",
16
+ description: "Click an element in the current session browser target by CSS selector or visible text. If the session has a selected attached Firefox tab, click there; otherwise use the isolated Playwright page.",
17
17
  inputSchema: schema_1.browserClickInputSchema,
18
18
  outputSchema: schema_1.browserClickOutputSchema,
19
19
  }, async (args) => {
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BrowserDetachTabTool = void 0;
4
+ const schema_1 = require("../../../entities/request/model/schema");
5
+ function createContent(output) {
6
+ return [{ type: "text", text: JSON.stringify(output, null, 2) }];
7
+ }
8
+ class BrowserDetachTabTool {
9
+ browserService;
10
+ constructor(browserService) {
11
+ this.browserService = browserService;
12
+ }
13
+ register(server) {
14
+ server.registerTool("browser_detach_tab", {
15
+ title: "Browser Detach Tab",
16
+ description: "Detach the current MCP session from the currently selected attached browser tab.",
17
+ inputSchema: schema_1.browserDetachTabInputSchema,
18
+ outputSchema: schema_1.browserDetachTabOutputSchema,
19
+ }, async (args) => {
20
+ const output = await this.browserService.detachTab(args);
21
+ return {
22
+ content: createContent(output),
23
+ structuredContent: output,
24
+ };
25
+ });
26
+ }
27
+ }
28
+ exports.BrowserDetachTabTool = BrowserDetachTabTool;
@@ -13,7 +13,7 @@ class BrowserDomTool {
13
13
  register(server) {
14
14
  server.registerTool("browser_dom", {
15
15
  title: "Browser DOM",
16
- description: "Inspect a DOM element in the Playwright tab for the current session and return text, HTML, attributes, and visibility.",
16
+ description: "Inspect a DOM element in the current session browser target and return text, HTML, attributes, and visibility. If the session has a selected attached Firefox tab, inspect that tab; otherwise use the isolated Playwright page.",
17
17
  inputSchema: schema_1.browserDomInputSchema,
18
18
  outputSchema: schema_1.browserDomOutputSchema,
19
19
  }, async (args) => {