@wingman-ai/gateway 0.2.5 → 0.3.1

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 (184) hide show
  1. package/README.md +9 -0
  2. package/dist/agent/config/agentConfig.cjs +12 -0
  3. package/dist/agent/config/agentConfig.d.ts +26 -0
  4. package/dist/agent/config/agentConfig.js +10 -1
  5. package/dist/agent/config/agentLoader.cjs +9 -0
  6. package/dist/agent/config/agentLoader.js +9 -0
  7. package/dist/agent/config/mcpClientManager.cjs +44 -10
  8. package/dist/agent/config/mcpClientManager.d.ts +6 -2
  9. package/dist/agent/config/mcpClientManager.js +44 -10
  10. package/dist/agent/config/toolRegistry.cjs +20 -1
  11. package/dist/agent/config/toolRegistry.d.ts +15 -0
  12. package/dist/agent/config/toolRegistry.js +20 -1
  13. package/dist/agent/tests/agentConfig.test.cjs +6 -1
  14. package/dist/agent/tests/agentConfig.test.js +6 -1
  15. package/dist/agent/tests/browserControlHelpers.test.cjs +35 -0
  16. package/dist/agent/tests/browserControlHelpers.test.d.ts +1 -0
  17. package/dist/agent/tests/browserControlHelpers.test.js +29 -0
  18. package/dist/agent/tests/browserControlTool.test.cjs +2117 -0
  19. package/dist/agent/tests/browserControlTool.test.d.ts +1 -0
  20. package/dist/agent/tests/browserControlTool.test.js +2111 -0
  21. package/dist/agent/tests/mcpClientManager.test.cjs +124 -0
  22. package/dist/agent/tests/mcpClientManager.test.d.ts +1 -0
  23. package/dist/agent/tests/mcpClientManager.test.js +118 -0
  24. package/dist/agent/tests/toolRegistry.test.cjs +6 -0
  25. package/dist/agent/tests/toolRegistry.test.js +6 -0
  26. package/dist/agent/tools/browser_control.cjs +1282 -0
  27. package/dist/agent/tools/browser_control.d.ts +478 -0
  28. package/dist/agent/tools/browser_control.js +1242 -0
  29. package/dist/agent/tools/command_execute.cjs +1 -1
  30. package/dist/agent/tools/command_execute.js +1 -1
  31. package/dist/cli/commands/agent.cjs +16 -2
  32. package/dist/cli/commands/agent.js +16 -2
  33. package/dist/cli/commands/browser.cjs +603 -0
  34. package/dist/cli/commands/browser.d.ts +13 -0
  35. package/dist/cli/commands/browser.js +566 -0
  36. package/dist/cli/commands/gateway.cjs +18 -7
  37. package/dist/cli/commands/gateway.d.ts +5 -1
  38. package/dist/cli/commands/gateway.js +18 -7
  39. package/dist/cli/commands/init.cjs +134 -45
  40. package/dist/cli/commands/init.js +134 -45
  41. package/dist/cli/commands/skill.cjs +3 -2
  42. package/dist/cli/commands/skill.js +3 -2
  43. package/dist/cli/config/loader.cjs +15 -0
  44. package/dist/cli/config/loader.js +15 -0
  45. package/dist/cli/config/schema.cjs +51 -2
  46. package/dist/cli/config/schema.d.ts +51 -0
  47. package/dist/cli/config/schema.js +44 -1
  48. package/dist/cli/core/agentInvoker.cjs +55 -66
  49. package/dist/cli/core/agentInvoker.d.ts +10 -13
  50. package/dist/cli/core/agentInvoker.js +42 -62
  51. package/dist/cli/core/imagePersistence.cjs +125 -0
  52. package/dist/cli/core/imagePersistence.d.ts +24 -0
  53. package/dist/cli/core/imagePersistence.js +85 -0
  54. package/dist/cli/core/sessionManager.cjs +297 -40
  55. package/dist/cli/core/sessionManager.d.ts +9 -0
  56. package/dist/cli/core/sessionManager.js +297 -40
  57. package/dist/cli/core/workspace.cjs +89 -0
  58. package/dist/cli/core/workspace.d.ts +1 -0
  59. package/dist/cli/core/workspace.js +55 -0
  60. package/dist/cli/index.cjs +53 -5
  61. package/dist/cli/index.js +53 -5
  62. package/dist/cli/types/browser.cjs +18 -0
  63. package/dist/cli/types/browser.d.ts +9 -0
  64. package/dist/cli/types/browser.js +0 -0
  65. package/dist/debug/terminalProbe.cjs +57 -0
  66. package/dist/debug/terminalProbe.d.ts +10 -0
  67. package/dist/debug/terminalProbe.js +20 -0
  68. package/dist/debug/terminalProbeAuth.cjs +140 -0
  69. package/dist/debug/terminalProbeAuth.d.ts +20 -0
  70. package/dist/debug/terminalProbeAuth.js +97 -0
  71. package/dist/gateway/browserRelayServer.cjs +338 -0
  72. package/dist/gateway/browserRelayServer.d.ts +38 -0
  73. package/dist/gateway/browserRelayServer.js +301 -0
  74. package/dist/gateway/http/agents.cjs +22 -0
  75. package/dist/gateway/http/agents.js +22 -0
  76. package/dist/gateway/http/fs.cjs +76 -0
  77. package/dist/gateway/http/fs.js +77 -1
  78. package/dist/gateway/http/sessions.cjs +25 -5
  79. package/dist/gateway/http/sessions.js +25 -5
  80. package/dist/gateway/server.cjs +155 -17
  81. package/dist/gateway/server.d.ts +6 -1
  82. package/dist/gateway/server.js +148 -16
  83. package/dist/gateway/transport/websocket.cjs +45 -10
  84. package/dist/gateway/transport/websocket.d.ts +1 -0
  85. package/dist/gateway/transport/websocket.js +41 -9
  86. package/dist/gateway/types.d.ts +4 -0
  87. package/dist/tests/agentInvokerSummarization.test.cjs +56 -37
  88. package/dist/tests/agentInvokerSummarization.test.js +58 -39
  89. package/dist/tests/agentInvokerWorkdir.test.cjs +50 -0
  90. package/dist/tests/agentInvokerWorkdir.test.js +52 -2
  91. package/dist/tests/agents-api.test.cjs +52 -0
  92. package/dist/tests/agents-api.test.js +53 -1
  93. package/dist/tests/browser-command.test.cjs +264 -0
  94. package/dist/tests/browser-command.test.d.ts +1 -0
  95. package/dist/tests/browser-command.test.js +258 -0
  96. package/dist/tests/browser-relay-server.test.cjs +20 -0
  97. package/dist/tests/browser-relay-server.test.d.ts +1 -0
  98. package/dist/tests/browser-relay-server.test.js +14 -0
  99. package/dist/tests/cli-config-loader.test.cjs +43 -0
  100. package/dist/tests/cli-config-loader.test.js +43 -0
  101. package/dist/tests/cli-init.test.cjs +61 -2
  102. package/dist/tests/cli-init.test.js +61 -2
  103. package/dist/tests/cli-workspace-root.test.cjs +114 -0
  104. package/dist/tests/cli-workspace-root.test.d.ts +1 -0
  105. package/dist/tests/cli-workspace-root.test.js +108 -0
  106. package/dist/tests/falRuntime.test.cjs +78 -0
  107. package/dist/tests/falRuntime.test.d.ts +1 -0
  108. package/dist/tests/falRuntime.test.js +72 -0
  109. package/dist/tests/falSummary.test.cjs +51 -0
  110. package/dist/tests/falSummary.test.d.ts +1 -0
  111. package/dist/tests/falSummary.test.js +45 -0
  112. package/dist/tests/fs-api.test.cjs +138 -0
  113. package/dist/tests/fs-api.test.d.ts +1 -0
  114. package/dist/tests/fs-api.test.js +132 -0
  115. package/dist/tests/gateway-command-workspace.test.cjs +150 -0
  116. package/dist/tests/gateway-command-workspace.test.d.ts +1 -0
  117. package/dist/tests/gateway-command-workspace.test.js +144 -0
  118. package/dist/tests/gateway-request-execution-overrides.test.cjs +42 -0
  119. package/dist/tests/gateway-request-execution-overrides.test.d.ts +1 -0
  120. package/dist/tests/gateway-request-execution-overrides.test.js +36 -0
  121. package/dist/tests/gateway.test.cjs +140 -1
  122. package/dist/tests/gateway.test.js +140 -1
  123. package/dist/tests/imagePersistence.test.cjs +143 -0
  124. package/dist/tests/imagePersistence.test.d.ts +1 -0
  125. package/dist/tests/imagePersistence.test.js +137 -0
  126. package/dist/tests/sessionMessageAttachments.test.cjs +30 -0
  127. package/dist/tests/sessionMessageAttachments.test.js +30 -0
  128. package/dist/tests/sessionStateMessages.test.cjs +126 -0
  129. package/dist/tests/sessionStateMessages.test.js +126 -0
  130. package/dist/tests/sessions-api.test.cjs +117 -3
  131. package/dist/tests/sessions-api.test.js +118 -4
  132. package/dist/tests/terminalProbe.test.cjs +45 -0
  133. package/dist/tests/terminalProbe.test.d.ts +1 -0
  134. package/dist/tests/terminalProbe.test.js +39 -0
  135. package/dist/tests/terminalProbeAuth.test.cjs +85 -0
  136. package/dist/tests/terminalProbeAuth.test.d.ts +1 -0
  137. package/dist/tests/terminalProbeAuth.test.js +79 -0
  138. package/dist/tests/websocket-transport.test.cjs +31 -0
  139. package/dist/tests/websocket-transport.test.d.ts +1 -0
  140. package/dist/tests/websocket-transport.test.js +25 -0
  141. package/dist/tools/fal/runtime.cjs +103 -0
  142. package/dist/tools/fal/runtime.d.ts +10 -0
  143. package/dist/tools/fal/runtime.js +60 -0
  144. package/dist/tools/fal/summary.cjs +78 -0
  145. package/dist/tools/fal/summary.d.ts +22 -0
  146. package/dist/tools/fal/summary.js +41 -0
  147. package/dist/tools/mcp-fal-ai.cjs +1041 -0
  148. package/dist/tools/mcp-fal-ai.d.ts +1 -0
  149. package/dist/tools/mcp-fal-ai.js +1025 -0
  150. package/dist/types/mcp.cjs +2 -0
  151. package/dist/types/mcp.d.ts +8 -0
  152. package/dist/types/mcp.js +3 -1
  153. package/dist/webui/assets/index-BW9nM0J2.css +11 -0
  154. package/dist/webui/assets/index-C8-oboEC.js +278 -0
  155. package/dist/webui/index.html +2 -2
  156. package/extensions/wingman-browser-extension/README.md +27 -0
  157. package/extensions/wingman-browser-extension/background.js +416 -0
  158. package/extensions/wingman-browser-extension/manifest.json +19 -0
  159. package/extensions/wingman-browser-extension/options.html +156 -0
  160. package/extensions/wingman-browser-extension/options.js +106 -0
  161. package/package.json +18 -13
  162. package/{.wingman → templates}/agents/README.md +2 -1
  163. package/{.wingman → templates}/agents/coding/agent.md +5 -1
  164. package/{.wingman → templates}/agents/coding-v2/agent.md +58 -1
  165. package/templates/agents/game-dev/agent.md +101 -0
  166. package/templates/agents/game-dev/art-generation.md +38 -0
  167. package/templates/agents/game-dev/asset-refinement.md +17 -0
  168. package/templates/agents/game-dev/planning-idea.md +17 -0
  169. package/templates/agents/game-dev/ui-specialist.md +17 -0
  170. package/templates/agents/main/agent.md +29 -0
  171. package/{.wingman → templates}/agents/researcher/agent.md +9 -0
  172. package/{.wingman → templates}/agents/stock-trader/agent.md +1 -0
  173. package/.wingman/agents/main/agent.md +0 -22
  174. package/dist/webui/assets/index-C7EuTbnE.js +0 -270
  175. package/dist/webui/assets/index-DVWQluit.css +0 -11
  176. /package/{.wingman → templates}/agents/coding-v2/implementor.md +0 -0
  177. /package/{.wingman → templates}/agents/stock-trader/chain-curator.md +0 -0
  178. /package/{.wingman → templates}/agents/stock-trader/goal-translator.md +0 -0
  179. /package/{.wingman → templates}/agents/stock-trader/guardrails-veto.md +0 -0
  180. /package/{.wingman → templates}/agents/stock-trader/path-planner.md +0 -0
  181. /package/{.wingman → templates}/agents/stock-trader/regime-analyst.md +0 -0
  182. /package/{.wingman → templates}/agents/stock-trader/risk.md +0 -0
  183. /package/{.wingman → templates}/agents/stock-trader/selection.md +0 -0
  184. /package/{.wingman → templates}/agents/stock-trader/strategy-composer.md +0 -0
@@ -0,0 +1,1282 @@
1
+ "use strict";
2
+ const __rslib_import_meta_url__ = /*#__PURE__*/ function() {
3
+ return "u" < typeof document ? new (require('url'.replace('', ''))).URL('file:' + __filename).href : document.currentScript && document.currentScript.src || new URL('main.js', document.baseURI).href;
4
+ }();
5
+ var __webpack_require__ = {};
6
+ (()=>{
7
+ __webpack_require__.d = (exports1, definition)=>{
8
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
9
+ enumerable: true,
10
+ get: definition[key]
11
+ });
12
+ };
13
+ })();
14
+ (()=>{
15
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
16
+ })();
17
+ (()=>{
18
+ __webpack_require__.r = (exports1)=>{
19
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
20
+ value: 'Module'
21
+ });
22
+ Object.defineProperty(exports1, '__esModule', {
23
+ value: true
24
+ });
25
+ };
26
+ })();
27
+ var __webpack_exports__ = {};
28
+ __webpack_require__.r(__webpack_exports__);
29
+ __webpack_require__.d(__webpack_exports__, {
30
+ clearStaleDevtoolsArtifacts: ()=>clearStaleDevtoolsArtifacts,
31
+ createBrowserControlTool: ()=>createBrowserControlTool
32
+ });
33
+ const external_node_child_process_namespaceObject = require("node:child_process");
34
+ const external_node_fs_namespaceObject = require("node:fs");
35
+ const external_node_os_namespaceObject = require("node:os");
36
+ const external_node_path_namespaceObject = require("node:path");
37
+ const external_node_url_namespaceObject = require("node:url");
38
+ const external_langchain_namespaceObject = require("langchain");
39
+ const external_zod_namespaceObject = require("zod");
40
+ const external_logger_cjs_namespaceObject = require("../../logger.cjs");
41
+ const logger = (0, external_logger_cjs_namespaceObject.createLogger)();
42
+ const DEVTOOLS_ENDPOINT_REGEX = /DevTools listening on (ws:\/\/\S+)/i;
43
+ const DEFAULT_ACTION_TIMEOUT_MS = 30000;
44
+ const MAX_ACTION_TIMEOUT_MS = 300000;
45
+ const DEFAULT_LAUNCH_TIMEOUT_MS = 15000;
46
+ const DEFAULT_RELAY_CONNECT_TIMEOUT_MS = 5000;
47
+ const DEFAULT_RELAY_HOST = "127.0.0.1";
48
+ const DEFAULT_RELAY_PORT = 18792;
49
+ const DEFAULT_MAX_EXTRACT_CHARS = 5000;
50
+ const MAX_EXTRACT_CHARS = 1000000;
51
+ const MAX_ACTIONS = 25;
52
+ const DEFAULT_PROFILES_ROOT = ".wingman/browser-profiles";
53
+ const DEFAULT_EXTENSIONS_ROOT = ".wingman/browser-extensions";
54
+ const DEFAULT_BUNDLED_EXTENSION_ID = "wingman";
55
+ const BUNDLED_EXTENSION_RELATIVE_PATH = "../../../extensions/wingman-browser-extension";
56
+ const PERSISTENT_PROFILE_IGNORE_DEFAULT_ARGS = [
57
+ "--password-store=basic",
58
+ "--use-mock-keychain"
59
+ ];
60
+ const PROFILE_LOCK_FILENAME = ".wingman-browser.lock";
61
+ const PROFILE_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/;
62
+ const CHROME_COMMON_ARGS = [
63
+ "--disable-extensions",
64
+ "--disable-background-networking",
65
+ "--disable-default-apps",
66
+ "--no-default-browser-check",
67
+ "--no-first-run",
68
+ "--disable-sync",
69
+ "--disable-component-update",
70
+ "--mute-audio",
71
+ "--hide-scrollbars"
72
+ ];
73
+ const NavigateActionSchema = external_zod_namespaceObject.object({
74
+ type: external_zod_namespaceObject.literal("navigate"),
75
+ url: external_zod_namespaceObject.string().url().describe("Destination URL")
76
+ });
77
+ const NavigateAliasActionSchema = external_zod_namespaceObject.object({
78
+ type: external_zod_namespaceObject.literal("url"),
79
+ url: external_zod_namespaceObject.string().url().describe("Destination URL")
80
+ });
81
+ const NavigateOpenAliasActionSchema = external_zod_namespaceObject.object({
82
+ type: external_zod_namespaceObject.literal("open"),
83
+ url: external_zod_namespaceObject.string().url().describe("Destination URL")
84
+ });
85
+ const NavigateGotoAliasActionSchema = external_zod_namespaceObject.object({
86
+ type: external_zod_namespaceObject.literal("goto"),
87
+ url: external_zod_namespaceObject.string().url().describe("Destination URL")
88
+ });
89
+ const ClickActionSchema = external_zod_namespaceObject.object({
90
+ type: external_zod_namespaceObject.literal("click"),
91
+ selector: external_zod_namespaceObject.string().min(1).describe("CSS selector to click")
92
+ });
93
+ const TypeActionSchema = external_zod_namespaceObject.object({
94
+ type: external_zod_namespaceObject.literal("type"),
95
+ selector: external_zod_namespaceObject.string().min(1).describe("CSS selector to target"),
96
+ text: external_zod_namespaceObject.string().describe("Text value to enter"),
97
+ submit: external_zod_namespaceObject.boolean().optional().default(false).describe("Press Enter after typing")
98
+ });
99
+ const PressActionSchema = external_zod_namespaceObject.object({
100
+ type: external_zod_namespaceObject.literal("press"),
101
+ key: external_zod_namespaceObject.string().min(1).describe("Keyboard key (for example Enter, Tab, ArrowDown)")
102
+ });
103
+ const WaitActionSchema = external_zod_namespaceObject.object({
104
+ type: external_zod_namespaceObject.literal("wait"),
105
+ ms: external_zod_namespaceObject.number().int().min(1).max(MAX_ACTION_TIMEOUT_MS).optional().describe("How long to wait in milliseconds"),
106
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Wait for this selector to become visible"),
107
+ url: external_zod_namespaceObject.string().min(1).optional().describe("Wait for this URL/glob pattern"),
108
+ load: external_zod_namespaceObject["enum"]([
109
+ "load",
110
+ "domcontentloaded",
111
+ "networkidle"
112
+ ]).optional().describe("Wait for this load state"),
113
+ fn: external_zod_namespaceObject.string().min(1).optional().describe("Wait for this JavaScript predicate to become truthy"),
114
+ timeoutMs: external_zod_namespaceObject.number().int().min(1).max(MAX_ACTION_TIMEOUT_MS).optional().describe("Optional timeout override in milliseconds")
115
+ }).refine((action)=>Boolean(action.ms || action.selector || action.url || action.load || action.fn), {
116
+ message: "wait requires ms or one of selector/url/load/fn"
117
+ });
118
+ const WaitAliasActionSchema = external_zod_namespaceObject.object({
119
+ type: external_zod_namespaceObject.literal("ms"),
120
+ ms: external_zod_namespaceObject.number().int().min(1).max(MAX_ACTION_TIMEOUT_MS).describe("How long to wait in milliseconds")
121
+ });
122
+ const WaitSleepAliasActionSchema = external_zod_namespaceObject.object({
123
+ type: external_zod_namespaceObject.literal("sleep"),
124
+ ms: external_zod_namespaceObject.number().int().min(1).max(MAX_ACTION_TIMEOUT_MS).describe("How long to wait in milliseconds")
125
+ });
126
+ const WaitPauseAliasActionSchema = external_zod_namespaceObject.object({
127
+ type: external_zod_namespaceObject.literal("pause"),
128
+ ms: external_zod_namespaceObject.number().int().min(1).max(MAX_ACTION_TIMEOUT_MS).describe("How long to wait in milliseconds")
129
+ });
130
+ const WaitForActionBaseSchema = external_zod_namespaceObject.object({
131
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Wait for this selector to become visible"),
132
+ url: external_zod_namespaceObject.string().min(1).optional().describe("Wait for this URL/glob pattern"),
133
+ load: external_zod_namespaceObject["enum"]([
134
+ "load",
135
+ "domcontentloaded",
136
+ "networkidle"
137
+ ]).optional().describe("Wait for this load state"),
138
+ fn: external_zod_namespaceObject.string().min(1).optional().describe("Wait for this JavaScript predicate to become truthy"),
139
+ timeoutMs: external_zod_namespaceObject.number().int().min(1).max(MAX_ACTION_TIMEOUT_MS).optional().describe("Optional timeout override in milliseconds")
140
+ });
141
+ const WaitForActionSchema = WaitForActionBaseSchema.extend({
142
+ type: external_zod_namespaceObject.literal("wait_for")
143
+ }).refine((action)=>Boolean(action.selector || action.url || action.load || action.fn), {
144
+ message: "wait_for requires at least one of selector/url/load/fn"
145
+ });
146
+ const WaitUntilAliasActionSchema = WaitForActionBaseSchema.extend({
147
+ type: external_zod_namespaceObject.literal("wait_until")
148
+ }).refine((action)=>Boolean(action.selector || action.url || action.load || action.fn), {
149
+ message: "wait_until requires at least one of selector/url/load/fn"
150
+ });
151
+ const ExtractTextActionSchema = external_zod_namespaceObject.object({
152
+ type: external_zod_namespaceObject.literal("extract_text"),
153
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Optional CSS selector; defaults to body"),
154
+ maxChars: external_zod_namespaceObject.number().int().min(1).max(MAX_EXTRACT_CHARS).optional().default(DEFAULT_MAX_EXTRACT_CHARS).describe("Maximum returned characters")
155
+ });
156
+ const ExtractTextAliasActionSchema = external_zod_namespaceObject.object({
157
+ type: external_zod_namespaceObject.literal("selector"),
158
+ selector: external_zod_namespaceObject.string().min(1).describe("CSS selector"),
159
+ maxChars: external_zod_namespaceObject.number().int().min(1).max(MAX_EXTRACT_CHARS).optional().default(DEFAULT_MAX_EXTRACT_CHARS).describe("Maximum returned characters")
160
+ });
161
+ const ExtractAliasActionSchema = external_zod_namespaceObject.object({
162
+ type: external_zod_namespaceObject.literal("extract"),
163
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Optional CSS selector; defaults to body"),
164
+ maxChars: external_zod_namespaceObject.number().int().min(1).max(MAX_EXTRACT_CHARS).optional().default(DEFAULT_MAX_EXTRACT_CHARS).describe("Maximum returned characters")
165
+ });
166
+ const GetContentAliasActionSchema = external_zod_namespaceObject.object({
167
+ type: external_zod_namespaceObject.literal("getContent"),
168
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Optional CSS selector; defaults to body"),
169
+ maxChars: external_zod_namespaceObject.number().int().min(1).max(MAX_EXTRACT_CHARS).optional().default(DEFAULT_MAX_EXTRACT_CHARS).describe("Maximum returned characters")
170
+ });
171
+ const GetContentSnakeAliasActionSchema = external_zod_namespaceObject.object({
172
+ type: external_zod_namespaceObject.literal("get_content"),
173
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Optional CSS selector; defaults to body"),
174
+ maxChars: external_zod_namespaceObject.number().int().min(1).max(MAX_EXTRACT_CHARS).optional().default(DEFAULT_MAX_EXTRACT_CHARS).describe("Maximum returned characters")
175
+ });
176
+ const QuerySelectorAliasActionSchema = external_zod_namespaceObject.object({
177
+ type: external_zod_namespaceObject.literal("querySelector"),
178
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Optional CSS selector; defaults to body"),
179
+ maxChars: external_zod_namespaceObject.number().int().min(1).max(MAX_EXTRACT_CHARS).optional().default(DEFAULT_MAX_EXTRACT_CHARS).describe("Maximum returned characters")
180
+ });
181
+ const QuerySelectorSnakeAliasActionSchema = external_zod_namespaceObject.object({
182
+ type: external_zod_namespaceObject.literal("query_selector"),
183
+ selector: external_zod_namespaceObject.string().min(1).optional().describe("Optional CSS selector; defaults to body"),
184
+ maxChars: external_zod_namespaceObject.number().int().min(1).max(MAX_EXTRACT_CHARS).optional().default(DEFAULT_MAX_EXTRACT_CHARS).describe("Maximum returned characters")
185
+ });
186
+ const ScreenshotActionSchema = external_zod_namespaceObject.object({
187
+ type: external_zod_namespaceObject.literal("screenshot"),
188
+ path: external_zod_namespaceObject.string().min(1).optional().describe("Relative output path within workspace"),
189
+ fullPage: external_zod_namespaceObject.boolean().optional().default(true).describe("Capture the full page")
190
+ });
191
+ const ScreenshotAliasActionSchema = external_zod_namespaceObject.object({
192
+ type: external_zod_namespaceObject.literal("path"),
193
+ path: external_zod_namespaceObject.string().min(1).describe("Relative output path within workspace"),
194
+ fullPage: external_zod_namespaceObject.boolean().optional().default(true).describe("Capture the full page")
195
+ });
196
+ const SnapshotAliasActionSchema = external_zod_namespaceObject.object({
197
+ type: external_zod_namespaceObject.literal("snapshot"),
198
+ path: external_zod_namespaceObject.string().min(1).describe("Relative output path within workspace"),
199
+ fullPage: external_zod_namespaceObject.boolean().optional().default(true).describe("Capture the full page")
200
+ });
201
+ const CaptureAliasActionSchema = external_zod_namespaceObject.object({
202
+ type: external_zod_namespaceObject.literal("capture"),
203
+ path: external_zod_namespaceObject.string().min(1).describe("Relative output path within workspace"),
204
+ fullPage: external_zod_namespaceObject.boolean().optional().default(true).describe("Capture the full page")
205
+ });
206
+ const EvaluateActionSchema = external_zod_namespaceObject.object({
207
+ type: external_zod_namespaceObject.literal("evaluate"),
208
+ expression: external_zod_namespaceObject.string().min(1).describe("JavaScript expression to evaluate in page context")
209
+ });
210
+ const EvaluateAliasActionSchema = external_zod_namespaceObject.object({
211
+ type: external_zod_namespaceObject.literal("expression"),
212
+ expression: external_zod_namespaceObject.string().min(1).describe("JavaScript expression to evaluate in page context")
213
+ });
214
+ const EvaluateJsAliasActionSchema = external_zod_namespaceObject.object({
215
+ type: external_zod_namespaceObject.literal("js"),
216
+ expression: external_zod_namespaceObject.string().min(1).describe("JavaScript expression to evaluate in page context")
217
+ });
218
+ const EvaluateScriptAliasActionSchema = external_zod_namespaceObject.object({
219
+ type: external_zod_namespaceObject.literal("script"),
220
+ expression: external_zod_namespaceObject.string().min(1).describe("JavaScript expression to evaluate in page context")
221
+ });
222
+ const BrowserActionSchema = external_zod_namespaceObject.discriminatedUnion("type", [
223
+ NavigateActionSchema,
224
+ NavigateAliasActionSchema,
225
+ NavigateOpenAliasActionSchema,
226
+ NavigateGotoAliasActionSchema,
227
+ ClickActionSchema,
228
+ TypeActionSchema,
229
+ PressActionSchema,
230
+ WaitActionSchema,
231
+ WaitAliasActionSchema,
232
+ WaitSleepAliasActionSchema,
233
+ WaitPauseAliasActionSchema,
234
+ WaitForActionSchema,
235
+ WaitUntilAliasActionSchema,
236
+ ExtractTextActionSchema,
237
+ ExtractTextAliasActionSchema,
238
+ ExtractAliasActionSchema,
239
+ GetContentAliasActionSchema,
240
+ GetContentSnakeAliasActionSchema,
241
+ QuerySelectorAliasActionSchema,
242
+ QuerySelectorSnakeAliasActionSchema,
243
+ ScreenshotActionSchema,
244
+ ScreenshotAliasActionSchema,
245
+ SnapshotAliasActionSchema,
246
+ CaptureAliasActionSchema,
247
+ EvaluateActionSchema,
248
+ EvaluateAliasActionSchema,
249
+ EvaluateJsAliasActionSchema,
250
+ EvaluateScriptAliasActionSchema
251
+ ]);
252
+ const BrowserControlInputSchema = external_zod_namespaceObject.object({
253
+ url: external_zod_namespaceObject.string().url().optional().describe("Optional initial URL to open"),
254
+ actions: external_zod_namespaceObject.array(BrowserActionSchema).max(MAX_ACTIONS).optional().default([]).describe("Ordered browser actions to execute"),
255
+ headless: external_zod_namespaceObject.boolean().optional().describe("Launch browser in headless mode. Non-persistent runs default to headless; persistent browser profiles default to headed unless headless is explicitly requested."),
256
+ timeoutMs: external_zod_namespaceObject.number().int().min(1000).max(MAX_ACTION_TIMEOUT_MS).optional().default(DEFAULT_ACTION_TIMEOUT_MS).describe("Per-action timeout in milliseconds"),
257
+ executablePath: external_zod_namespaceObject.string().min(1).optional().describe("Optional path to Chrome/Chromium binary. Falls back to WINGMAN_CHROME_EXECUTABLE or common install paths.")
258
+ });
259
+ const DEFAULT_BROWSER_CONTROL_DEPENDENCIES = {
260
+ importPlaywright: async ()=>await import("playwright-core"),
261
+ startChrome: startChromeWithDevtools,
262
+ resolveRelayWsEndpoint: resolveRelayWsEndpoint,
263
+ mkTempDir: ()=>(0, external_node_fs_namespaceObject.mkdtempSync)((0, external_node_path_namespaceObject.join)((0, external_node_os_namespaceObject.tmpdir)(), "wingman-browser-")),
264
+ removeDir: (target)=>(0, external_node_fs_namespaceObject.rmSync)(target, {
265
+ recursive: true,
266
+ force: true
267
+ }),
268
+ now: ()=>Date.now()
269
+ };
270
+ function getChromeCandidates() {
271
+ const candidates = [
272
+ process.env.WINGMAN_CHROME_EXECUTABLE,
273
+ "google-chrome",
274
+ "chromium-browser",
275
+ "chromium"
276
+ ].filter((candidate)=>Boolean(candidate?.trim()));
277
+ if ("darwin" === process.platform) candidates.push("/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", "/Applications/Chromium.app/Contents/MacOS/Chromium");
278
+ if ("linux" === process.platform) candidates.push("/usr/bin/google-chrome", "/usr/bin/google-chrome-stable", "/usr/bin/chromium-browser", "/usr/bin/chromium");
279
+ if ("win32" === process.platform) candidates.push("C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe", "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe");
280
+ return candidates;
281
+ }
282
+ function resolveBinaryFromPath(binaryName) {
283
+ const locator = "win32" === process.platform ? "where" : "which";
284
+ const result = (0, external_node_child_process_namespaceObject.spawnSync)(locator, [
285
+ binaryName
286
+ ], {
287
+ encoding: "utf-8"
288
+ });
289
+ if (0 !== result.status || !result.stdout) return null;
290
+ const firstLine = result.stdout.split(/\r?\n/).map((line)=>line.trim()).find(Boolean);
291
+ return firstLine || null;
292
+ }
293
+ function resolveChromeExecutablePath(explicitPath) {
294
+ const candidatePool = explicitPath?.trim() ? [
295
+ explicitPath.trim()
296
+ ] : getChromeCandidates();
297
+ for (const candidate of candidatePool)if (candidate) {
298
+ if ((0, external_node_path_namespaceObject.isAbsolute)(candidate) && (0, external_node_fs_namespaceObject.existsSync)(candidate)) return candidate;
299
+ if (!(0, external_node_path_namespaceObject.isAbsolute)(candidate)) {
300
+ const fromPath = resolveBinaryFromPath(candidate);
301
+ if (fromPath) return fromPath;
302
+ }
303
+ }
304
+ if (explicitPath?.trim()) throw new Error(`Chrome executable not found at "${explicitPath}". Provide a valid executable path.`);
305
+ throw new Error("No Chrome/Chromium executable found. Install Chrome/Chromium or set WINGMAN_CHROME_EXECUTABLE.");
306
+ }
307
+ function waitForDevtoolsEndpoint(chromeProcess, launchTimeoutMs, userDataDir) {
308
+ return new Promise((resolveEndpoint, rejectEndpoint)=>{
309
+ let settled = false;
310
+ let logs = "";
311
+ const intervalHandle = setInterval(()=>{
312
+ const endpointFromFile = readDevtoolsEndpointFromFile(userDataDir);
313
+ if (!endpointFromFile) return;
314
+ finish(()=>resolveEndpoint(endpointFromFile));
315
+ }, 100);
316
+ const finish = (callback)=>{
317
+ if (settled) return;
318
+ settled = true;
319
+ clearTimeout(timeoutHandle);
320
+ clearInterval(intervalHandle);
321
+ chromeProcess.stdout.removeListener("data", onData);
322
+ chromeProcess.stderr.removeListener("data", onData);
323
+ chromeProcess.removeListener("error", onError);
324
+ chromeProcess.removeListener("exit", onExit);
325
+ callback();
326
+ };
327
+ const onData = (chunk)=>{
328
+ const text = chunk.toString();
329
+ logs += text;
330
+ const match = text.match(DEVTOOLS_ENDPOINT_REGEX);
331
+ if (!match?.[1]) return;
332
+ finish(()=>resolveEndpoint(match[1]));
333
+ };
334
+ const onError = (error)=>{
335
+ finish(()=>rejectEndpoint(new Error(`Failed to launch Chrome for CDP connection: ${error.message}`)));
336
+ };
337
+ const onExit = (code)=>{
338
+ finish(()=>rejectEndpoint(new Error(`Chrome exited before exposing DevTools endpoint (code: ${code ?? "unknown"}). Output: ${logs.trim() || "none"}`)));
339
+ };
340
+ const timeoutHandle = setTimeout(()=>{
341
+ finish(()=>rejectEndpoint(new Error(`Timed out waiting for DevTools endpoint after ${launchTimeoutMs}ms.`)));
342
+ }, launchTimeoutMs);
343
+ chromeProcess.stdout.on("data", onData);
344
+ chromeProcess.stderr.on("data", onData);
345
+ chromeProcess.on("error", onError);
346
+ chromeProcess.on("exit", onExit);
347
+ });
348
+ }
349
+ function readDevtoolsEndpointFromFile(userDataDir) {
350
+ const activePortPath = (0, external_node_path_namespaceObject.join)(userDataDir, "DevToolsActivePort");
351
+ if (!(0, external_node_fs_namespaceObject.existsSync)(activePortPath)) return null;
352
+ try {
353
+ const raw = (0, external_node_fs_namespaceObject.readFileSync)(activePortPath, "utf-8").trim();
354
+ if (!raw) return null;
355
+ const [portLine, browserPathLine] = raw.split(/\r?\n/);
356
+ const port = Number.parseInt(portLine?.trim() || "", 10);
357
+ if (!Number.isFinite(port) || port <= 0) return null;
358
+ const rawPath = browserPathLine?.trim();
359
+ if (!rawPath) return null;
360
+ const browserPath = rawPath.startsWith("/") ? rawPath : `/${rawPath}`;
361
+ return `ws://127.0.0.1:${port}${browserPath}`;
362
+ } catch {
363
+ return null;
364
+ }
365
+ }
366
+ async function closeChromeProcess(chromeProcess) {
367
+ if (null !== chromeProcess.exitCode || chromeProcess.killed) return;
368
+ await new Promise((resolveClose)=>{
369
+ let resolved = false;
370
+ const finish = ()=>{
371
+ if (resolved) return;
372
+ resolved = true;
373
+ resolveClose();
374
+ };
375
+ const forceKillTimeout = setTimeout(()=>{
376
+ if (null === chromeProcess.exitCode && !chromeProcess.killed) chromeProcess.kill("SIGKILL");
377
+ }, 2000);
378
+ chromeProcess.once("exit", ()=>{
379
+ clearTimeout(forceKillTimeout);
380
+ finish();
381
+ });
382
+ try {
383
+ chromeProcess.kill("SIGTERM");
384
+ } catch {
385
+ clearTimeout(forceKillTimeout);
386
+ finish();
387
+ }
388
+ });
389
+ }
390
+ function clearStaleDevtoolsArtifacts(userDataDir) {
391
+ const activePortPath = (0, external_node_path_namespaceObject.join)(userDataDir, "DevToolsActivePort");
392
+ try {
393
+ (0, external_node_fs_namespaceObject.unlinkSync)(activePortPath);
394
+ } catch {}
395
+ }
396
+ async function startChromeWithDevtools(input) {
397
+ const executablePath = resolveChromeExecutablePath(input.executablePath);
398
+ clearStaleDevtoolsArtifacts(input.userDataDir);
399
+ const args = [
400
+ "--remote-debugging-port=0",
401
+ `--user-data-dir=${input.userDataDir}`,
402
+ ...input.chromeArgs || CHROME_COMMON_ARGS
403
+ ];
404
+ if (input.headless) args.push("--headless=new");
405
+ args.push("about:blank");
406
+ const chromeProcess = (0, external_node_child_process_namespaceObject.spawn)(executablePath, args, {
407
+ stdio: [
408
+ "ignore",
409
+ "pipe",
410
+ "pipe"
411
+ ]
412
+ });
413
+ let wsEndpoint = "";
414
+ try {
415
+ wsEndpoint = await waitForDevtoolsEndpoint(chromeProcess, input.launchTimeoutMs, input.userDataDir);
416
+ } catch (error) {
417
+ await closeChromeProcess(chromeProcess);
418
+ throw error;
419
+ }
420
+ return {
421
+ wsEndpoint,
422
+ close: async ()=>{
423
+ await closeChromeProcess(chromeProcess);
424
+ }
425
+ };
426
+ }
427
+ function resolveBrowserTransportPreference(value) {
428
+ if ("playwright" === value || "relay" === value) return value;
429
+ return "auto";
430
+ }
431
+ function resolveRelayConfig(options) {
432
+ if (!options.relayConfig?.enabled) return null;
433
+ const host = (options.relayConfig.host || DEFAULT_RELAY_HOST).trim();
434
+ const port = Number.isInteger(options.relayConfig.port) ? Number(options.relayConfig.port) : DEFAULT_RELAY_PORT;
435
+ const requireAuth = false !== options.relayConfig.requireAuth;
436
+ const authToken = options.relayConfig.authToken?.trim() || void 0;
437
+ if (!host) throw new Error("Browser relay host cannot be empty.");
438
+ if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error(`Invalid browser relay port: ${String(options.relayConfig.port)}`);
439
+ if (requireAuth && !authToken) throw new Error('Browser relay requires authToken. Run "wingman browser extension pair" and configure the extension token.');
440
+ return {
441
+ host,
442
+ port,
443
+ requireAuth,
444
+ authToken
445
+ };
446
+ }
447
+ async function resolveRelayWsEndpoint(config, timeoutMs) {
448
+ const relayHttpBase = `http://${config.host}:${config.port}`;
449
+ const controller = new AbortController();
450
+ const timer = setTimeout(()=>controller.abort(), timeoutMs);
451
+ try {
452
+ const versionResponse = await fetch(`${relayHttpBase}/json/version`, {
453
+ method: "GET",
454
+ signal: controller.signal
455
+ });
456
+ if (!versionResponse.ok) throw new Error(`Browser relay endpoint returned HTTP ${versionResponse.status}`);
457
+ const payload = await versionResponse.json();
458
+ if ("string" == typeof payload?.webSocketDebuggerUrl && payload.webSocketDebuggerUrl.trim()) return payload.webSocketDebuggerUrl.trim();
459
+ } catch (error) {
460
+ const suffix = error instanceof Error ? `: ${error.message}` : "";
461
+ throw new Error(`Failed to resolve browser relay endpoint${suffix}`);
462
+ } finally{
463
+ clearTimeout(timer);
464
+ }
465
+ const tokenParam = config.requireAuth && config.authToken ? `?token=${encodeURIComponent(config.authToken)}` : "";
466
+ return `ws://${config.host}:${config.port}/cdp${tokenParam}`;
467
+ }
468
+ function preferPersistentLaunch(options, _isPersistentProfile) {
469
+ if ("boolean" == typeof options.preferPersistentLaunch) return options.preferPersistentLaunch;
470
+ return _isPersistentProfile;
471
+ }
472
+ function resolveHeadlessMode(inputHeadless, isPersistentProfile) {
473
+ if ("boolean" == typeof inputHeadless) return inputHeadless;
474
+ return !isPersistentProfile;
475
+ }
476
+ function selectContext(contexts) {
477
+ if (0 === contexts.length) throw new Error("Failed to initialize browser context.");
478
+ const reversed = [
479
+ ...contexts
480
+ ].reverse();
481
+ return reversed.find((candidate)=>candidate.pages().length > 0) || reversed[0];
482
+ }
483
+ async function launchPersistentContext(playwright, userDataDir, executablePath, headless, startupTimeoutMs, chromeArgs) {
484
+ if ("function" != typeof playwright.chromium.launchPersistentContext) throw new Error("playwright-core runtime does not support launchPersistentContext.");
485
+ return playwright.chromium.launchPersistentContext(userDataDir, {
486
+ executablePath: resolveChromeExecutablePath(executablePath),
487
+ headless,
488
+ timeout: startupTimeoutMs,
489
+ args: chromeArgs,
490
+ ignoreDefaultArgs: PERSISTENT_PROFILE_IGNORE_DEFAULT_ARGS
491
+ });
492
+ }
493
+ function validateExtensionId(extensionId) {
494
+ const normalized = extensionId.trim();
495
+ if (!normalized) throw new Error("Extension ID cannot be empty.");
496
+ if (!PROFILE_ID_PATTERN.test(normalized)) throw new Error(`Invalid extension ID "${extensionId}". Use letters, numbers, dot, underscore, or dash.`);
497
+ return normalized;
498
+ }
499
+ function resolveExtensionPath(workspace, extensionId, options) {
500
+ const mappedPath = options.extensionPaths?.[extensionId];
501
+ if (mappedPath?.trim()) {
502
+ const trimmed = mappedPath.trim();
503
+ return (0, external_node_path_namespaceObject.isAbsolute)(trimmed) ? trimmed : (0, external_node_path_namespaceObject.resolve)(workspace, trimmed);
504
+ }
505
+ const rootDir = options.extensionsRootDir?.trim() || DEFAULT_EXTENSIONS_ROOT;
506
+ const absoluteRoot = (0, external_node_path_namespaceObject.isAbsolute)(rootDir) ? rootDir : (0, external_node_path_namespaceObject.resolve)(workspace, rootDir);
507
+ return (0, external_node_path_namespaceObject.join)(absoluteRoot, extensionId);
508
+ }
509
+ function resolveBundledExtensionSourcePath() {
510
+ const bundledPath = (0, external_node_path_namespaceObject.resolve)((0, external_node_url_namespaceObject.fileURLToPath)(new URL(BUNDLED_EXTENSION_RELATIVE_PATH, __rslib_import_meta_url__)));
511
+ const bundledManifest = (0, external_node_path_namespaceObject.join)(bundledPath, "manifest.json");
512
+ return (0, external_node_fs_namespaceObject.existsSync)(bundledManifest) ? bundledPath : null;
513
+ }
514
+ function ensureBundledWingmanExtension(extensionPath) {
515
+ const bundledSourcePath = resolveBundledExtensionSourcePath();
516
+ if (!bundledSourcePath) return false;
517
+ (0, external_node_fs_namespaceObject.mkdirSync)((0, external_node_path_namespaceObject.dirname)(extensionPath), {
518
+ recursive: true
519
+ });
520
+ (0, external_node_fs_namespaceObject.cpSync)(bundledSourcePath, extensionPath, {
521
+ recursive: true,
522
+ force: true
523
+ });
524
+ return true;
525
+ }
526
+ function resolveEnabledExtensions(workspace, options) {
527
+ const requestedIds = options.browserExtensions?.length ? options.browserExtensions : options.defaultExtensions;
528
+ if (!requestedIds?.length) return {
529
+ extensionIds: [],
530
+ extensionDirs: []
531
+ };
532
+ const uniqueIds = Array.from(new Set(requestedIds.map((value)=>validateExtensionId(value))));
533
+ const extensionDirs = uniqueIds.map((extensionId)=>{
534
+ const extensionPath = resolveExtensionPath(workspace, extensionId, options);
535
+ if (!(0, external_node_fs_namespaceObject.existsSync)(extensionPath)) {
536
+ if (extensionId === DEFAULT_BUNDLED_EXTENSION_ID && ensureBundledWingmanExtension(extensionPath)) logger.info(`browser_control auto-provisioned bundled extension "${extensionId}" at ${extensionPath}`);
537
+ }
538
+ if (!(0, external_node_fs_namespaceObject.existsSync)(extensionPath)) throw new Error(`Configured extension path does not exist for "${extensionId}": ${extensionPath}`);
539
+ const manifestPath = (0, external_node_path_namespaceObject.join)(extensionPath, "manifest.json");
540
+ if (!(0, external_node_fs_namespaceObject.existsSync)(manifestPath)) throw new Error(`manifest.json not found for extension "${extensionId}" at ${extensionPath}`);
541
+ return extensionPath;
542
+ });
543
+ return {
544
+ extensionIds: uniqueIds,
545
+ extensionDirs
546
+ };
547
+ }
548
+ function buildChromeArgs(extensionDirs) {
549
+ if (0 === extensionDirs.length) return CHROME_COMMON_ARGS;
550
+ const joined = extensionDirs.join(",");
551
+ return [
552
+ ...CHROME_COMMON_ARGS.filter((arg)=>"--disable-extensions" !== arg),
553
+ `--disable-extensions-except=${joined}`,
554
+ `--load-extension=${joined}`
555
+ ];
556
+ }
557
+ function validateProfileId(profileId) {
558
+ const normalized = profileId.trim();
559
+ if (!normalized) throw new Error("browserProfile cannot be empty.");
560
+ if (!PROFILE_ID_PATTERN.test(normalized)) throw new Error(`Invalid browserProfile "${profileId}". Use letters, numbers, dot, underscore, or dash.`);
561
+ return normalized;
562
+ }
563
+ function resolveProfilePath(workspace, profileId, options) {
564
+ const normalizedProfileId = validateProfileId(profileId);
565
+ const mappedPath = options.profilePaths?.[normalizedProfileId];
566
+ if (mappedPath && mappedPath.trim()) {
567
+ const trimmed = mappedPath.trim();
568
+ return (0, external_node_path_namespaceObject.isAbsolute)(trimmed) ? trimmed : (0, external_node_path_namespaceObject.resolve)(workspace, trimmed);
569
+ }
570
+ const rootDir = options.profilesRootDir?.trim() || DEFAULT_PROFILES_ROOT;
571
+ const absoluteRoot = (0, external_node_path_namespaceObject.isAbsolute)(rootDir) ? rootDir : (0, external_node_path_namespaceObject.resolve)(workspace, rootDir);
572
+ return (0, external_node_path_namespaceObject.join)(absoluteRoot, normalizedProfileId);
573
+ }
574
+ function readProfileLockMetadata(lockPath) {
575
+ try {
576
+ const raw = (0, external_node_fs_namespaceObject.readFileSync)(lockPath, "utf-8");
577
+ const parsed = JSON.parse(raw);
578
+ if (!parsed || "object" != typeof parsed) return null;
579
+ return parsed;
580
+ } catch {
581
+ return null;
582
+ }
583
+ }
584
+ function isPidAlive(pid) {
585
+ if (!Number.isInteger(pid) || pid <= 0) return false;
586
+ try {
587
+ process.kill(pid, 0);
588
+ return true;
589
+ } catch (error) {
590
+ if (error && "object" == typeof error && "code" in error && "EPERM" === error.code) return true;
591
+ return false;
592
+ }
593
+ }
594
+ function acquireProfileLock(profileDir) {
595
+ const lockPath = (0, external_node_path_namespaceObject.join)(profileDir, PROFILE_LOCK_FILENAME);
596
+ const writeLock = ()=>(0, external_node_fs_namespaceObject.writeFileSync)(lockPath, JSON.stringify({
597
+ pid: process.pid,
598
+ createdAt: new Date().toISOString()
599
+ }), {
600
+ flag: "wx"
601
+ });
602
+ try {
603
+ writeLock();
604
+ } catch (error) {
605
+ if (!(error instanceof Error && "code" in error && "EEXIST" === error.code)) throw error;
606
+ const lockMetadata = readProfileLockMetadata(lockPath);
607
+ if ("number" == typeof lockMetadata?.pid && lockMetadata.pid === process.pid) return ()=>{};
608
+ const stalePid = "number" == typeof lockMetadata?.pid && !isPidAlive(lockMetadata.pid);
609
+ if (!stalePid) throw new Error(`Browser profile is already in use: ${profileDir}. Wait for the other run to finish.`);
610
+ try {
611
+ (0, external_node_fs_namespaceObject.unlinkSync)(lockPath);
612
+ } catch {
613
+ throw new Error(`Browser profile is already in use: ${profileDir}. Wait for the other run to finish.`);
614
+ }
615
+ try {
616
+ writeLock();
617
+ } catch {
618
+ throw new Error(`Browser profile is already in use: ${profileDir}. Wait for the other run to finish.`);
619
+ }
620
+ }
621
+ let released = false;
622
+ return ()=>{
623
+ if (released) return;
624
+ released = true;
625
+ try {
626
+ (0, external_node_fs_namespaceObject.unlinkSync)(lockPath);
627
+ } catch {}
628
+ };
629
+ }
630
+ function resolveUserDataDir(workspace, options, dependencies) {
631
+ const configuredProfile = options.browserProfile?.trim();
632
+ if (!configuredProfile) return {
633
+ userDataDir: dependencies.mkTempDir(),
634
+ persistentProfile: false
635
+ };
636
+ const profileId = validateProfileId(configuredProfile);
637
+ const profileDir = resolveProfilePath(workspace, profileId, options);
638
+ (0, external_node_fs_namespaceObject.mkdirSync)(profileDir, {
639
+ recursive: true
640
+ });
641
+ const releaseLock = acquireProfileLock(profileDir);
642
+ return {
643
+ userDataDir: profileDir,
644
+ persistentProfile: true,
645
+ profileId,
646
+ releaseLock
647
+ };
648
+ }
649
+ function resolveWorkspaceRelativePath(workspace, targetPath) {
650
+ const absolutePath = (0, external_node_path_namespaceObject.resolve)(workspace, targetPath);
651
+ const relativePath = (0, external_node_path_namespaceObject.relative)((0, external_node_path_namespaceObject.resolve)(workspace), absolutePath);
652
+ if (relativePath.startsWith("..") || (0, external_node_path_namespaceObject.isAbsolute)(relativePath)) throw new Error("Output path must stay inside the workspace.");
653
+ return absolutePath;
654
+ }
655
+ function resolveScreenshotPath(workspace, requestedPath, now, actionIndex) {
656
+ if (requestedPath && (0, external_node_path_namespaceObject.isAbsolute)(requestedPath)) throw new Error("Screenshot path must be relative to the workspace.");
657
+ const fallback = (0, external_node_path_namespaceObject.join)(".wingman", "browser", `screenshot-${now()}-${actionIndex + 1}.png`);
658
+ const relativeOutputPath = requestedPath || fallback;
659
+ const absoluteOutputPath = resolveWorkspaceRelativePath(workspace, relativeOutputPath);
660
+ (0, external_node_fs_namespaceObject.mkdirSync)((0, external_node_path_namespaceObject.dirname)(absoluteOutputPath), {
661
+ recursive: true
662
+ });
663
+ return {
664
+ absolute: absoluteOutputPath,
665
+ relative: (0, external_node_path_namespaceObject.relative)(workspace, absoluteOutputPath).split("\\").join("/")
666
+ };
667
+ }
668
+ function serializeEvaluation(value) {
669
+ if ("string" == typeof value || "number" == typeof value || "boolean" == typeof value || null === value) return value;
670
+ try {
671
+ return JSON.parse(JSON.stringify(value));
672
+ } catch {
673
+ return String(value);
674
+ }
675
+ }
676
+ function globToRegex(globPattern) {
677
+ let regex = "^";
678
+ for(let index = 0; index < globPattern.length; index += 1){
679
+ const current = globPattern[index];
680
+ const next = globPattern[index + 1];
681
+ if ("*" === current && "*" === next) {
682
+ regex += ".*";
683
+ index += 1;
684
+ continue;
685
+ }
686
+ if ("*" === current) {
687
+ regex += "[^/]*";
688
+ continue;
689
+ }
690
+ regex += current.replace(/[-/\\^$+?.()|[\]{}]/g, "\\$&");
691
+ }
692
+ regex += "$";
693
+ return new RegExp(regex);
694
+ }
695
+ async function waitForUrlFallback(page, urlPattern, timeoutMs) {
696
+ const regex = globToRegex(urlPattern);
697
+ const startedAt = Date.now();
698
+ while(Date.now() - startedAt < timeoutMs){
699
+ if (regex.test(page.url())) return;
700
+ await page.waitForTimeout(100);
701
+ }
702
+ throw new Error(`Timed out waiting for URL pattern "${urlPattern}".`);
703
+ }
704
+ async function waitForPredicateFallback(page, expression, timeoutMs) {
705
+ const startedAt = Date.now();
706
+ while(Date.now() - startedAt < timeoutMs){
707
+ const result = await page.evaluate(expression);
708
+ if (result) return;
709
+ await page.waitForTimeout(100);
710
+ }
711
+ throw new Error("Timed out waiting for JavaScript predicate to become truthy.");
712
+ }
713
+ async function runConditionalWait(page, action, timeoutMs) {
714
+ const waitTimeoutMs = action.timeoutMs ?? timeoutMs;
715
+ if (action.selector) if ("function" == typeof page.waitForSelector) await page.waitForSelector(action.selector, {
716
+ state: "visible",
717
+ timeout: waitTimeoutMs
718
+ });
719
+ else await page.textContent(action.selector, {
720
+ timeout: waitTimeoutMs
721
+ });
722
+ if (action.url) if ("function" == typeof page.waitForURL) await page.waitForURL(globToRegex(action.url), {
723
+ timeout: waitTimeoutMs
724
+ });
725
+ else await waitForUrlFallback(page, action.url, waitTimeoutMs);
726
+ if (action.load && "function" == typeof page.waitForLoadState) await page.waitForLoadState(action.load, {
727
+ timeout: waitTimeoutMs
728
+ });
729
+ if (action.fn) if ("function" == typeof page.waitForFunction) await page.waitForFunction(action.fn, void 0, {
730
+ timeout: waitTimeoutMs
731
+ });
732
+ else await waitForPredicateFallback(page, action.fn, waitTimeoutMs);
733
+ return {
734
+ type: "wait_for",
735
+ selector: action.selector || null,
736
+ url: action.url || null,
737
+ load: action.load || null,
738
+ fn: action.fn || null,
739
+ timeoutMs: waitTimeoutMs
740
+ };
741
+ }
742
+ async function runAction(page, action, timeoutMs, workspace, now, actionIndex) {
743
+ switch(action.type){
744
+ case "navigate":
745
+ await page.goto(action.url, {
746
+ waitUntil: "domcontentloaded",
747
+ timeout: timeoutMs
748
+ });
749
+ return {
750
+ type: action.type,
751
+ url: action.url
752
+ };
753
+ case "url":
754
+ await page.goto(action.url, {
755
+ waitUntil: "domcontentloaded",
756
+ timeout: timeoutMs
757
+ });
758
+ return {
759
+ type: "navigate",
760
+ url: action.url
761
+ };
762
+ case "open":
763
+ await page.goto(action.url, {
764
+ waitUntil: "domcontentloaded",
765
+ timeout: timeoutMs
766
+ });
767
+ return {
768
+ type: "navigate",
769
+ url: action.url
770
+ };
771
+ case "goto":
772
+ await page.goto(action.url, {
773
+ waitUntil: "domcontentloaded",
774
+ timeout: timeoutMs
775
+ });
776
+ return {
777
+ type: "navigate",
778
+ url: action.url
779
+ };
780
+ case "click":
781
+ await page.click(action.selector, {
782
+ timeout: timeoutMs
783
+ });
784
+ return {
785
+ type: action.type,
786
+ selector: action.selector
787
+ };
788
+ case "type":
789
+ await page.fill(action.selector, action.text, {
790
+ timeout: timeoutMs
791
+ });
792
+ if (action.submit) await page.keyboard.press("Enter");
793
+ return {
794
+ type: action.type,
795
+ selector: action.selector,
796
+ textLength: action.text.length,
797
+ submit: Boolean(action.submit)
798
+ };
799
+ case "press":
800
+ await page.keyboard.press(action.key);
801
+ return {
802
+ type: action.type,
803
+ key: action.key
804
+ };
805
+ case "wait":
806
+ if ("number" == typeof action.ms) {
807
+ await page.waitForTimeout(action.ms);
808
+ return {
809
+ type: action.type,
810
+ ms: action.ms
811
+ };
812
+ }
813
+ return runConditionalWait(page, {
814
+ type: "wait_for",
815
+ selector: action.selector,
816
+ url: action.url,
817
+ load: action.load,
818
+ fn: action.fn,
819
+ timeoutMs: action.timeoutMs
820
+ }, timeoutMs);
821
+ case "ms":
822
+ await page.waitForTimeout(action.ms);
823
+ return {
824
+ type: "wait",
825
+ ms: action.ms
826
+ };
827
+ case "sleep":
828
+ await page.waitForTimeout(action.ms);
829
+ return {
830
+ type: "wait",
831
+ ms: action.ms
832
+ };
833
+ case "pause":
834
+ await page.waitForTimeout(action.ms);
835
+ return {
836
+ type: "wait",
837
+ ms: action.ms
838
+ };
839
+ case "wait_for":
840
+ return runConditionalWait(page, action, timeoutMs);
841
+ case "wait_until":
842
+ return runConditionalWait(page, action, timeoutMs);
843
+ case "extract_text":
844
+ {
845
+ const selector = action.selector || "body";
846
+ const content = await page.textContent(selector, {
847
+ timeout: timeoutMs
848
+ });
849
+ const text = content || "";
850
+ const maxChars = action.maxChars ?? DEFAULT_MAX_EXTRACT_CHARS;
851
+ return {
852
+ type: action.type,
853
+ selector,
854
+ text: text.slice(0, maxChars),
855
+ truncated: text.length > maxChars
856
+ };
857
+ }
858
+ case "selector":
859
+ {
860
+ const content = await page.textContent(action.selector, {
861
+ timeout: timeoutMs
862
+ });
863
+ const text = content || "";
864
+ const maxChars = action.maxChars ?? DEFAULT_MAX_EXTRACT_CHARS;
865
+ return {
866
+ type: "extract_text",
867
+ selector: action.selector,
868
+ text: text.slice(0, maxChars),
869
+ truncated: text.length > maxChars
870
+ };
871
+ }
872
+ case "extract":
873
+ {
874
+ const selector = action.selector || "body";
875
+ const content = await page.textContent(selector, {
876
+ timeout: timeoutMs
877
+ });
878
+ const text = content || "";
879
+ const maxChars = action.maxChars ?? DEFAULT_MAX_EXTRACT_CHARS;
880
+ return {
881
+ type: "extract_text",
882
+ selector,
883
+ text: text.slice(0, maxChars),
884
+ truncated: text.length > maxChars
885
+ };
886
+ }
887
+ case "getContent":
888
+ {
889
+ const selector = action.selector || "body";
890
+ const content = await page.textContent(selector, {
891
+ timeout: timeoutMs
892
+ });
893
+ const text = content || "";
894
+ const maxChars = action.maxChars ?? DEFAULT_MAX_EXTRACT_CHARS;
895
+ return {
896
+ type: "extract_text",
897
+ selector,
898
+ text: text.slice(0, maxChars),
899
+ truncated: text.length > maxChars
900
+ };
901
+ }
902
+ case "get_content":
903
+ {
904
+ const selector = action.selector || "body";
905
+ const content = await page.textContent(selector, {
906
+ timeout: timeoutMs
907
+ });
908
+ const text = content || "";
909
+ const maxChars = action.maxChars ?? DEFAULT_MAX_EXTRACT_CHARS;
910
+ return {
911
+ type: "extract_text",
912
+ selector,
913
+ text: text.slice(0, maxChars),
914
+ truncated: text.length > maxChars
915
+ };
916
+ }
917
+ case "querySelector":
918
+ {
919
+ const selector = action.selector || "body";
920
+ const content = await page.textContent(selector, {
921
+ timeout: timeoutMs
922
+ });
923
+ const text = content || "";
924
+ const maxChars = action.maxChars ?? DEFAULT_MAX_EXTRACT_CHARS;
925
+ return {
926
+ type: "extract_text",
927
+ selector,
928
+ text: text.slice(0, maxChars),
929
+ truncated: text.length > maxChars
930
+ };
931
+ }
932
+ case "query_selector":
933
+ {
934
+ const selector = action.selector || "body";
935
+ const content = await page.textContent(selector, {
936
+ timeout: timeoutMs
937
+ });
938
+ const text = content || "";
939
+ const maxChars = action.maxChars ?? DEFAULT_MAX_EXTRACT_CHARS;
940
+ return {
941
+ type: "extract_text",
942
+ selector,
943
+ text: text.slice(0, maxChars),
944
+ truncated: text.length > maxChars
945
+ };
946
+ }
947
+ case "screenshot":
948
+ {
949
+ const screenshotPath = resolveScreenshotPath(workspace, action.path, now, actionIndex);
950
+ await page.screenshot({
951
+ path: screenshotPath.absolute,
952
+ fullPage: action.fullPage
953
+ });
954
+ return {
955
+ type: action.type,
956
+ path: screenshotPath.relative,
957
+ fullPage: Boolean(action.fullPage)
958
+ };
959
+ }
960
+ case "path":
961
+ {
962
+ const screenshotPath = resolveScreenshotPath(workspace, action.path, now, actionIndex);
963
+ await page.screenshot({
964
+ path: screenshotPath.absolute,
965
+ fullPage: action.fullPage
966
+ });
967
+ return {
968
+ type: "screenshot",
969
+ path: screenshotPath.relative,
970
+ fullPage: Boolean(action.fullPage)
971
+ };
972
+ }
973
+ case "snapshot":
974
+ {
975
+ const screenshotPath = resolveScreenshotPath(workspace, action.path, now, actionIndex);
976
+ await page.screenshot({
977
+ path: screenshotPath.absolute,
978
+ fullPage: action.fullPage
979
+ });
980
+ return {
981
+ type: "screenshot",
982
+ path: screenshotPath.relative,
983
+ fullPage: Boolean(action.fullPage)
984
+ };
985
+ }
986
+ case "capture":
987
+ {
988
+ const screenshotPath = resolveScreenshotPath(workspace, action.path, now, actionIndex);
989
+ await page.screenshot({
990
+ path: screenshotPath.absolute,
991
+ fullPage: action.fullPage
992
+ });
993
+ return {
994
+ type: "screenshot",
995
+ path: screenshotPath.relative,
996
+ fullPage: Boolean(action.fullPage)
997
+ };
998
+ }
999
+ case "evaluate":
1000
+ {
1001
+ const result = await page.evaluate(action.expression);
1002
+ return {
1003
+ type: action.type,
1004
+ expression: action.expression,
1005
+ result: serializeEvaluation(result)
1006
+ };
1007
+ }
1008
+ case "expression":
1009
+ {
1010
+ const result = await page.evaluate(action.expression);
1011
+ return {
1012
+ type: "evaluate",
1013
+ expression: action.expression,
1014
+ result: serializeEvaluation(result)
1015
+ };
1016
+ }
1017
+ case "js":
1018
+ {
1019
+ const result = await page.evaluate(action.expression);
1020
+ return {
1021
+ type: "evaluate",
1022
+ expression: action.expression,
1023
+ result: serializeEvaluation(result)
1024
+ };
1025
+ }
1026
+ case "script":
1027
+ {
1028
+ const result = await page.evaluate(action.expression);
1029
+ return {
1030
+ type: "evaluate",
1031
+ expression: action.expression,
1032
+ result: serializeEvaluation(result)
1033
+ };
1034
+ }
1035
+ default:
1036
+ throw new Error(`Unsupported browser action type: ${action.type}`);
1037
+ }
1038
+ }
1039
+ const createBrowserControlTool = (options = {}, dependencies = {})=>{
1040
+ const workspace = options.workspace || process.cwd();
1041
+ const configWorkspace = options.configWorkspace || workspace;
1042
+ const launchTimeoutMs = options.launchTimeoutMs || DEFAULT_LAUNCH_TIMEOUT_MS;
1043
+ const deps = {
1044
+ ...DEFAULT_BROWSER_CONTROL_DEPENDENCIES,
1045
+ ...dependencies
1046
+ };
1047
+ return (0, external_langchain_namespaceObject.tool)(async (input)=>{
1048
+ let userDataDirSelection = null;
1049
+ let chromeSession = null;
1050
+ let browser = null;
1051
+ let launchedContext = null;
1052
+ let browserTransport = "cdp";
1053
+ let transportFallbackReason = null;
1054
+ const transportRequested = resolveBrowserTransportPreference(options.browserTransport);
1055
+ let reusedExistingCdpSession = false;
1056
+ let context = null;
1057
+ try {
1058
+ userDataDirSelection = resolveUserDataDir(configWorkspace, options, deps);
1059
+ const selectedUserDataDir = userDataDirSelection;
1060
+ if (!selectedUserDataDir) throw new Error("Failed to resolve browser user data directory.");
1061
+ const headless = resolveHeadlessMode(input.headless, selectedUserDataDir.persistentProfile);
1062
+ const timeoutMs = input.timeoutMs ?? DEFAULT_ACTION_TIMEOUT_MS;
1063
+ const startupTimeoutMs = Math.max(launchTimeoutMs, timeoutMs);
1064
+ const playwright = await deps.importPlaywright();
1065
+ const relayConfig = resolveRelayConfig(options);
1066
+ const executablePath = input.executablePath || options.defaultExecutablePath;
1067
+ const { extensionIds, extensionDirs } = resolveEnabledExtensions(configWorkspace, options);
1068
+ const chromeArgs = buildChromeArgs(extensionDirs);
1069
+ const usePersistentLaunch = preferPersistentLaunch(options, selectedUserDataDir.persistentProfile);
1070
+ const closeTransientSessions = async ()=>{
1071
+ if (launchedContext?.close) {
1072
+ try {
1073
+ await launchedContext.close();
1074
+ } catch {}
1075
+ launchedContext = null;
1076
+ }
1077
+ if (browser) {
1078
+ if (!reusedExistingCdpSession) try {
1079
+ await browser.close();
1080
+ } catch {}
1081
+ browser = null;
1082
+ }
1083
+ if (chromeSession) {
1084
+ try {
1085
+ await chromeSession.close();
1086
+ } catch {}
1087
+ chromeSession = null;
1088
+ }
1089
+ context = null;
1090
+ reusedExistingCdpSession = false;
1091
+ };
1092
+ const initializeViaRelay = async (connectTimeoutMs)=>{
1093
+ if (!relayConfig) throw new Error("Browser relay transport requested but browser.relay.enabled is false.");
1094
+ const relayWsEndpoint = await deps.resolveRelayWsEndpoint(relayConfig, connectTimeoutMs);
1095
+ browser = await playwright.chromium.connectOverCDP(relayWsEndpoint, {
1096
+ timeout: connectTimeoutMs
1097
+ });
1098
+ context = selectContext(browser.contexts());
1099
+ browserTransport = "relay-cdp";
1100
+ reusedExistingCdpSession = false;
1101
+ };
1102
+ const initializeViaPlaywright = async ()=>{
1103
+ if (usePersistentLaunch) {
1104
+ launchedContext = await launchPersistentContext(playwright, selectedUserDataDir.userDataDir, executablePath, headless, startupTimeoutMs, chromeArgs);
1105
+ context = launchedContext;
1106
+ browserTransport = "persistent-context";
1107
+ return;
1108
+ }
1109
+ if (selectedUserDataDir.persistentProfile) {
1110
+ const existingWsEndpoint = readDevtoolsEndpointFromFile(selectedUserDataDir.userDataDir);
1111
+ if (existingWsEndpoint) try {
1112
+ browser = await playwright.chromium.connectOverCDP(existingWsEndpoint, {
1113
+ timeout: startupTimeoutMs
1114
+ });
1115
+ context = selectContext(browser.contexts());
1116
+ reusedExistingCdpSession = true;
1117
+ } catch (reuseError) {
1118
+ logger.warn(`browser_control failed to attach to existing CDP endpoint, launching a fresh session: ${reuseError instanceof Error ? reuseError.message : String(reuseError)}`);
1119
+ if (browser) {
1120
+ try {
1121
+ await browser.close();
1122
+ } catch {}
1123
+ browser = null;
1124
+ }
1125
+ }
1126
+ }
1127
+ if (!context) {
1128
+ chromeSession = await deps.startChrome({
1129
+ executablePath,
1130
+ headless,
1131
+ launchTimeoutMs: startupTimeoutMs,
1132
+ userDataDir: selectedUserDataDir.userDataDir,
1133
+ chromeArgs
1134
+ });
1135
+ try {
1136
+ browser = await playwright.chromium.connectOverCDP(chromeSession.wsEndpoint, {
1137
+ timeout: startupTimeoutMs
1138
+ });
1139
+ context = selectContext(browser.contexts());
1140
+ } catch (cdpError) {
1141
+ const cdpMessage = cdpError instanceof Error ? cdpError.message : String(cdpError);
1142
+ if (browser) {
1143
+ try {
1144
+ await browser.close();
1145
+ } catch {}
1146
+ browser = null;
1147
+ }
1148
+ if (chromeSession) {
1149
+ try {
1150
+ await chromeSession.close();
1151
+ } catch {}
1152
+ chromeSession = null;
1153
+ }
1154
+ if (selectedUserDataDir.persistentProfile) {
1155
+ logger.warn(`browser_control CDP connection failed for persistent profile, retrying CDP once: ${cdpMessage}`);
1156
+ try {
1157
+ chromeSession = await deps.startChrome({
1158
+ executablePath,
1159
+ headless,
1160
+ launchTimeoutMs: startupTimeoutMs,
1161
+ userDataDir: selectedUserDataDir.userDataDir,
1162
+ chromeArgs
1163
+ });
1164
+ browser = await playwright.chromium.connectOverCDP(chromeSession.wsEndpoint, {
1165
+ timeout: startupTimeoutMs
1166
+ });
1167
+ context = selectContext(browser.contexts());
1168
+ } catch (retryError) {
1169
+ const retryMessage = retryError instanceof Error ? retryError.message : String(retryError);
1170
+ if (browser) {
1171
+ try {
1172
+ await browser.close();
1173
+ } catch {}
1174
+ browser = null;
1175
+ }
1176
+ if (chromeSession) {
1177
+ try {
1178
+ await chromeSession.close();
1179
+ } catch {}
1180
+ chromeSession = null;
1181
+ }
1182
+ const launchPersistent = playwright.chromium.launchPersistentContext;
1183
+ if ("function" != typeof launchPersistent) throw new Error(`CDP connection failed for persistent profile after retry. Initial error: ${cdpMessage}. Retry error: ${retryMessage}`);
1184
+ logger.warn(`browser_control CDP retry failed for persistent profile, falling back to persistent launch: ${retryMessage}`);
1185
+ launchedContext = await launchPersistentContext(playwright, selectedUserDataDir.userDataDir, executablePath, headless, startupTimeoutMs, chromeArgs);
1186
+ context = launchedContext;
1187
+ browserTransport = "persistent-context";
1188
+ }
1189
+ } else {
1190
+ const launchPersistent = playwright.chromium.launchPersistentContext;
1191
+ if ("function" != typeof launchPersistent) throw cdpError;
1192
+ logger.warn(`browser_control CDP connection failed, retrying with persistent launch: ${cdpMessage}`);
1193
+ launchedContext = await launchPersistentContext(playwright, selectedUserDataDir.userDataDir, executablePath, headless, startupTimeoutMs, chromeArgs);
1194
+ context = launchedContext;
1195
+ browserTransport = "persistent-context";
1196
+ }
1197
+ }
1198
+ }
1199
+ };
1200
+ if ("relay" === transportRequested) await initializeViaRelay(startupTimeoutMs);
1201
+ else try {
1202
+ await initializeViaPlaywright();
1203
+ } catch (playwrightError) {
1204
+ if ("auto" !== transportRequested || !relayConfig) throw playwrightError;
1205
+ await closeTransientSessions();
1206
+ const relayTimeoutMs = Math.min(startupTimeoutMs, DEFAULT_RELAY_CONNECT_TIMEOUT_MS);
1207
+ const playwrightMessage = playwrightError instanceof Error ? playwrightError.message : String(playwrightError);
1208
+ logger.warn(`browser_control playwright initialization failed in auto mode, falling back to relay: ${playwrightMessage}`);
1209
+ await initializeViaRelay(relayTimeoutMs);
1210
+ transportFallbackReason = `playwright initialization failed: ${playwrightMessage}`;
1211
+ }
1212
+ const resolvedContext = context;
1213
+ if (!resolvedContext) throw new Error("Failed to initialize browser context.");
1214
+ const activeContext = resolvedContext;
1215
+ let page = activeContext.pages().at(-1);
1216
+ if (!page) page = await activeContext.newPage();
1217
+ if ("function" == typeof page.bringToFront) await page.bringToFront();
1218
+ if (input.url) await page.goto(input.url, {
1219
+ waitUntil: "domcontentloaded",
1220
+ timeout: timeoutMs
1221
+ });
1222
+ const actionResults = [];
1223
+ for (const [index, action] of (input.actions || []).entries()){
1224
+ const result = await runAction(page, action, timeoutMs, workspace, deps.now, index);
1225
+ actionResults.push(result);
1226
+ }
1227
+ const summary = {
1228
+ browser: "cdp" === browserTransport ? "chrome-cdp" : "persistent-context" === browserTransport ? "chrome-playwright" : "chrome-relay",
1229
+ transport: browserTransport,
1230
+ transportRequested,
1231
+ transportUsed: browserTransport,
1232
+ fallbackReason: transportFallbackReason,
1233
+ mode: headless ? "headless" : "headed",
1234
+ persistentProfile: selectedUserDataDir.persistentProfile,
1235
+ profileId: selectedUserDataDir.profileId || null,
1236
+ profilePath: selectedUserDataDir.persistentProfile ? selectedUserDataDir.userDataDir : null,
1237
+ reusedExistingSession: reusedExistingCdpSession,
1238
+ executionWorkspace: workspace,
1239
+ configWorkspace,
1240
+ extensions: extensionIds,
1241
+ finalUrl: page.url(),
1242
+ title: await page.title(),
1243
+ actionResults
1244
+ };
1245
+ return JSON.stringify(summary, null, 2);
1246
+ } catch (error) {
1247
+ const message = error instanceof Error ? error.message : "Unknown browser error";
1248
+ logger.error(`browser_control failed: ${message}`);
1249
+ return `Error running browser_control: ${message}`;
1250
+ } finally{
1251
+ const contextToClose = launchedContext;
1252
+ if (contextToClose?.close) try {
1253
+ await contextToClose.close();
1254
+ } catch {}
1255
+ const browserToClose = browser;
1256
+ if (browserToClose) {
1257
+ if (!reusedExistingCdpSession) try {
1258
+ await browserToClose.close();
1259
+ } catch {}
1260
+ }
1261
+ const chromeSessionToClose = chromeSession;
1262
+ if (chromeSessionToClose) try {
1263
+ await chromeSessionToClose.close();
1264
+ } catch {}
1265
+ if (userDataDirSelection?.releaseLock) userDataDirSelection.releaseLock();
1266
+ if (userDataDirSelection && !userDataDirSelection.persistentProfile) deps.removeDir(userDataDirSelection.userDataDir);
1267
+ }
1268
+ }, {
1269
+ name: "browser_control",
1270
+ description: 'Native browser automation for Wingman using Chrome/Chromium runtime control. Transport is selected by config ("auto", "playwright", or "relay"): Playwright persistent-context is preferred for persistent profiles, CDP is used for standard runs with persistent-context fallback, and relay can bridge a live extension-attached tab. This is a first-class runtime capability, not an MCP server. Use it for JavaScript-rendered pages, interactions, screenshots, and structured extraction.',
1271
+ schema: BrowserControlInputSchema
1272
+ });
1273
+ };
1274
+ exports.clearStaleDevtoolsArtifacts = __webpack_exports__.clearStaleDevtoolsArtifacts;
1275
+ exports.createBrowserControlTool = __webpack_exports__.createBrowserControlTool;
1276
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
1277
+ "clearStaleDevtoolsArtifacts",
1278
+ "createBrowserControlTool"
1279
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
1280
+ Object.defineProperty(exports, '__esModule', {
1281
+ value: true
1282
+ });