@deadragdoll/tellymcp 0.0.12 → 0.0.13

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 (48) hide show
  1. package/README-ru.md +19 -0
  2. package/README.md +19 -0
  3. package/TOOLS.md +206 -3
  4. package/dist/cli.js +109 -1
  5. package/dist/services/features/telegram-mcp/browser.service.js +38 -1
  6. package/dist/services/features/telegram-mcp/mcp-server.service.js +12 -0
  7. package/dist/services/features/telegram-mcp/src/app/bootstrap/runtime.js +14 -0
  8. package/dist/services/features/telegram-mcp/src/app/config/env.js +13 -0
  9. package/dist/services/features/telegram-mcp/src/entities/request/model/schema.js +147 -2
  10. package/dist/services/features/telegram-mcp/src/features/browser/model/browserClickTool.js +1 -1
  11. package/dist/services/features/telegram-mcp/src/features/browser/model/browserDomTool.js +1 -1
  12. package/dist/services/features/telegram-mcp/src/features/browser/model/browserFillTool.js +1 -1
  13. package/dist/services/features/telegram-mcp/src/features/browser/model/browserInjectScriptTool.js +28 -0
  14. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListAttachedInstancesTool.js +33 -0
  15. package/dist/services/features/telegram-mcp/src/features/browser/model/browserListTabsTool.js +33 -0
  16. package/dist/services/features/telegram-mcp/src/features/browser/model/browserPressTool.js +1 -1
  17. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStartTool.js +28 -0
  18. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStatusTool.js +28 -0
  19. package/dist/services/features/telegram-mcp/src/features/browser/model/browserRecordingStopTool.js +28 -0
  20. package/dist/services/features/telegram-mcp/src/features/browser/model/browserScreenshotTool.js +1 -1
  21. package/dist/services/features/telegram-mcp/src/features/browser/model/browserService.js +485 -1
  22. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/browserRecordingBundle.js +502 -0
  23. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachRegistry.js +79 -0
  24. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/firefoxAttachServer.js +559 -0
  25. package/dist/services/features/telegram-mcp/src/features/browser-attach/model/types.js +2 -0
  26. package/dist/services/features/telegram-mcp/src/shared/integrations/redis/stateStore.js +28 -0
  27. package/docs/STANDALONE-ru.md +42 -6
  28. package/docs/STANDALONE.md +42 -6
  29. package/package.json +6 -3
  30. package/packages/chrome-attach-extension/dist/background.js +1326 -0
  31. package/packages/chrome-attach-extension/dist/icon.svg +6 -0
  32. package/packages/chrome-attach-extension/dist/manifest.json +36 -0
  33. package/packages/chrome-attach-extension/dist/options.html +312 -0
  34. package/packages/chrome-attach-extension/dist/options.js +593 -0
  35. package/packages/chrome-attach-extension/dist/popup.html +93 -0
  36. package/packages/chrome-attach-extension/dist/popup.js +79 -0
  37. package/packages/chrome-attach-extension/dist/recorder-content.js +83 -0
  38. package/packages/chrome-attach-extension/dist/recorder-page.js +266 -0
  39. package/packages/firefox-attach-extension/README.md +13 -0
  40. package/packages/firefox-attach-extension/dist/background.js +1242 -0
  41. package/packages/firefox-attach-extension/dist/icon.svg +6 -0
  42. package/packages/firefox-attach-extension/dist/manifest.json +56 -0
  43. package/packages/firefox-attach-extension/dist/options.html +312 -0
  44. package/packages/firefox-attach-extension/dist/options.js +527 -0
  45. package/packages/firefox-attach-extension/dist/popup.html +93 -0
  46. package/packages/firefox-attach-extension/dist/popup.js +64 -0
  47. package/packages/firefox-attach-extension/dist/recorder-content.js +77 -0
  48. package/packages/firefox-attach-extension/dist/recorder-page.js +302 -0
package/README-ru.md CHANGED
@@ -29,6 +29,9 @@
29
29
  - позволяет одной консоли агента ставить задачу другой консоли
30
30
  - хранит structured xchange records в `.mcp-xchange`
31
31
  - поддерживает browser automation через Playwright
32
+ - умеет attach к уже открытому Firefox или Chrome через bundled local extensions
33
+ - умеет писать structured browser bundles в `.mcp-xchange/web/...` с HTML, network и console артефактами
34
+ - умеет инжектить helper scripts в attached tabs или Playwright pages через `browser_inject_script`
32
35
  - отдаёт Telegram Mini App / Live View с gateway
33
36
  - поддерживает polling и webhook на gateway
34
37
  - поставляет встроенный Codex plugin со skills под типовые workflow
@@ -76,9 +79,13 @@ Gateway
76
79
  - `browser_open`
77
80
  - `browser_click`
78
81
  - `browser_fill`
82
+ - `browser_inject_script`
79
83
  - `browser_press`
80
84
  - `browser_wait_for`
81
85
  - `browser_screenshot`
86
+ - `browser_recording_start`
87
+ - `browser_recording_stop`
88
+ - `browser_recording_status`
82
89
 
83
90
  Синхронизация инструкций:
84
91
 
@@ -105,6 +112,18 @@ npm install -g @deadragdoll/tellymcp
105
112
  tellymcp browser install
106
113
  ```
107
114
 
115
+ Если нужны attach extensions для существующего Firefox/Chrome:
116
+
117
+ ```bash
118
+ tellymcp extension firefox
119
+ tellymcp extension chrome
120
+ ```
121
+
122
+ Команда выгружает готовые unpacked bundles в текущий каталог:
123
+
124
+ - `./tellymcp-firefox-attach`
125
+ - `./tellymcp-chrome-attach`
126
+
108
127
  Если используешь Codex:
109
128
 
110
129
  ```bash
package/README.md CHANGED
@@ -29,6 +29,9 @@ The current model is gateway-first:
29
29
  - lets one agent ask another agent to do work and return files or notes
30
30
  - stores structured xchange records in `.mcp-xchange`
31
31
  - supports browser automation with Playwright
32
+ - can attach to an already running Firefox or Chrome tab through bundled local extensions
33
+ - records browser sessions into structured `.mcp-xchange/web/...` bundles with HTML, network and console artifacts
34
+ - can inject helper scripts into attached tabs or Playwright pages through `browser_inject_script`
32
35
  - serves the Telegram Mini App / Live View from the gateway
33
36
  - supports Telegram polling or webhook mode on the gateway
34
37
  - ships a bundled Codex workflow plugin for better agent behavior
@@ -76,9 +79,13 @@ Browser:
76
79
  - `browser_open`
77
80
  - `browser_click`
78
81
  - `browser_fill`
82
+ - `browser_inject_script`
79
83
  - `browser_press`
80
84
  - `browser_wait_for`
81
85
  - `browser_screenshot`
86
+ - `browser_recording_start`
87
+ - `browser_recording_stop`
88
+ - `browser_recording_status`
82
89
 
83
90
  Tools sync:
84
91
 
@@ -105,6 +112,18 @@ Optional browser runtime:
105
112
  tellymcp browser install
106
113
  ```
107
114
 
115
+ Optional attached-browser extensions:
116
+
117
+ ```bash
118
+ tellymcp extension firefox
119
+ tellymcp extension chrome
120
+ ```
121
+
122
+ This exports unpacked extension bundles into the current directory:
123
+
124
+ - `./tellymcp-firefox-attach`
125
+ - `./tellymcp-chrome-attach`
126
+
108
127
  Optional Codex workflow plugin:
109
128
 
110
129
  ```bash
package/TOOLS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Tools
2
2
 
3
- Version: `2026-05-22.1`
3
+ Version: `2026-06-06.1`
4
4
 
5
5
  Gateway/client runtime compatibility:
6
6
 
@@ -16,10 +16,16 @@ This MCP server currently exposes the following tools.
16
16
 
17
17
  Browser tools:
18
18
 
19
+ - `browser_list_attached_instances`
20
+ - `browser_list_tabs`
21
+ - `browser_recording_start`
22
+ - `browser_recording_stop`
23
+ - `browser_recording_status`
19
24
  - `browser_open`
20
25
  - `browser_reload`
21
26
  - `browser_click`
22
27
  - `browser_fill`
28
+ - `browser_inject_script`
23
29
  - `browser_press`
24
30
  - `browser_wait_for`
25
31
  - `browser_wait_for_url`
@@ -34,7 +40,17 @@ Browser tools:
34
40
 
35
41
  Browser runtime rule:
36
42
 
37
- - Browser tools use Playwright Chromium.
43
+ - Browser tools now have two backends:
44
+ - attached browser tab backend for a real user Firefox or Chrome session
45
+ - isolated Playwright Chromium backend
46
+ - `browser_open` is only for the isolated Playwright backend.
47
+ - `browser_list_attached_instances` and `browser_list_tabs` are for the attached browser backend.
48
+ - if the local attach extension is connected and a tab was selected from the browser control panel, `browser_click`, `browser_fill`, `browser_inject_script`, `browser_press`, `browser_dom`, and `browser_screenshot` use that attached browser tab for the current session without requiring `browser_open`
49
+ - `browser_recording_start`, `browser_recording_stop`, and `browser_recording_status` are also for the attached browser backend
50
+ - browser recordings are written under `.mcp-xchange/web/{tab-title-slug}-{timestamp}/`
51
+ - each recording bundle contains `session.json`, `timeline.ndjson`, `pages/`, `network/`, and `console/`
52
+ - inside `network/`, use `index.ndjson` as the thin search index and `requests/{seq}-{request_id}/` as the per-request artifact bundle
53
+ - otherwise those tools use the Playwright backend after `browser_open`
38
54
  - If browser tools fail because the Playwright browser runtime is missing, install it with `tellymcp browser install`.
39
55
  - Do not stop at the installation error itself. Install the browser runtime first, then retry the browser tool.
40
56
 
@@ -758,7 +774,8 @@ Output:
758
774
  Notes:
759
775
 
760
776
  - each session gets its own isolated browser context and page
761
- - call this first before reading console, DOM, styles, or screenshots
777
+ - call this first only when you want the isolated Playwright backend
778
+ - do not call this just to work with an already selected Firefox attached tab
762
779
  - `url` may be an absolute URL, or a relative path when `BROWSER_ADDRESS` is configured
763
780
  - in headed mode the browser window is started maximized by default
764
781
  - in headed mode a fresh browser context is created without a fixed viewport unless you explicitly pass `width` and `height`
@@ -766,6 +783,148 @@ Notes:
766
783
  - if you pass viewport size, pass both `width` and `height` together
767
784
  - use explicit `width` and `height` when the task depends on a specific responsive breakpoint or working area
768
785
 
786
+ ## `browser_list_attached_instances`
787
+
788
+ Purpose:
789
+
790
+ - list browser instances currently connected through the local attach extension bridge
791
+
792
+ Input:
793
+
794
+ - `session_id?`
795
+
796
+ Output:
797
+
798
+ - `session_id?`
799
+ - `total`
800
+ - `instances[]`
801
+ - `instance_id`
802
+ - `browser`
803
+ - `extension_version`
804
+ - `profile_name?`
805
+ - `connected_at`
806
+ - `last_seen_at`
807
+ - `capabilities[]`
808
+ - `tab_count`
809
+ - `active_tab?`
810
+
811
+ ## `browser_list_tabs`
812
+
813
+ Purpose:
814
+
815
+ - list tabs from an attached browser instance
816
+
817
+ Input:
818
+
819
+ - `session_id?`
820
+ - `instance_id?`
821
+
822
+ Output:
823
+
824
+ - `session_id?`
825
+ - `instance_id`
826
+ - `total`
827
+ - `tabs[]`
828
+ - `tab_id`
829
+ - `window_id?`
830
+ - `active`
831
+ - `selected?`
832
+ - `title`
833
+ - `url`
834
+ - `status?`
835
+
836
+ Notes:
837
+
838
+ - if only one attached browser instance is connected, `instance_id` can be omitted
839
+ - current tab selection for the attach backend is done from the browser extension control panel
840
+ - `active=true` means the tab is active in its browser window; there may be more than one such tab across multiple windows
841
+ - for the tab currently selected for this MCP session, prefer `selected=true`
842
+
843
+ ## `browser_recording_start`
844
+
845
+ Purpose:
846
+
847
+ - start a structured browser recording bundle for the attached browser tab currently selected for this MCP session
848
+
849
+ Input:
850
+
851
+ - `session_id?`
852
+ - `instance_id?`
853
+
854
+ Output:
855
+
856
+ - `session_id`
857
+ - `backend`
858
+ - `started`
859
+ - `recording_id`
860
+ - `instance_id`
861
+ - `tab_id`
862
+ - `tab_title?`
863
+ - `tab_url?`
864
+ - `bundle_dir_name`
865
+ - `bundle_relative_path`
866
+ - `bundle_path`
867
+ - `started_at`
868
+
869
+ Notes:
870
+
871
+ - this works only with the attached browser backend, not Playwright
872
+ - select the target tab first from the browser extension control panel
873
+ - the bundle is created under `.mcp-xchange/web/{tab-title-slug}-{timestamp}/`
874
+ - the recording captures page snapshots, console events, request metadata, headers, cookies snapshots, and available request/response bodies
875
+ - for network analysis, read `network/index.ndjson` first, then open the matching `network/requests/{seq}-{request_id}/meta.json`
876
+ - use `request.json`, `response.json`, and optional body files only when deeper detail is needed
877
+
878
+ ## `browser_recording_stop`
879
+
880
+ Purpose:
881
+
882
+ - stop the active structured browser recording for this MCP session
883
+
884
+ Input:
885
+
886
+ - `session_id?`
887
+
888
+ Output:
889
+
890
+ - `session_id`
891
+ - `stopped`
892
+ - `recording_id?`
893
+ - `bundle_dir_name?`
894
+ - `bundle_relative_path?`
895
+ - `bundle_path?`
896
+ - `stopped_at?`
897
+
898
+ ## `browser_recording_status`
899
+
900
+ Purpose:
901
+
902
+ - inspect whether a structured browser recording is active and where its bundle is being written
903
+
904
+ Input:
905
+
906
+ - `session_id?`
907
+
908
+ Output:
909
+
910
+ - `session_id`
911
+ - `active`
912
+ - `recording?`
913
+ - `backend`
914
+ - `recording_id`
915
+ - `instance_id`
916
+ - `tab_id`
917
+ - `tab_title?`
918
+ - `tab_url?`
919
+ - `bundle_dir_name`
920
+ - `bundle_relative_path`
921
+ - `bundle_path`
922
+ - `started_at`
923
+ - `stopped_at?`
924
+ - `status`
925
+ - `event_count`
926
+ - `last_event_at?`
927
+
769
928
  ## `browser_console`
770
929
 
771
930
  Purpose:
@@ -806,6 +965,8 @@ Output:
806
965
  Purpose:
807
966
 
808
967
  - click an element in the current session page
968
+ - if the session has a selected attached browser tab, click there
969
+ - otherwise click in the isolated Playwright page
809
970
 
810
971
  Input:
811
972
 
@@ -831,6 +992,8 @@ Output:
831
992
  Purpose:
832
993
 
833
994
  - fill an input or textarea in the current session page
995
+ - if the session has a selected attached browser tab, fill there
996
+ - otherwise fill in the isolated Playwright page
834
997
 
835
998
  Input:
836
999
 
@@ -858,6 +1021,8 @@ Output:
858
1021
  Purpose:
859
1022
 
860
1023
  - send a key press to the page or a targeted element
1024
+ - if the session has a selected attached browser tab, press there
1025
+ - otherwise press in the isolated Playwright page
861
1026
 
862
1027
  Input:
863
1028
 
@@ -880,6 +1045,38 @@ Output:
880
1045
  - `url`
881
1046
  - `title?`
882
1047
 
1048
+ ## `browser_inject_script`
1049
+
1050
+ Purpose:
1051
+
1052
+ - inject JavaScript into the current session browser target
1053
+ - if the session has a selected attached browser tab, inject into that real browser tab
1054
+ - otherwise inject into the isolated Playwright page
1055
+ - the script is wrapped so `window[namespace]` exists; default namespace is `TELLY`
1056
+
1057
+ Input:
1058
+
1059
+ - `session_id?`
1060
+ - `source?`
1061
+ - `file_path?`
1062
+ - `namespace?`
1063
+
1064
+ Rules:
1065
+
1066
+ - provide either `source` or `file_path`
1067
+ - prefer `file_path` for local reusable scripts and larger payloads
1068
+ - injected scripts can use both `window.TELLY` and local `TELLY` when `namespace` is left at the default
1069
+
1070
+ Output:
1071
+
1072
+ - `session_id`
1073
+ - `injected`
1074
+ - `namespace`
1075
+ - `source_type`
1076
+ - `bytes`
1077
+ - `url`
1078
+ - `title?`
1079
+
883
1080
  ## `browser_wait_for`
884
1081
 
885
1082
  Purpose:
@@ -921,6 +1118,7 @@ Browser target rules for `browser_click`, `browser_fill`, `browser_press`, `brow
921
1118
  - `div[data-testid="save"]`
922
1119
  - use `text` only when there is no reliable selector
923
1120
  - do not mix ambiguous hashed CSS classes with fuzzy text guessing when a stable selector exists
1121
+ - for the attached browser backend, prefer `selector` first; `ai_tag` and `text` still work, but the attach executor is intentionally lighter than full Playwright
924
1122
 
925
1123
  ## `browser_wait_for_url`
926
1124
 
@@ -1002,6 +1200,8 @@ Output:
1002
1200
  Purpose:
1003
1201
 
1004
1202
  - inspect a DOM element in the session browser tab
1203
+ - if the session has a selected attached browser tab, inspect that real browser tab
1204
+ - otherwise inspect the isolated Playwright page
1005
1205
 
1006
1206
  Input:
1007
1207
 
@@ -1050,6 +1250,8 @@ Output:
1050
1250
  Purpose:
1051
1251
 
1052
1252
  - capture a screenshot from the session browser tab
1253
+ - if the session has a selected attached browser tab, capture from that real browser tab
1254
+ - otherwise capture from the isolated Playwright page
1053
1255
 
1054
1256
  Input:
1055
1257
 
@@ -1076,6 +1278,7 @@ Notes:
1076
1278
  - they are tracked separately from Telegram-uploaded files
1077
1279
  - they appear under Telegram `Browser -> Screenshots`
1078
1280
  - if `send_to_telegram=true`, the saved screenshot is also sent into the bound Telegram chat for that session
1281
+ - for the attached browser backend, the screenshot comes from the selected tab in the user browser session
1079
1282
 
1080
1283
  ## `browser_close`
1081
1284
 
package/dist/cli.js CHANGED
@@ -72,6 +72,8 @@ function printHelp() {
72
72
  " tellymcp doctor [--env <file>]",
73
73
  " tellymcp system-prune [--env <file>] --yes",
74
74
  " tellymcp browser install",
75
+ " tellymcp extension firefox",
76
+ " tellymcp extension chrome",
75
77
  " tellymcp codex-plugin install",
76
78
  " tellymcp codex-plugin status",
77
79
  " tellymcp mcp [--url <url>] [--bearer <token>] [--format claude|legacy]",
@@ -87,6 +89,8 @@ function printHelp() {
87
89
  " tellymcp doctor --env .env.client",
88
90
  " tellymcp system-prune --env .env.gateway --yes",
89
91
  " tellymcp browser install",
92
+ " tellymcp extension firefox",
93
+ " tellymcp extension chrome ./tellymcp-chrome-attach",
90
94
  " tellymcp codex-plugin install",
91
95
  " tellymcp codex-plugin status",
92
96
  " tellymcp mcp --help",
@@ -245,6 +249,27 @@ function printBrowserHelp() {
245
249
  " Avoids generic npx warnings about missing local project dependencies.",
246
250
  ]);
247
251
  }
252
+ function printExtensionHelp() {
253
+ printBanner("extension helper", "Export bundled browser attach extensions into a local directory");
254
+ printSection("Usage", [
255
+ " tellymcp extension firefox [output-directory]",
256
+ " tellymcp extension ff [output-directory]",
257
+ " tellymcp extension chrome [output-directory]",
258
+ " tellymcp extension <firefox|ff|chrome> --out-dir <directory>",
259
+ " tellymcp extension <firefox|ff|chrome> --force",
260
+ ]);
261
+ printSection("What this command does", [
262
+ " Copies the packaged unpacked extension bundle out of the installed tellymcp package.",
263
+ " By default exports into the current directory.",
264
+ " Creates a browser-specific folder you can load into Firefox or Chrome.",
265
+ ]);
266
+ printSection("Examples", [
267
+ " tellymcp extension firefox",
268
+ " tellymcp extension chrome",
269
+ " tellymcp extension firefox ./tellymcp-firefox-attach",
270
+ " tellymcp extension chrome --out-dir ./browser-addon",
271
+ ]);
272
+ }
248
273
  function printCodexPluginHelp() {
249
274
  printBanner("codex plugin", "Install or inspect the bundled Codex workflow plugin");
250
275
  printSection("Usage", [
@@ -684,6 +709,39 @@ async function runDoctor(args) {
684
709
  function hasFlag(args, flagName) {
685
710
  return args.includes(flagName);
686
711
  }
712
+ function normalizeExtensionFlavor(rawValue) {
713
+ if (rawValue === "firefox" || rawValue === "ff") {
714
+ return "firefox";
715
+ }
716
+ if (rawValue === "chrome") {
717
+ return "chrome";
718
+ }
719
+ return null;
720
+ }
721
+ function getBundledExtensionDir(flavor) {
722
+ return node_path_1.default.join(packageRoot, "packages", flavor === "firefox" ? "firefox-attach-extension" : "chrome-attach-extension", "dist");
723
+ }
724
+ function getDefaultExtensionTargetDir(flavor) {
725
+ return node_path_1.default.resolve(process.cwd(), flavor === "firefox" ? "tellymcp-firefox-attach" : "tellymcp-chrome-attach");
726
+ }
727
+ function resolveExtensionTargetDir(args, flavor) {
728
+ const explicitOutDir = readFlagValue(args, "--out-dir");
729
+ if (explicitOutDir) {
730
+ return node_path_1.default.resolve(process.cwd(), explicitOutDir);
731
+ }
732
+ const positionalOutDir = args.find((value, index) => {
733
+ if (index === 0 || !value) {
734
+ return false;
735
+ }
736
+ if (value === "--force") {
737
+ return false;
738
+ }
739
+ return !value.startsWith("--out-dir=");
740
+ });
741
+ return positionalOutDir
742
+ ? node_path_1.default.resolve(process.cwd(), positionalOutDir)
743
+ : getDefaultExtensionTargetDir(flavor);
744
+ }
687
745
  async function runSystemPrune(args) {
688
746
  const confirmed = hasFlag(args, "--yes");
689
747
  if (!confirmed) {
@@ -821,6 +879,52 @@ function runBrowserCommand(args) {
821
879
  process.exit(code ?? 0);
822
880
  });
823
881
  }
882
+ function runExtensionCommand(args) {
883
+ const [rawFlavor] = args;
884
+ if (!rawFlavor || rawFlavor === "--help" || rawFlavor === "-h") {
885
+ printExtensionHelp();
886
+ return;
887
+ }
888
+ const flavor = normalizeExtensionFlavor(rawFlavor);
889
+ if (!flavor) {
890
+ fail("Supported extension targets: firefox, ff, chrome");
891
+ }
892
+ const sourceDir = getBundledExtensionDir(flavor);
893
+ const sourceManifestPath = node_path_1.default.join(sourceDir, "manifest.json");
894
+ if (!(0, node_fs_1.existsSync)(sourceManifestPath)) {
895
+ fail(`Missing bundled ${flavor} extension. Reinstall or republish tellymcp with packaged extension bundles.`);
896
+ }
897
+ const targetDir = resolveExtensionTargetDir(args, flavor);
898
+ const force = hasFlag(args, "--force");
899
+ if ((0, node_fs_1.existsSync)(targetDir)) {
900
+ if (!force) {
901
+ fail(`Refusing to overwrite existing directory: ${targetDir}. Re-run with --force.`);
902
+ }
903
+ (0, node_fs_1.rmSync)(targetDir, { recursive: true, force: true });
904
+ }
905
+ (0, node_fs_1.mkdirSync)(node_path_1.default.dirname(targetDir), { recursive: true });
906
+ (0, node_fs_1.cpSync)(sourceDir, targetDir, { recursive: true });
907
+ printBanner(`extension ${flavor}`, "Bundled attach extension exported");
908
+ printSection("result", [
909
+ ` source: ${sourceDir}`,
910
+ ` target: ${targetDir}`,
911
+ ` manifest: ${node_path_1.default.join(targetDir, "manifest.json")}`,
912
+ ]);
913
+ if (flavor === "firefox") {
914
+ printSection("next", [
915
+ " 1. Open about:debugging#/runtime/this-firefox",
916
+ " 2. Click 'Load Temporary Add-on'",
917
+ ` 3. Choose ${node_path_1.default.join(targetDir, "manifest.json")}`,
918
+ ]);
919
+ return;
920
+ }
921
+ printSection("next", [
922
+ " 1. Open chrome://extensions",
923
+ " 2. Enable Developer mode",
924
+ " 3. Click 'Load unpacked'",
925
+ ` 4. Choose ${targetDir}`,
926
+ ]);
927
+ }
824
928
  function runCodexPluginCommand(args) {
825
929
  const [subcommand] = args;
826
930
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
@@ -942,7 +1046,7 @@ async function runRuntime(args) {
942
1046
  async function main(argv) {
943
1047
  const [rawCommand, firstArg, secondArg] = argv;
944
1048
  const command = rawCommand === "init" || rawCommand === "run" || rawCommand === "help" || rawCommand === "mcp" || rawCommand === "doctor" || rawCommand === "browser" || rawCommand === "system-prune"
945
- || rawCommand === "codex-plugin"
1049
+ || rawCommand === "codex-plugin" || rawCommand === "extension"
946
1050
  ? rawCommand
947
1051
  : "help";
948
1052
  if (command === "help" || !rawCommand || rawCommand === "--help" || rawCommand === "-h") {
@@ -965,6 +1069,10 @@ async function main(argv) {
965
1069
  runBrowserCommand(argv.slice(1));
966
1070
  return;
967
1071
  }
1072
+ if (command === "extension") {
1073
+ runExtensionCommand(argv.slice(1));
1074
+ return;
1075
+ }
968
1076
  if (command === "codex-plugin") {
969
1077
  runCodexPluginCommand(argv.slice(1));
970
1078
  return;
@@ -21,6 +21,36 @@ const TelegramMcpBrowserService = {
21
21
  return this.getBrowserService().getConsole(ctx.params);
22
22
  },
23
23
  },
24
+ listAttachedInstancesRemote: {
25
+ params: { $$strict: false },
26
+ async handler(ctx) {
27
+ return this.getBrowserService().listAttachedInstances(ctx.params);
28
+ },
29
+ },
30
+ listTabsRemote: {
31
+ params: { $$strict: false },
32
+ async handler(ctx) {
33
+ return this.getBrowserService().listTabs(ctx.params);
34
+ },
35
+ },
36
+ startRecordingRemote: {
37
+ params: { $$strict: false },
38
+ async handler(ctx) {
39
+ return this.getBrowserService().startRecording(ctx.params);
40
+ },
41
+ },
42
+ stopRecordingRemote: {
43
+ params: { $$strict: false },
44
+ async handler(ctx) {
45
+ return this.getBrowserService().stopRecording(ctx.params);
46
+ },
47
+ },
48
+ getRecordingStatusRemote: {
49
+ params: { $$strict: false },
50
+ async handler(ctx) {
51
+ return this.getBrowserService().getRecordingStatus(ctx.params);
52
+ },
53
+ },
24
54
  clickRemote: {
25
55
  params: { $$strict: false },
26
56
  async handler(ctx) {
@@ -33,6 +63,12 @@ const TelegramMcpBrowserService = {
33
63
  return this.getBrowserService().fill(ctx.params);
34
64
  },
35
65
  },
66
+ injectScriptRemote: {
67
+ params: { $$strict: false },
68
+ async handler(ctx) {
69
+ return this.getBrowserService().injectScript(ctx.params);
70
+ },
71
+ },
36
72
  pressRemote: {
37
73
  params: { $$strict: false },
38
74
  async handler(ctx) {
@@ -119,7 +155,8 @@ const TelegramMcpBrowserService = {
119
155
  }
120
156
  const runtime = await runtimeService.waitUntilReady();
121
157
  this.logger.info("Starting telegram_mcp browser service");
122
- this.browserService = new browserService_1.BrowserService(runtime.config, runtime.sessionStore, runtime.maintenanceStore, runtime.stateStore, runtime.stateStore, runtime.objectStore, runtime.telegramTransport, runtime.logger, runtime.projectIdentityResolver, new remoteConsoleActionClient_1.RemoteConsoleActionClient((actionName, params) => this.broker.call(actionName, params, { meta: { internal_call: true } })));
158
+ const firefoxAttachServer = runtime.firefoxAttachServer;
159
+ this.browserService = new browserService_1.BrowserService(runtime.config, runtime.sessionStore, runtime.maintenanceStore, runtime.stateStore, runtime.stateStore, runtime.objectStore, runtime.telegramTransport, runtime.logger, runtime.projectIdentityResolver, new remoteConsoleActionClient_1.RemoteConsoleActionClient((actionName, params) => this.broker.call(actionName, params, { meta: { internal_call: true } })), firefoxAttachServer);
123
160
  this.logger.info("telegram_mcp browser service is ready");
124
161
  },
125
162
  async stopped() {
@@ -18,9 +18,15 @@ const browserConsoleTool_1 = require("./src/features/browser/model/browserConsol
18
18
  const browserDomTool_1 = require("./src/features/browser/model/browserDomTool");
19
19
  const browserErrorsTool_1 = require("./src/features/browser/model/browserErrorsTool");
20
20
  const browserFillTool_1 = require("./src/features/browser/model/browserFillTool");
21
+ const browserInjectScriptTool_1 = require("./src/features/browser/model/browserInjectScriptTool");
22
+ const browserListAttachedInstancesTool_1 = require("./src/features/browser/model/browserListAttachedInstancesTool");
23
+ const browserListTabsTool_1 = require("./src/features/browser/model/browserListTabsTool");
21
24
  const browserNetworkFailuresTool_1 = require("./src/features/browser/model/browserNetworkFailuresTool");
22
25
  const browserOpenTool_1 = require("./src/features/browser/model/browserOpenTool");
23
26
  const browserPressTool_1 = require("./src/features/browser/model/browserPressTool");
27
+ const browserRecordingStartTool_1 = require("./src/features/browser/model/browserRecordingStartTool");
28
+ const browserRecordingStatusTool_1 = require("./src/features/browser/model/browserRecordingStatusTool");
29
+ const browserRecordingStopTool_1 = require("./src/features/browser/model/browserRecordingStopTool");
24
30
  const browserReloadTool_1 = require("./src/features/browser/model/browserReloadTool");
25
31
  const browserScreenshotTool_1 = require("./src/features/browser/model/browserScreenshotTool");
26
32
  const browserWaitForTool_1 = require("./src/features/browser/model/browserWaitForTool");
@@ -76,10 +82,16 @@ const TelegramMcpMcpServerService = {
76
82
  new notifyTelegramTool_1.NotifyTelegramTool(notifyService.getNotifyService()),
77
83
  new sendFileToTelegramTool_1.SendFileToTelegramTool(notifyService.getNotifyService()),
78
84
  new askUserTelegram_1.AskUserTelegramTool(approvalService.getApprovalOrchestrator()),
85
+ new browserListAttachedInstancesTool_1.BrowserListAttachedInstancesTool(browserService.getBrowserService()),
86
+ new browserListTabsTool_1.BrowserListTabsTool(browserService.getBrowserService()),
87
+ new browserRecordingStartTool_1.BrowserRecordingStartTool(browserService.getBrowserService()),
88
+ new browserRecordingStopTool_1.BrowserRecordingStopTool(browserService.getBrowserService()),
89
+ new browserRecordingStatusTool_1.BrowserRecordingStatusTool(browserService.getBrowserService()),
79
90
  new browserOpenTool_1.BrowserOpenTool(browserService.getBrowserService()),
80
91
  new browserReloadTool_1.BrowserReloadTool(browserService.getBrowserService()),
81
92
  new browserClickTool_1.BrowserClickTool(browserService.getBrowserService()),
82
93
  new browserFillTool_1.BrowserFillTool(browserService.getBrowserService()),
94
+ new browserInjectScriptTool_1.BrowserInjectScriptTool(browserService.getBrowserService()),
83
95
  new browserPressTool_1.BrowserPressTool(browserService.getBrowserService()),
84
96
  new browserWaitForTool_1.BrowserWaitForTool(browserService.getBrowserService()),
85
97
  new browserWaitForUrlTool_1.BrowserWaitForUrlTool(browserService.getBrowserService()),
@@ -12,6 +12,7 @@ const transport_1 = require("../../shared/integrations/telegram/transport");
12
12
  const minioExchangeStore_1 = require("../../shared/integrations/object-storage/minioExchangeStore");
13
13
  const gatewayHttpService_1 = require("../../features/distributed-gateway/model/gatewayHttpService");
14
14
  const gatewayClientAccess_1 = require("../../features/distributed-client/model/gatewayClientAccess");
15
+ const firefoxAttachServer_1 = require("../../features/browser-attach/model/firefoxAttachServer");
15
16
  const client_2 = require("../../shared/integrations/terminal/client");
16
17
  const ptyRegistry_1 = require("../../shared/integrations/terminal/ptyRegistry");
17
18
  async function createAppRuntime(input) {
@@ -63,6 +64,15 @@ async function createAppRuntime(input) {
63
64
  nudgeDebounceSeconds: config.terminal.nudgeDebounceSeconds,
64
65
  nudgeCooldownSeconds: config.terminal.nudgeCooldownSeconds,
65
66
  },
67
+ browser: {
68
+ enabled: config.browser.enabled,
69
+ headless: config.browser.headless,
70
+ devtools: config.browser.devtools,
71
+ attachEnabled: config.browser.attach.enabled,
72
+ attachHost: config.browser.attach.host,
73
+ attachPort: config.browser.attach.port,
74
+ attachPath: config.browser.attach.path,
75
+ },
66
76
  telegram: {
67
77
  webhookEnabled: config.telegram.webhook.enabled,
68
78
  webhookPath: config.telegram.webhook.path,
@@ -200,6 +210,8 @@ async function createAppRuntime(input) {
200
210
  await telegramTransport.sendStartupNotifications();
201
211
  logger.info("Startup Telegram notifications completed");
202
212
  const gatewayHttpService = new gatewayHttpService_1.GatewayHttpService(config, input.callBroker);
213
+ const firefoxAttachServer = new firefoxAttachServer_1.FirefoxAttachServer(config, logger, stateStore, stateStore);
214
+ await firefoxAttachServer.start();
203
215
  return {
204
216
  callBroker: input.callBroker,
205
217
  config,
@@ -217,8 +229,10 @@ async function createAppRuntime(input) {
217
229
  projectIdentityResolver,
218
230
  webAppLaunchRegistry,
219
231
  gatewayHttpService,
232
+ firefoxAttachServer,
220
233
  shutdown: async () => {
221
234
  logger.info("Shutdown started");
235
+ await firefoxAttachServer.stop();
222
236
  await telegramTransport.stop();
223
237
  (0, ptyRegistry_1.stopAllPtyTargets)();
224
238
  redis.disconnect();