kiro-mobile-bridge 1.0.7 → 1.0.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -24
- package/package.json +1 -1
- package/src/public/index.html +1414 -1628
- package/src/routes/api.js +539 -0
- package/src/server.js +287 -2593
- package/src/services/cdp.js +210 -0
- package/src/services/click.js +533 -0
- package/src/services/message.js +214 -0
- package/src/services/snapshot.js +370 -0
- package/src/utils/constants.js +116 -0
- package/src/utils/hash.js +34 -0
- package/src/utils/network.js +64 -0
- package/src/utils/security.js +160 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chrome DevTools Protocol (CDP) connection service
|
|
3
|
+
*/
|
|
4
|
+
import http from 'http';
|
|
5
|
+
import { WebSocket } from 'ws';
|
|
6
|
+
import { CDP_CALL_TIMEOUT, HTTP_TIMEOUT } from '../utils/constants.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Fetch JSON from a CDP endpoint
|
|
10
|
+
* @param {number} port - The port to fetch from
|
|
11
|
+
* @param {string} path - The path to fetch (default: /json/list)
|
|
12
|
+
* @returns {Promise<any>} - Parsed JSON response
|
|
13
|
+
*/
|
|
14
|
+
export function fetchCDPTargets(port, path = '/json/list') {
|
|
15
|
+
return new Promise((resolve, reject) => {
|
|
16
|
+
const url = `http://127.0.0.1:${port}${path}`;
|
|
17
|
+
|
|
18
|
+
const req = http.get(url, { timeout: HTTP_TIMEOUT }, (res) => {
|
|
19
|
+
let data = '';
|
|
20
|
+
res.on('data', chunk => data += chunk);
|
|
21
|
+
res.on('end', () => {
|
|
22
|
+
try {
|
|
23
|
+
resolve(JSON.parse(data));
|
|
24
|
+
} catch (e) {
|
|
25
|
+
reject(new Error(`Failed to parse JSON from ${url}: ${e.message}`));
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
req.on('error', (err) => reject(new Error(`Failed to fetch ${url}: ${err.message}`)));
|
|
31
|
+
req.on('timeout', () => {
|
|
32
|
+
req.destroy();
|
|
33
|
+
reject(new Error(`Timeout fetching ${url}`));
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @typedef {Object} PendingCall
|
|
40
|
+
* @property {Function} resolve - Promise resolve function
|
|
41
|
+
* @property {Function} reject - Promise reject function
|
|
42
|
+
* @property {NodeJS.Timeout} timeoutId - Timeout handle for cleanup
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @typedef {Object} CDPConnection
|
|
47
|
+
* @property {WebSocket} ws - WebSocket connection
|
|
48
|
+
* @property {Array} contexts - Execution contexts
|
|
49
|
+
* @property {number|null} rootContextId - Root context ID
|
|
50
|
+
* @property {Function} call - Make a CDP call
|
|
51
|
+
* @property {Function} close - Close the connection
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Create a CDP connection to a target
|
|
56
|
+
* @param {string} wsUrl - WebSocket debugger URL
|
|
57
|
+
* @returns {Promise<CDPConnection>} - CDP connection object
|
|
58
|
+
*/
|
|
59
|
+
export function connectToCDP(wsUrl) {
|
|
60
|
+
return new Promise((resolve, reject) => {
|
|
61
|
+
const ws = new WebSocket(wsUrl);
|
|
62
|
+
let idCounter = 1;
|
|
63
|
+
/** @type {Map<number, PendingCall>} */
|
|
64
|
+
const pendingCalls = new Map();
|
|
65
|
+
const contexts = [];
|
|
66
|
+
let rootContextId = null;
|
|
67
|
+
let isConnected = false;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Clear all pending calls and their timeouts
|
|
71
|
+
* @param {Error} error - Error to reject pending calls with
|
|
72
|
+
*/
|
|
73
|
+
function clearAllPendingCalls(error) {
|
|
74
|
+
for (const [id, pending] of pendingCalls) {
|
|
75
|
+
// Clear the timeout to prevent memory leak
|
|
76
|
+
if (pending.timeoutId) {
|
|
77
|
+
clearTimeout(pending.timeoutId);
|
|
78
|
+
}
|
|
79
|
+
pending.reject(error);
|
|
80
|
+
}
|
|
81
|
+
pendingCalls.clear();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
ws.on('message', (rawMsg) => {
|
|
85
|
+
try {
|
|
86
|
+
const msg = JSON.parse(rawMsg.toString());
|
|
87
|
+
|
|
88
|
+
if (msg.method === 'Runtime.executionContextCreated') {
|
|
89
|
+
const ctx = msg.params.context;
|
|
90
|
+
contexts.push(ctx);
|
|
91
|
+
if (rootContextId === null || ctx.auxData?.isDefault) {
|
|
92
|
+
rootContextId = ctx.id;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (msg.method === 'Runtime.executionContextDestroyed') {
|
|
97
|
+
const ctxId = msg.params.executionContextId;
|
|
98
|
+
const idx = contexts.findIndex(c => c.id === ctxId);
|
|
99
|
+
if (idx !== -1) contexts.splice(idx, 1);
|
|
100
|
+
if (rootContextId === ctxId) {
|
|
101
|
+
rootContextId = contexts.length > 0 ? contexts[0].id : null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (msg.method === 'Runtime.executionContextsCleared') {
|
|
106
|
+
contexts.length = 0;
|
|
107
|
+
rootContextId = null;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (msg.id !== undefined && pendingCalls.has(msg.id)) {
|
|
111
|
+
const pending = pendingCalls.get(msg.id);
|
|
112
|
+
|
|
113
|
+
// Clear the timeout since we got a response
|
|
114
|
+
if (pending.timeoutId) {
|
|
115
|
+
clearTimeout(pending.timeoutId);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
pendingCalls.delete(msg.id);
|
|
119
|
+
|
|
120
|
+
if (msg.error) {
|
|
121
|
+
pending.reject(new Error(`CDP Error: ${msg.error.message} (code: ${msg.error.code})`));
|
|
122
|
+
} else {
|
|
123
|
+
pending.resolve(msg.result);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {
|
|
127
|
+
console.error('[CDP] Failed to parse message:', e.message);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
ws.on('open', async () => {
|
|
132
|
+
isConnected = true;
|
|
133
|
+
console.log(`[CDP] Connected to ${wsUrl}`);
|
|
134
|
+
|
|
135
|
+
const cdp = {
|
|
136
|
+
ws,
|
|
137
|
+
contexts,
|
|
138
|
+
get rootContextId() { return rootContextId; },
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Make a CDP call
|
|
142
|
+
* @param {string} method - CDP method name
|
|
143
|
+
* @param {object} params - Method parameters
|
|
144
|
+
* @returns {Promise<any>} - Call result
|
|
145
|
+
*/
|
|
146
|
+
call(method, params = {}) {
|
|
147
|
+
return new Promise((res, rej) => {
|
|
148
|
+
if (!isConnected) {
|
|
149
|
+
rej(new Error('CDP connection is closed'));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const id = idCounter++;
|
|
154
|
+
|
|
155
|
+
// Set up timeout with cleanup
|
|
156
|
+
const timeoutId = setTimeout(() => {
|
|
157
|
+
if (pendingCalls.has(id)) {
|
|
158
|
+
pendingCalls.delete(id);
|
|
159
|
+
rej(new Error(`CDP call timeout: ${method}`));
|
|
160
|
+
}
|
|
161
|
+
}, CDP_CALL_TIMEOUT);
|
|
162
|
+
|
|
163
|
+
// Store pending call with timeout reference for cleanup
|
|
164
|
+
pendingCalls.set(id, { resolve: res, reject: rej, timeoutId });
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
ws.send(JSON.stringify({ id, method, params }));
|
|
168
|
+
} catch (sendError) {
|
|
169
|
+
// Clean up on send failure
|
|
170
|
+
clearTimeout(timeoutId);
|
|
171
|
+
pendingCalls.delete(id);
|
|
172
|
+
rej(new Error(`Failed to send CDP call: ${sendError.message}`));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Close the CDP connection
|
|
179
|
+
*/
|
|
180
|
+
close() {
|
|
181
|
+
isConnected = false;
|
|
182
|
+
clearAllPendingCalls(new Error('CDP connection closed'));
|
|
183
|
+
ws.terminate();
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
await cdp.call('Runtime.enable', {});
|
|
189
|
+
await new Promise(r => setTimeout(r, 300));
|
|
190
|
+
console.log(`[CDP] Runtime enabled, found ${contexts.length} context(s)`);
|
|
191
|
+
resolve(cdp);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
cdp.close();
|
|
194
|
+
reject(err);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
ws.on('error', (err) => {
|
|
199
|
+
console.error(`[CDP] WebSocket error: ${err.message}`);
|
|
200
|
+
isConnected = false;
|
|
201
|
+
reject(err);
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
ws.on('close', () => {
|
|
205
|
+
console.log('[CDP] Connection closed');
|
|
206
|
+
isConnected = false;
|
|
207
|
+
clearAllPendingCalls(new Error('CDP connection closed'));
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
}
|