@godscene/web 1.7.11

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 (129) hide show
  1. package/README.md +7 -0
  2. package/bin/midscene-playground +3 -0
  3. package/bin/midscene-web +2 -0
  4. package/dist/es/bin.mjs +14 -0
  5. package/dist/es/bridge-mode/agent-cli-side.mjs +135 -0
  6. package/dist/es/bridge-mode/browser.mjs +2 -0
  7. package/dist/es/bridge-mode/common.mjs +41 -0
  8. package/dist/es/bridge-mode/index.mjs +4 -0
  9. package/dist/es/bridge-mode/io-client.mjs +99 -0
  10. package/dist/es/bridge-mode/io-server.mjs +218 -0
  11. package/dist/es/bridge-mode/page-browser-side.mjs +119 -0
  12. package/dist/es/cdp-proxy-constants.mjs +7 -0
  13. package/dist/es/cdp-proxy-manager.mjs +217 -0
  14. package/dist/es/cdp-proxy.mjs +151 -0
  15. package/dist/es/cdp-target-store.mjs +26 -0
  16. package/dist/es/chrome-extension/agent.mjs +8 -0
  17. package/dist/es/chrome-extension/cdpInput.mjs +172 -0
  18. package/dist/es/chrome-extension/cdpInput.mjs.LICENSE.txt +5 -0
  19. package/dist/es/chrome-extension/dynamic-scripts.mjs +36 -0
  20. package/dist/es/chrome-extension/index.mjs +5 -0
  21. package/dist/es/chrome-extension/page.mjs +733 -0
  22. package/dist/es/cli-options.mjs +97 -0
  23. package/dist/es/cli.mjs +26 -0
  24. package/dist/es/common/cache-helper.mjs +26 -0
  25. package/dist/es/common/viewport.mjs +36 -0
  26. package/dist/es/index.mjs +8 -0
  27. package/dist/es/mcp-server.mjs +33 -0
  28. package/dist/es/mcp-tools-cdp.mjs +164 -0
  29. package/dist/es/mcp-tools-puppeteer.mjs +246 -0
  30. package/dist/es/mcp-tools.mjs +81 -0
  31. package/dist/es/platform.mjs +37 -0
  32. package/dist/es/playwright/ai-fixture.mjs +364 -0
  33. package/dist/es/playwright/index.mjs +36 -0
  34. package/dist/es/playwright/page.mjs +42 -0
  35. package/dist/es/playwright/reporter/index.mjs +178 -0
  36. package/dist/es/puppeteer/agent-launcher.mjs +172 -0
  37. package/dist/es/puppeteer/base-page.mjs +830 -0
  38. package/dist/es/puppeteer/index.mjs +35 -0
  39. package/dist/es/puppeteer/page.mjs +7 -0
  40. package/dist/es/static/index.mjs +3 -0
  41. package/dist/es/static/static-agent.mjs +10 -0
  42. package/dist/es/static/static-page.mjs +123 -0
  43. package/dist/es/utils.mjs +6 -0
  44. package/dist/es/web-element.mjs +57 -0
  45. package/dist/es/web-page.mjs +272 -0
  46. package/dist/lib/bin.js +20 -0
  47. package/dist/lib/bridge-mode/agent-cli-side.js +172 -0
  48. package/dist/lib/bridge-mode/browser.js +36 -0
  49. package/dist/lib/bridge-mode/common.js +105 -0
  50. package/dist/lib/bridge-mode/index.js +44 -0
  51. package/dist/lib/bridge-mode/io-client.js +133 -0
  52. package/dist/lib/bridge-mode/io-server.js +255 -0
  53. package/dist/lib/bridge-mode/page-browser-side.js +163 -0
  54. package/dist/lib/cdp-proxy-constants.js +50 -0
  55. package/dist/lib/cdp-proxy-manager.js +273 -0
  56. package/dist/lib/cdp-proxy.js +179 -0
  57. package/dist/lib/cdp-target-store.js +66 -0
  58. package/dist/lib/chrome-extension/agent.js +42 -0
  59. package/dist/lib/chrome-extension/cdpInput.js +206 -0
  60. package/dist/lib/chrome-extension/cdpInput.js.LICENSE.txt +5 -0
  61. package/dist/lib/chrome-extension/dynamic-scripts.js +86 -0
  62. package/dist/lib/chrome-extension/index.js +58 -0
  63. package/dist/lib/chrome-extension/page.js +767 -0
  64. package/dist/lib/cli-options.js +131 -0
  65. package/dist/lib/cli.js +54 -0
  66. package/dist/lib/common/cache-helper.js +66 -0
  67. package/dist/lib/common/viewport.js +88 -0
  68. package/dist/lib/index.js +66 -0
  69. package/dist/lib/mcp-server.js +73 -0
  70. package/dist/lib/mcp-tools-cdp.js +208 -0
  71. package/dist/lib/mcp-tools-puppeteer.js +296 -0
  72. package/dist/lib/mcp-tools.js +115 -0
  73. package/dist/lib/platform.js +71 -0
  74. package/dist/lib/playwright/ai-fixture.js +401 -0
  75. package/dist/lib/playwright/index.js +89 -0
  76. package/dist/lib/playwright/page.js +76 -0
  77. package/dist/lib/playwright/reporter/index.js +212 -0
  78. package/dist/lib/puppeteer/agent-launcher.js +240 -0
  79. package/dist/lib/puppeteer/base-page.js +876 -0
  80. package/dist/lib/puppeteer/index.js +85 -0
  81. package/dist/lib/puppeteer/page.js +41 -0
  82. package/dist/lib/static/index.js +50 -0
  83. package/dist/lib/static/static-agent.js +44 -0
  84. package/dist/lib/static/static-page.js +157 -0
  85. package/dist/lib/utils.js +38 -0
  86. package/dist/lib/web-element.js +94 -0
  87. package/dist/lib/web-page.js +322 -0
  88. package/dist/types/bin.d.ts +1 -0
  89. package/dist/types/bridge-mode/agent-cli-side.d.ts +49 -0
  90. package/dist/types/bridge-mode/browser.d.ts +2 -0
  91. package/dist/types/bridge-mode/common.d.ts +74 -0
  92. package/dist/types/bridge-mode/index.d.ts +4 -0
  93. package/dist/types/bridge-mode/io-client.d.ts +10 -0
  94. package/dist/types/bridge-mode/io-server.d.ts +27 -0
  95. package/dist/types/bridge-mode/page-browser-side.d.ts +21 -0
  96. package/dist/types/cdp-proxy-constants.d.ts +4 -0
  97. package/dist/types/cdp-proxy-manager.d.ts +53 -0
  98. package/dist/types/cdp-proxy.d.ts +37 -0
  99. package/dist/types/cdp-target-store.d.ts +26 -0
  100. package/dist/types/chrome-extension/agent.d.ts +4 -0
  101. package/dist/types/chrome-extension/cdpInput.d.ts +52 -0
  102. package/dist/types/chrome-extension/dynamic-scripts.d.ts +3 -0
  103. package/dist/types/chrome-extension/index.d.ts +5 -0
  104. package/dist/types/chrome-extension/page.d.ts +120 -0
  105. package/dist/types/cli-options.d.ts +8 -0
  106. package/dist/types/cli.d.ts +1 -0
  107. package/dist/types/common/cache-helper.d.ts +20 -0
  108. package/dist/types/common/viewport.d.ts +17 -0
  109. package/dist/types/index.d.ts +9 -0
  110. package/dist/types/mcp-server.d.ts +26 -0
  111. package/dist/types/mcp-tools-cdp.d.ts +23 -0
  112. package/dist/types/mcp-tools-puppeteer.d.ts +23 -0
  113. package/dist/types/mcp-tools.d.ts +14 -0
  114. package/dist/types/platform.d.ts +10 -0
  115. package/dist/types/playwright/ai-fixture.d.ts +133 -0
  116. package/dist/types/playwright/index.d.ts +13 -0
  117. package/dist/types/playwright/page.d.ts +11 -0
  118. package/dist/types/playwright/reporter/index.d.ts +28 -0
  119. package/dist/types/puppeteer/agent-launcher.d.ts +59 -0
  120. package/dist/types/puppeteer/base-page.d.ts +123 -0
  121. package/dist/types/puppeteer/index.d.ts +11 -0
  122. package/dist/types/puppeteer/page.d.ts +6 -0
  123. package/dist/types/static/index.d.ts +2 -0
  124. package/dist/types/static/static-agent.d.ts +5 -0
  125. package/dist/types/static/static-page.d.ts +46 -0
  126. package/dist/types/utils.d.ts +6 -0
  127. package/dist/types/web-element.d.ts +48 -0
  128. package/dist/types/web-page.d.ts +69 -0
  129. package/package.json +173 -0
@@ -0,0 +1,217 @@
1
+ import { spawn } from "node:child_process";
2
+ import { existsSync, readFileSync, unlinkSync } from "node:fs";
3
+ import node_http from "node:http";
4
+ import { join } from "node:path";
5
+ import { getDebug } from "@godscene/shared/logger";
6
+ import { PROXY_ENDPOINT_FILE, PROXY_PID_FILE, PROXY_UPSTREAM_FILE } from "./cdp-proxy-constants.mjs";
7
+ import { cleanupTargetIdFile } from "./cdp-target-store.mjs";
8
+ const debug = getDebug('mcp:cdp:proxy');
9
+ const PROXY_TERM_GRACE_MS = 2000;
10
+ const PROXY_TERM_POLL_MS = 50;
11
+ const PROXY_STDERR_BUFFER_LIMIT = 8192;
12
+ const PROXY_STARTUP_TIMEOUT_MS = 10000;
13
+ function isPageLevelEndpoint(endpoint) {
14
+ return /\/devtools\/page\//.test(endpoint);
15
+ }
16
+ function resolveBrowserEndpoint(pageEndpoint) {
17
+ return new Promise((resolve, reject)=>{
18
+ let host;
19
+ try {
20
+ const url = new URL(pageEndpoint);
21
+ host = url.host;
22
+ } catch {
23
+ reject(new Error(`Invalid CDP endpoint URL: ${pageEndpoint}`));
24
+ return;
25
+ }
26
+ const req = node_http.get(`http://${host}/json/version`, {
27
+ timeout: 5000
28
+ }, (res)=>{
29
+ if (res.statusCode && res.statusCode >= 400) {
30
+ reject(new Error(`/json/version returned HTTP ${res.statusCode}`));
31
+ res.resume();
32
+ return;
33
+ }
34
+ let data = '';
35
+ res.on('data', (chunk)=>{
36
+ data += chunk.toString();
37
+ });
38
+ res.on('end', ()=>{
39
+ try {
40
+ const info = JSON.parse(data);
41
+ if (info.webSocketDebuggerUrl) resolve(info.webSocketDebuggerUrl);
42
+ else reject(new Error('webSocketDebuggerUrl not found in /json/version response'));
43
+ } catch {
44
+ reject(new Error(`Failed to parse /json/version response: ${data}`));
45
+ }
46
+ });
47
+ });
48
+ req.on('error', (err)=>reject(new Error(`Failed to fetch /json/version: ${err.message}`)));
49
+ req.on('timeout', ()=>{
50
+ req.destroy();
51
+ reject(new Error('Timeout fetching /json/version'));
52
+ });
53
+ });
54
+ }
55
+ function isProxyAlive() {
56
+ if (!existsSync(PROXY_PID_FILE)) return false;
57
+ try {
58
+ const pid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());
59
+ process.kill(pid, 0);
60
+ return true;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+ function readProxyEndpoint() {
66
+ if (!existsSync(PROXY_ENDPOINT_FILE)) return null;
67
+ try {
68
+ return readFileSync(PROXY_ENDPOINT_FILE, 'utf-8').trim();
69
+ } catch {
70
+ return null;
71
+ }
72
+ }
73
+ function readProxyUpstream() {
74
+ if (!existsSync(PROXY_UPSTREAM_FILE)) return null;
75
+ try {
76
+ return readFileSync(PROXY_UPSTREAM_FILE, 'utf-8').trim();
77
+ } catch {
78
+ return null;
79
+ }
80
+ }
81
+ function sweepProxyMetadataFiles() {
82
+ try {
83
+ if (existsSync(PROXY_ENDPOINT_FILE)) unlinkSync(PROXY_ENDPOINT_FILE);
84
+ } catch {}
85
+ try {
86
+ if (existsSync(PROXY_PID_FILE)) unlinkSync(PROXY_PID_FILE);
87
+ } catch {}
88
+ try {
89
+ if (existsSync(PROXY_UPSTREAM_FILE)) unlinkSync(PROXY_UPSTREAM_FILE);
90
+ } catch {}
91
+ }
92
+ async function killProxy() {
93
+ cleanupTargetIdFile();
94
+ if (!existsSync(PROXY_PID_FILE)) return;
95
+ let pid;
96
+ try {
97
+ pid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());
98
+ if (!Number.isFinite(pid)) return void sweepProxyMetadataFiles();
99
+ } catch (err) {
100
+ debug('killProxy: cannot read pid file: %s', err);
101
+ sweepProxyMetadataFiles();
102
+ return;
103
+ }
104
+ try {
105
+ process.kill(pid, 'SIGTERM');
106
+ debug('Sent SIGTERM to proxy pid %d', pid);
107
+ } catch (err) {
108
+ debug('killProxy: SIGTERM failed (pid %d): %s', pid, err);
109
+ sweepProxyMetadataFiles();
110
+ return;
111
+ }
112
+ const deadline = Date.now() + PROXY_TERM_GRACE_MS;
113
+ while(Date.now() < deadline && existsSync(PROXY_PID_FILE))await new Promise((r)=>setTimeout(r, PROXY_TERM_POLL_MS));
114
+ if (existsSync(PROXY_PID_FILE)) {
115
+ debug('proxy pid %d did not clean up within %dms, forcing SIGKILL', pid, PROXY_TERM_GRACE_MS);
116
+ try {
117
+ process.kill(pid, 'SIGKILL');
118
+ } catch {}
119
+ sweepProxyMetadataFiles();
120
+ }
121
+ }
122
+ function spawnProxy(chromeEndpoint) {
123
+ return new Promise((resolve, reject)=>{
124
+ const proxyScript = join(__dirname, 'cdp-proxy.js');
125
+ const proc = spawn(process.execPath, [
126
+ proxyScript,
127
+ chromeEndpoint
128
+ ], {
129
+ detached: true,
130
+ stdio: [
131
+ 'ignore',
132
+ 'pipe',
133
+ 'pipe'
134
+ ]
135
+ });
136
+ proc.unref();
137
+ let output = '';
138
+ let stderrBuf = '';
139
+ let settled = false;
140
+ const appendStderr = (chunk)=>{
141
+ stderrBuf += chunk.toString();
142
+ if (stderrBuf.length > PROXY_STDERR_BUFFER_LIMIT) stderrBuf = stderrBuf.slice(-PROXY_STDERR_BUFFER_LIMIT);
143
+ };
144
+ proc.stderr.on('data', appendStderr);
145
+ const formatStderr = ()=>{
146
+ const trimmed = stderrBuf.trim();
147
+ return trimmed ? ` (stderr: ${trimmed})` : '';
148
+ };
149
+ const timer = setTimeout(()=>{
150
+ if (!settled) {
151
+ settled = true;
152
+ reject(new Error(`Proxy startup timeout (${PROXY_STARTUP_TIMEOUT_MS / 1000}s)${formatStderr()}`));
153
+ }
154
+ }, PROXY_STARTUP_TIMEOUT_MS);
155
+ const onData = (chunk)=>{
156
+ output += chunk.toString();
157
+ const lines = output.split('\n');
158
+ for (const line of lines)if (line.trim()) try {
159
+ const parsed = JSON.parse(line);
160
+ if (parsed.endpoint && !settled) {
161
+ settled = true;
162
+ clearTimeout(timer);
163
+ proc.stdout.removeListener('data', onData);
164
+ proc.stderr.removeListener('data', appendStderr);
165
+ proc.stdout.destroy();
166
+ proc.stderr.destroy();
167
+ resolve(parsed.endpoint);
168
+ return;
169
+ }
170
+ } catch {}
171
+ };
172
+ proc.stdout.on('data', onData);
173
+ proc.on('error', (err)=>{
174
+ if (!settled) {
175
+ settled = true;
176
+ clearTimeout(timer);
177
+ reject(new Error(`Failed to spawn proxy: ${err.message}`));
178
+ }
179
+ });
180
+ proc.on('exit', (code, signal)=>{
181
+ if (!settled) {
182
+ settled = true;
183
+ clearTimeout(timer);
184
+ const how = signal ? `signal ${signal}` : `code ${code}`;
185
+ reject(new Error(`Proxy exited with ${how} before ready${formatStderr()}`));
186
+ }
187
+ });
188
+ });
189
+ }
190
+ async function getProxyEndpoint(chromeEndpoint) {
191
+ let browserEndpoint = chromeEndpoint;
192
+ if (isPageLevelEndpoint(chromeEndpoint)) {
193
+ debug('Page-level CDP endpoint detected, resolving via /json/version: %s', chromeEndpoint);
194
+ try {
195
+ browserEndpoint = await resolveBrowserEndpoint(chromeEndpoint);
196
+ debug('Resolved browser endpoint: %s', browserEndpoint);
197
+ } catch (err) {
198
+ throw new Error(`Cannot use page-level CDP endpoint directly. Puppeteer requires a browser-level endpoint (e.g., ws://host:port/devtools/browser/<id>). Auto-resolution via /json/version failed: ${err.message}. Please provide a browser-level CDP endpoint instead.`);
199
+ }
200
+ }
201
+ if (isProxyAlive()) {
202
+ const endpoint = readProxyEndpoint();
203
+ const savedUpstream = readProxyUpstream();
204
+ if (endpoint) if (!savedUpstream || savedUpstream === browserEndpoint) return endpoint;
205
+ else {
206
+ debug('Proxy connected to different upstream (%s), killing', savedUpstream);
207
+ await killProxy();
208
+ }
209
+ }
210
+ try {
211
+ return await spawnProxy(browserEndpoint);
212
+ } catch (err) {
213
+ console.warn(`[cdp] proxy failed, falling back to direct connection: ${err}`);
214
+ return browserEndpoint;
215
+ }
216
+ }
217
+ export { getProxyEndpoint, isProxyAlive, killProxy, readProxyEndpoint, readProxyUpstream };
@@ -0,0 +1,151 @@
1
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { createServer } from "node:http";
3
+ import ws_0, { WebSocketServer } from "ws";
4
+ import { PROXY_ENDPOINT_FILE, PROXY_PID_FILE, PROXY_UPSTREAM_FILE } from "./cdp-proxy-constants.mjs";
5
+ const IDLE_TIMEOUT_MS = 300000;
6
+ const chromeEndpoint = process.argv[2];
7
+ if (!chromeEndpoint) {
8
+ process.stderr.write('Usage: node cdp-proxy.js <chrome-ws-endpoint>\n');
9
+ process.exit(1);
10
+ }
11
+ function cleanupIfOwned() {
12
+ try {
13
+ if (!existsSync(PROXY_PID_FILE)) return;
14
+ const pid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());
15
+ if (pid !== process.pid) return;
16
+ } catch {
17
+ return;
18
+ }
19
+ try {
20
+ if (existsSync(PROXY_ENDPOINT_FILE)) unlinkSync(PROXY_ENDPOINT_FILE);
21
+ } catch {}
22
+ try {
23
+ if (existsSync(PROXY_PID_FILE)) unlinkSync(PROXY_PID_FILE);
24
+ } catch {}
25
+ try {
26
+ if (existsSync(PROXY_UPSTREAM_FILE)) unlinkSync(PROXY_UPSTREAM_FILE);
27
+ } catch {}
28
+ }
29
+ const STDERR_FLUSH_FALLBACK_MS = 500;
30
+ function exitWithStderr(message, code = 0) {
31
+ let exited = false;
32
+ const doExit = ()=>{
33
+ if (exited) return;
34
+ exited = true;
35
+ process.exit(code);
36
+ };
37
+ const fallback = setTimeout(doExit, STDERR_FLUSH_FALLBACK_MS);
38
+ fallback.unref?.();
39
+ try {
40
+ process.stderr.write(message, ()=>doExit());
41
+ } catch {
42
+ doExit();
43
+ }
44
+ }
45
+ function shutdown(reason) {
46
+ cleanupIfOwned();
47
+ exitWithStderr(`[cdp-proxy] shutting down: ${reason}\n`, 0);
48
+ }
49
+ process.on('SIGTERM', ()=>shutdown('SIGTERM'));
50
+ process.on('SIGINT', ()=>shutdown('SIGINT'));
51
+ process.on('uncaughtException', (e)=>shutdown(`uncaught: ${e.message}`));
52
+ let idleTimer = null;
53
+ function resetIdleTimer() {
54
+ if (idleTimer) clearTimeout(idleTimer);
55
+ idleTimer = setTimeout(()=>shutdown('idle timeout (5min)'), IDLE_TIMEOUT_MS);
56
+ }
57
+ resetIdleTimer();
58
+ const clients = new Set();
59
+ let reconnecting = false;
60
+ let needsUpstreamReconnect = false;
61
+ const pendingUpstreamMessages = [];
62
+ function createUpstream(endpoint) {
63
+ const ws = new ws_0(endpoint);
64
+ ws.on('error', (err)=>{
65
+ reconnecting = false;
66
+ shutdown(`upstream error: ${err.message}`);
67
+ });
68
+ ws.on('close', (code, reasonBuf)=>{
69
+ if (reconnecting) return;
70
+ const reason = reasonBuf?.toString?.() || '';
71
+ const detail = reason ? ` (code=${code}, reason=${reason})` : ` (code=${code})`;
72
+ shutdown(`upstream closed${detail}`);
73
+ });
74
+ ws.on('message', (data, isBinary)=>{
75
+ resetIdleTimer();
76
+ for (const client of clients)if (client.readyState === ws_0.OPEN) client.send(data, {
77
+ binary: isBinary
78
+ });
79
+ });
80
+ ws.on('open', ()=>{
81
+ reconnecting = false;
82
+ for (const msg of pendingUpstreamMessages)ws.send(msg.data, {
83
+ binary: msg.isBinary
84
+ });
85
+ pendingUpstreamMessages.length = 0;
86
+ });
87
+ return ws;
88
+ }
89
+ let upstream = createUpstream(chromeEndpoint);
90
+ function reconnectUpstream() {
91
+ reconnecting = true;
92
+ upstream.removeAllListeners();
93
+ upstream.close();
94
+ upstream = createUpstream(chromeEndpoint);
95
+ resetIdleTimer();
96
+ }
97
+ const httpServer = createServer((_req, res)=>{
98
+ res.writeHead(404);
99
+ res.end();
100
+ });
101
+ const wss = new WebSocketServer({
102
+ server: httpServer
103
+ });
104
+ wss.on('connection', (clientWs)=>{
105
+ if (needsUpstreamReconnect && upstream.readyState === ws_0.OPEN) {
106
+ reconnectUpstream();
107
+ needsUpstreamReconnect = false;
108
+ }
109
+ clients.add(clientWs);
110
+ resetIdleTimer();
111
+ clientWs.on('message', (data, isBinary)=>{
112
+ resetIdleTimer();
113
+ if (upstream.readyState === ws_0.OPEN) upstream.send(data, {
114
+ binary: isBinary
115
+ });
116
+ else pendingUpstreamMessages.push({
117
+ data,
118
+ isBinary
119
+ });
120
+ });
121
+ const removeClient = ()=>{
122
+ clients.delete(clientWs);
123
+ if (0 === clients.size) {
124
+ pendingUpstreamMessages.length = 0;
125
+ needsUpstreamReconnect = true;
126
+ }
127
+ };
128
+ clientWs.on('close', removeClient);
129
+ clientWs.on('error', removeClient);
130
+ });
131
+ upstream.once('open', ()=>{
132
+ if (existsSync(PROXY_PID_FILE)) try {
133
+ const existingPid = Number(readFileSync(PROXY_PID_FILE, 'utf-8').trim());
134
+ if (existingPid !== process.pid) try {
135
+ process.kill(existingPid, 0);
136
+ exitWithStderr(`[cdp-proxy] duplicate proxy detected (existing pid=${existingPid})\n`, 0);
137
+ return;
138
+ } catch {}
139
+ } catch {}
140
+ httpServer.listen(0, '127.0.0.1', ()=>{
141
+ const addr = httpServer.address();
142
+ if (!addr || 'string' == typeof addr) return void shutdown('failed to get server address');
143
+ const proxyEndpoint = `ws://127.0.0.1:${addr.port}/devtools/browser`;
144
+ writeFileSync(PROXY_ENDPOINT_FILE, proxyEndpoint);
145
+ writeFileSync(PROXY_PID_FILE, String(process.pid));
146
+ writeFileSync(PROXY_UPSTREAM_FILE, chromeEndpoint);
147
+ process.stdout.write(`${JSON.stringify({
148
+ endpoint: proxyEndpoint
149
+ })}\n`);
150
+ });
151
+ });
@@ -0,0 +1,26 @@
1
+ import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
2
+ import { getDebug } from "@godscene/shared/logger";
3
+ import { TARGET_ID_FILE } from "./cdp-proxy-constants.mjs";
4
+ const debug = getDebug('mcp:cdp:target-store');
5
+ function readSavedTargetId() {
6
+ if (!existsSync(TARGET_ID_FILE)) return null;
7
+ try {
8
+ return readFileSync(TARGET_ID_FILE, 'utf-8').trim() || null;
9
+ } catch {
10
+ return null;
11
+ }
12
+ }
13
+ function saveTargetId(targetId) {
14
+ try {
15
+ writeFileSync(TARGET_ID_FILE, targetId, 'utf-8');
16
+ debug('Saved targetId: %s', targetId);
17
+ } catch (err) {
18
+ debug('Failed to save targetId: %s', err);
19
+ }
20
+ }
21
+ function cleanupTargetIdFile() {
22
+ try {
23
+ if (existsSync(TARGET_ID_FILE)) unlinkSync(TARGET_ID_FILE);
24
+ } catch {}
25
+ }
26
+ export { cleanupTargetIdFile, readSavedTargetId, saveTargetId };
@@ -0,0 +1,8 @@
1
+ import { Agent } from "@godscene/core/agent";
2
+ import { BROWSER_NAVIGATION_ERROR_PATTERN } from "../puppeteer/base-page.mjs";
3
+ class ChromeExtensionProxyPageAgent extends Agent {
4
+ isRetryableContextError(error) {
5
+ return error instanceof Error && BROWSER_NAVIGATION_ERROR_PATTERN.test(error.message);
6
+ }
7
+ }
8
+ export { ChromeExtensionProxyPageAgent };
@@ -0,0 +1,172 @@
1
+ /*! For license information please see cdpInput.mjs.LICENSE.txt */
2
+ import { _keyDefinitions } from "@godscene/shared/us-keyboard-layout";
3
+ import { assert } from "@godscene/shared/utils";
4
+ function _check_private_redeclaration(obj, privateCollection) {
5
+ if (privateCollection.has(obj)) throw new TypeError("Cannot initialize the same private elements twice on an object");
6
+ }
7
+ function _class_apply_descriptor_get(receiver, descriptor) {
8
+ if (descriptor.get) return descriptor.get.call(receiver);
9
+ return descriptor.value;
10
+ }
11
+ function _class_apply_descriptor_set(receiver, descriptor, value) {
12
+ if (descriptor.set) descriptor.set.call(receiver, value);
13
+ else {
14
+ if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
15
+ descriptor.value = value;
16
+ }
17
+ }
18
+ function _class_extract_field_descriptor(receiver, privateMap, action) {
19
+ if (!privateMap.has(receiver)) throw new TypeError("attempted to " + action + " private field on non-instance");
20
+ return privateMap.get(receiver);
21
+ }
22
+ function _class_private_field_get(receiver, privateMap) {
23
+ var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
24
+ return _class_apply_descriptor_get(receiver, descriptor);
25
+ }
26
+ function _class_private_field_init(obj, privateMap, value) {
27
+ _check_private_redeclaration(obj, privateMap);
28
+ privateMap.set(obj, value);
29
+ }
30
+ function _class_private_field_set(receiver, privateMap, value) {
31
+ var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
32
+ _class_apply_descriptor_set(receiver, descriptor, value);
33
+ return value;
34
+ }
35
+ function _class_private_method_get(receiver, privateSet, fn) {
36
+ if (!privateSet.has(receiver)) throw new TypeError("attempted to get private field on non-instance");
37
+ return fn;
38
+ }
39
+ function _class_private_method_init(obj, privateSet) {
40
+ _check_private_redeclaration(obj, privateSet);
41
+ privateSet.add(obj);
42
+ }
43
+ function _define_property(obj, key, value) {
44
+ if (key in obj) Object.defineProperty(obj, key, {
45
+ value: value,
46
+ enumerable: true,
47
+ configurable: true,
48
+ writable: true
49
+ });
50
+ else obj[key] = value;
51
+ return obj;
52
+ }
53
+ /**
54
+ * @license
55
+ * Copyright 2017 Google Inc.
56
+ * SPDX-License-Identifier: Apache-2.0
57
+ */ var _pressedKeys = /*#__PURE__*/ new WeakMap(), _client = /*#__PURE__*/ new WeakMap(), _modifierBit = /*#__PURE__*/ new WeakSet(), _keyDescriptionForString = /*#__PURE__*/ new WeakSet();
58
+ class CdpKeyboard {
59
+ updateClient(client) {
60
+ _class_private_field_set(this, _client, client);
61
+ }
62
+ async down(key, options = {
63
+ text: void 0,
64
+ commands: []
65
+ }) {
66
+ const description = _class_private_method_get(this, _keyDescriptionForString, keyDescriptionForString).call(this, key);
67
+ const autoRepeat = _class_private_field_get(this, _pressedKeys).has(description.code);
68
+ _class_private_field_get(this, _pressedKeys).add(description.code);
69
+ this._modifiers |= _class_private_method_get(this, _modifierBit, modifierBit).call(this, description.key);
70
+ const text = void 0 === options.text ? description.text : options.text;
71
+ await _class_private_field_get(this, _client).send('Input.dispatchKeyEvent', {
72
+ type: text ? 'keyDown' : 'rawKeyDown',
73
+ modifiers: this._modifiers,
74
+ windowsVirtualKeyCode: description.keyCode,
75
+ code: description.code,
76
+ key: description.key,
77
+ text: text,
78
+ unmodifiedText: text,
79
+ autoRepeat,
80
+ location: description.location,
81
+ isKeypad: 3 === description.location,
82
+ commands: options.commands
83
+ });
84
+ }
85
+ async up(key) {
86
+ const description = _class_private_method_get(this, _keyDescriptionForString, keyDescriptionForString).call(this, key);
87
+ this._modifiers &= ~_class_private_method_get(this, _modifierBit, modifierBit).call(this, description.key);
88
+ _class_private_field_get(this, _pressedKeys).delete(description.code);
89
+ await _class_private_field_get(this, _client).send('Input.dispatchKeyEvent', {
90
+ type: 'keyUp',
91
+ modifiers: this._modifiers,
92
+ key: description.key,
93
+ windowsVirtualKeyCode: description.keyCode,
94
+ code: description.code,
95
+ location: description.location
96
+ });
97
+ }
98
+ async sendCharacter(char) {
99
+ await _class_private_field_get(this, _client).send('Input.insertText', {
100
+ text: char
101
+ });
102
+ }
103
+ charIsKey(char) {
104
+ return !!_keyDefinitions[char];
105
+ }
106
+ async type(text, options = {}) {
107
+ const delay = options.delay || void 0;
108
+ for (const char of text)if (this.charIsKey(char)) await this.press(char, {
109
+ delay
110
+ });
111
+ else {
112
+ if (delay) await new Promise((f)=>setTimeout(f, delay));
113
+ await this.sendCharacter(char);
114
+ }
115
+ }
116
+ async press(key, options = {}) {
117
+ const { delay = null } = options;
118
+ const keys = Array.isArray(key) ? key : [
119
+ key
120
+ ];
121
+ for (const k of keys)await this.down(k, options);
122
+ if (delay) await new Promise((f)=>setTimeout(f, options.delay));
123
+ for (const k of [
124
+ ...keys
125
+ ].reverse())await this.up(k);
126
+ }
127
+ constructor(client){
128
+ _class_private_method_init(this, _modifierBit);
129
+ _class_private_method_init(this, _keyDescriptionForString);
130
+ _class_private_field_init(this, _pressedKeys, {
131
+ writable: true,
132
+ value: new Set()
133
+ });
134
+ _class_private_field_init(this, _client, {
135
+ writable: true,
136
+ value: void 0
137
+ });
138
+ _define_property(this, "_modifiers", 0);
139
+ _class_private_field_set(this, _client, client);
140
+ }
141
+ }
142
+ function modifierBit(key) {
143
+ if ('Alt' === key) return 1;
144
+ if ('Control' === key) return 2;
145
+ if ('Meta' === key) return 4;
146
+ if ('Shift' === key) return 8;
147
+ return 0;
148
+ }
149
+ function keyDescriptionForString(keyString) {
150
+ const shift = 8 & this._modifiers;
151
+ const description = {
152
+ key: '',
153
+ keyCode: 0,
154
+ code: '',
155
+ text: '',
156
+ location: 0
157
+ };
158
+ const definition = _keyDefinitions[keyString];
159
+ assert(definition, `Unknown key: "${keyString}"`);
160
+ if (definition.key) description.key = definition.key;
161
+ if (shift && definition.shiftKey) description.key = definition.shiftKey;
162
+ if (definition.keyCode) description.keyCode = definition.keyCode;
163
+ if (shift && definition.shiftKeyCode) description.keyCode = definition.shiftKeyCode;
164
+ if (definition.code) description.code = definition.code;
165
+ if (definition.location) description.location = definition.location;
166
+ if (1 === description.key.length) description.text = description.key;
167
+ if (definition.text) description.text = definition.text;
168
+ if (shift && definition.shiftText) description.text = definition.shiftText;
169
+ if (-9 & this._modifiers) description.text = '';
170
+ return description;
171
+ }
172
+ export { CdpKeyboard };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2017 Google Inc.
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
@@ -0,0 +1,36 @@
1
+ import node_fs from "node:fs";
2
+ import { ifInBrowser, ifInWorker } from "@godscene/shared/utils";
3
+ let scriptFileContentCache = null;
4
+ const getHtmlElementScript = async ()=>{
5
+ const scriptFileToRetrieve = chrome.runtime.getURL("scripts/htmlElement.js");
6
+ if (scriptFileContentCache) return scriptFileContentCache;
7
+ if (ifInBrowser || ifInWorker) {
8
+ const script = await fetch(scriptFileToRetrieve);
9
+ scriptFileContentCache = await script.text();
10
+ return scriptFileContentCache;
11
+ }
12
+ return node_fs.readFileSync(scriptFileToRetrieve, 'utf8');
13
+ };
14
+ let waterFlowScriptFileContentCache = null;
15
+ const injectWaterFlowAnimation = async ()=>{
16
+ const waterFlowScriptFileToRetrieve = chrome.runtime.getURL("scripts/water-flow.js");
17
+ if (waterFlowScriptFileContentCache) return waterFlowScriptFileContentCache;
18
+ if (ifInBrowser || ifInWorker) {
19
+ const script = await fetch(waterFlowScriptFileToRetrieve);
20
+ waterFlowScriptFileContentCache = await script.text();
21
+ return waterFlowScriptFileContentCache;
22
+ }
23
+ return node_fs.readFileSync(waterFlowScriptFileToRetrieve, 'utf8');
24
+ };
25
+ let stopWaterFlowScriptFileContentCache = null;
26
+ const injectStopWaterFlowAnimation = async ()=>{
27
+ const stopWaterFlowScriptFileToRetrieve = chrome.runtime.getURL("scripts/stop-water-flow.js");
28
+ if (stopWaterFlowScriptFileContentCache) return stopWaterFlowScriptFileContentCache;
29
+ if (ifInBrowser || ifInWorker) {
30
+ const script = await fetch(stopWaterFlowScriptFileToRetrieve);
31
+ stopWaterFlowScriptFileContentCache = await script.text();
32
+ return stopWaterFlowScriptFileContentCache;
33
+ }
34
+ return node_fs.readFileSync(stopWaterFlowScriptFileToRetrieve, 'utf8');
35
+ };
36
+ export { getHtmlElementScript, injectStopWaterFlowAnimation, injectWaterFlowAnimation };
@@ -0,0 +1,5 @@
1
+ import { ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED } from "@godscene/shared/common";
2
+ import { ChromeExtensionProxyPageAgent } from "./agent.mjs";
3
+ import page from "./page.mjs";
4
+ import { overrideAIConfig } from "@godscene/shared/env";
5
+ export { page as ChromeExtensionProxyPage, ChromeExtensionProxyPageAgent, ERROR_CODE_NOT_IMPLEMENTED_AS_DESIGNED, overrideAIConfig };