opendevbrowser 0.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 +241 -0
- package/dist/chunk-R5VUZEUU.js +128 -0
- package/dist/chunk-R5VUZEUU.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +802 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +3615 -0
- package/dist/index.js.map +1 -0
- package/dist/opendevbrowser.d.ts +5 -0
- package/dist/opendevbrowser.js +3615 -0
- package/dist/opendevbrowser.js.map +1 -0
- package/extension/dist/background.js +32 -0
- package/extension/dist/popup.js +150 -0
- package/extension/dist/popup.jsx +150 -0
- package/extension/dist/relay-settings.js +4 -0
- package/extension/dist/services/CDPRouter.js +176 -0
- package/extension/dist/services/ConnectionManager.js +301 -0
- package/extension/dist/services/RelayClient.js +73 -0
- package/extension/dist/services/TabManager.js +18 -0
- package/extension/dist/types.js +1 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon32.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/manifest.json +34 -0
- package/extension/popup.html +108 -0
- package/package.json +71 -0
- package/skills/AGENTS.md +80 -0
- package/skills/data-extraction/SKILL.md +136 -0
- package/skills/form-testing/SKILL.md +113 -0
- package/skills/login-automation/SKILL.md +98 -0
- package/skills/opendevbrowser-best-practices/SKILL.md +81 -0
- package/skills/opendevbrowser-continuity-ledger/SKILL.md +45 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { DEFAULT_AUTO_PAIR, DEFAULT_PAIRING_ENABLED, DEFAULT_PAIRING_TOKEN, DEFAULT_RELAY_PORT } from "./relay-settings.js";
|
|
2
|
+
const statusEl = document.getElementById("status");
|
|
3
|
+
const statusIndicator = document.getElementById("statusIndicator");
|
|
4
|
+
const toggleButton = document.getElementById("toggle");
|
|
5
|
+
const relayPortInput = document.getElementById("relayPort");
|
|
6
|
+
const pairingTokenInput = document.getElementById("pairingToken");
|
|
7
|
+
const pairingEnabledInput = document.getElementById("pairingEnabled");
|
|
8
|
+
const autoPairInput = document.getElementById("autoPair");
|
|
9
|
+
if (!statusEl || !statusIndicator || !toggleButton || !relayPortInput || !pairingTokenInput || !pairingEnabledInput || !autoPairInput) {
|
|
10
|
+
throw new Error("Popup DOM missing required elements");
|
|
11
|
+
}
|
|
12
|
+
const setStatus = (status) => {
|
|
13
|
+
const isConnected = status === "connected";
|
|
14
|
+
statusEl.textContent = isConnected ? "Connected" : "Disconnected";
|
|
15
|
+
toggleButton.textContent = isConnected ? "Disconnect" : "Connect";
|
|
16
|
+
if (isConnected) {
|
|
17
|
+
statusIndicator.classList.add("connected");
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
statusIndicator.classList.remove("connected");
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const sendMessage = (message) => {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
chrome.runtime.sendMessage(message, (response) => {
|
|
26
|
+
resolve(response);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const refreshStatus = async () => {
|
|
31
|
+
const response = await sendMessage({ type: "status" });
|
|
32
|
+
setStatus(response.status);
|
|
33
|
+
};
|
|
34
|
+
const fetchTokenFromPlugin = async (port) => {
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(`http://127.0.0.1:${port}/pair`, {
|
|
37
|
+
method: "GET",
|
|
38
|
+
headers: { "Accept": "application/json" }
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
return typeof data.token === "string" ? data.token : null;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const loadSettings = async () => {
|
|
51
|
+
const data = await new Promise((resolve) => {
|
|
52
|
+
chrome.storage.local.get(["pairingToken", "pairingEnabled", "relayPort", "autoPair"], (items) => {
|
|
53
|
+
resolve(items);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
const autoPair = typeof data.autoPair === "boolean" ? data.autoPair : DEFAULT_AUTO_PAIR;
|
|
57
|
+
const pairingEnabled = typeof data.pairingEnabled === "boolean"
|
|
58
|
+
? data.pairingEnabled
|
|
59
|
+
: DEFAULT_PAIRING_ENABLED;
|
|
60
|
+
const rawToken = typeof data.pairingToken === "string" ? data.pairingToken.trim() : "";
|
|
61
|
+
const tokenValue = pairingEnabled ? (rawToken || DEFAULT_PAIRING_TOKEN || "") : rawToken;
|
|
62
|
+
const portValue = typeof data.relayPort === "number" ? data.relayPort : DEFAULT_RELAY_PORT;
|
|
63
|
+
autoPairInput.checked = autoPair;
|
|
64
|
+
pairingEnabledInput.checked = pairingEnabled;
|
|
65
|
+
pairingTokenInput.disabled = !pairingEnabled || autoPair;
|
|
66
|
+
pairingTokenInput.value = tokenValue;
|
|
67
|
+
relayPortInput.value = Number.isInteger(portValue) ? String(portValue) : String(DEFAULT_RELAY_PORT);
|
|
68
|
+
const updates = {};
|
|
69
|
+
if (typeof data.autoPair !== "boolean") {
|
|
70
|
+
updates.autoPair = autoPair;
|
|
71
|
+
}
|
|
72
|
+
if (typeof data.pairingEnabled !== "boolean") {
|
|
73
|
+
updates.pairingEnabled = pairingEnabled;
|
|
74
|
+
}
|
|
75
|
+
if (Object.keys(updates).length > 0) {
|
|
76
|
+
chrome.storage.local.set(updates);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const toggle = async () => {
|
|
80
|
+
const isConnected = statusEl.textContent === "Connected";
|
|
81
|
+
if (!isConnected && autoPairInput.checked && pairingEnabledInput.checked) {
|
|
82
|
+
const port = parseInt(relayPortInput.value, 10) || DEFAULT_RELAY_PORT;
|
|
83
|
+
const fetchedToken = await fetchTokenFromPlugin(port);
|
|
84
|
+
if (fetchedToken) {
|
|
85
|
+
pairingTokenInput.value = fetchedToken;
|
|
86
|
+
chrome.storage.local.set({ pairingToken: fetchedToken });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
statusEl.textContent = "Failed to fetch token from plugin";
|
|
90
|
+
setTimeout(() => refreshStatus(), 2000);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const response = await sendMessage({
|
|
95
|
+
type: isConnected ? "disconnect" : "connect"
|
|
96
|
+
});
|
|
97
|
+
setStatus(response.status);
|
|
98
|
+
};
|
|
99
|
+
toggleButton.addEventListener("click", () => {
|
|
100
|
+
toggle().catch(() => {
|
|
101
|
+
setStatus("disconnected");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
autoPairInput.addEventListener("change", () => {
|
|
105
|
+
const enabled = autoPairInput.checked;
|
|
106
|
+
pairingTokenInput.disabled = !pairingEnabledInput.checked || enabled;
|
|
107
|
+
chrome.storage.local.set({ autoPair: enabled });
|
|
108
|
+
if (enabled) {
|
|
109
|
+
pairingTokenInput.placeholder = "Will be fetched automatically";
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
pairingTokenInput.placeholder = "Enter token or enable Auto-Pair";
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
pairingTokenInput.addEventListener("input", () => {
|
|
116
|
+
const value = pairingTokenInput.value.trim();
|
|
117
|
+
chrome.storage.local.set({ pairingToken: value });
|
|
118
|
+
});
|
|
119
|
+
pairingEnabledInput.addEventListener("change", () => {
|
|
120
|
+
const enabled = pairingEnabledInput.checked;
|
|
121
|
+
pairingTokenInput.disabled = !enabled || autoPairInput.checked;
|
|
122
|
+
const updates = { pairingEnabled: enabled };
|
|
123
|
+
if (enabled && !autoPairInput.checked) {
|
|
124
|
+
const tokenValue = pairingTokenInput.value.trim() || DEFAULT_PAIRING_TOKEN || "";
|
|
125
|
+
pairingTokenInput.value = tokenValue;
|
|
126
|
+
updates.pairingToken = tokenValue;
|
|
127
|
+
}
|
|
128
|
+
chrome.storage.local.set(updates);
|
|
129
|
+
});
|
|
130
|
+
relayPortInput.addEventListener("input", () => {
|
|
131
|
+
const raw = relayPortInput.value.trim();
|
|
132
|
+
if (!raw) {
|
|
133
|
+
chrome.storage.local.set({ relayPort: DEFAULT_RELAY_PORT });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const parsed = Number(raw);
|
|
137
|
+
if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) {
|
|
138
|
+
chrome.storage.local.set({ relayPort: parsed });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
refreshStatus().catch(() => {
|
|
142
|
+
setStatus("disconnected");
|
|
143
|
+
});
|
|
144
|
+
loadSettings().catch(() => {
|
|
145
|
+
autoPairInput.checked = DEFAULT_AUTO_PAIR;
|
|
146
|
+
pairingEnabledInput.checked = DEFAULT_PAIRING_ENABLED;
|
|
147
|
+
pairingTokenInput.disabled = !DEFAULT_PAIRING_ENABLED;
|
|
148
|
+
pairingTokenInput.value = DEFAULT_PAIRING_TOKEN || "";
|
|
149
|
+
relayPortInput.value = String(DEFAULT_RELAY_PORT);
|
|
150
|
+
});
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { DEFAULT_AUTO_PAIR, DEFAULT_PAIRING_ENABLED, DEFAULT_PAIRING_TOKEN, DEFAULT_RELAY_PORT } from "./relay-settings";
|
|
2
|
+
const statusEl = document.getElementById("status");
|
|
3
|
+
const statusIndicator = document.getElementById("statusIndicator");
|
|
4
|
+
const toggleButton = document.getElementById("toggle");
|
|
5
|
+
const relayPortInput = document.getElementById("relayPort");
|
|
6
|
+
const pairingTokenInput = document.getElementById("pairingToken");
|
|
7
|
+
const pairingEnabledInput = document.getElementById("pairingEnabled");
|
|
8
|
+
const autoPairInput = document.getElementById("autoPair");
|
|
9
|
+
if (!statusEl || !statusIndicator || !toggleButton || !relayPortInput || !pairingTokenInput || !pairingEnabledInput || !autoPairInput) {
|
|
10
|
+
throw new Error("Popup DOM missing required elements");
|
|
11
|
+
}
|
|
12
|
+
const setStatus = (status) => {
|
|
13
|
+
const isConnected = status === "connected";
|
|
14
|
+
statusEl.textContent = isConnected ? "Connected" : "Disconnected";
|
|
15
|
+
toggleButton.textContent = isConnected ? "Disconnect" : "Connect";
|
|
16
|
+
if (isConnected) {
|
|
17
|
+
statusIndicator.classList.add("connected");
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
statusIndicator.classList.remove("connected");
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const sendMessage = (message) => {
|
|
24
|
+
return new Promise((resolve) => {
|
|
25
|
+
chrome.runtime.sendMessage(message, (response) => {
|
|
26
|
+
resolve(response);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
const refreshStatus = async () => {
|
|
31
|
+
const response = await sendMessage({ type: "status" });
|
|
32
|
+
setStatus(response.status);
|
|
33
|
+
};
|
|
34
|
+
const fetchTokenFromPlugin = async (port) => {
|
|
35
|
+
try {
|
|
36
|
+
const response = await fetch(`http://127.0.0.1:${port}/pair`, {
|
|
37
|
+
method: "GET",
|
|
38
|
+
headers: { "Accept": "application/json" }
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const data = await response.json();
|
|
44
|
+
return typeof data.token === "string" ? data.token : null;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const loadSettings = async () => {
|
|
51
|
+
const data = await new Promise((resolve) => {
|
|
52
|
+
chrome.storage.local.get(["pairingToken", "pairingEnabled", "relayPort", "autoPair"], (items) => {
|
|
53
|
+
resolve(items);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
const autoPair = typeof data.autoPair === "boolean" ? data.autoPair : DEFAULT_AUTO_PAIR;
|
|
57
|
+
const pairingEnabled = typeof data.pairingEnabled === "boolean"
|
|
58
|
+
? data.pairingEnabled
|
|
59
|
+
: DEFAULT_PAIRING_ENABLED;
|
|
60
|
+
const rawToken = typeof data.pairingToken === "string" ? data.pairingToken.trim() : "";
|
|
61
|
+
const tokenValue = pairingEnabled ? (rawToken || DEFAULT_PAIRING_TOKEN || "") : rawToken;
|
|
62
|
+
const portValue = typeof data.relayPort === "number" ? data.relayPort : DEFAULT_RELAY_PORT;
|
|
63
|
+
autoPairInput.checked = autoPair;
|
|
64
|
+
pairingEnabledInput.checked = pairingEnabled;
|
|
65
|
+
pairingTokenInput.disabled = !pairingEnabled || autoPair;
|
|
66
|
+
pairingTokenInput.value = tokenValue;
|
|
67
|
+
relayPortInput.value = Number.isInteger(portValue) ? String(portValue) : String(DEFAULT_RELAY_PORT);
|
|
68
|
+
const updates = {};
|
|
69
|
+
if (typeof data.autoPair !== "boolean") {
|
|
70
|
+
updates.autoPair = autoPair;
|
|
71
|
+
}
|
|
72
|
+
if (typeof data.pairingEnabled !== "boolean") {
|
|
73
|
+
updates.pairingEnabled = pairingEnabled;
|
|
74
|
+
}
|
|
75
|
+
if (Object.keys(updates).length > 0) {
|
|
76
|
+
chrome.storage.local.set(updates);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
const toggle = async () => {
|
|
80
|
+
const isConnected = statusEl.textContent === "Connected";
|
|
81
|
+
if (!isConnected && autoPairInput.checked && pairingEnabledInput.checked) {
|
|
82
|
+
const port = parseInt(relayPortInput.value, 10) || DEFAULT_RELAY_PORT;
|
|
83
|
+
const fetchedToken = await fetchTokenFromPlugin(port);
|
|
84
|
+
if (fetchedToken) {
|
|
85
|
+
pairingTokenInput.value = fetchedToken;
|
|
86
|
+
chrome.storage.local.set({ pairingToken: fetchedToken });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
statusEl.textContent = "Failed to fetch token from plugin";
|
|
90
|
+
setTimeout(() => refreshStatus(), 2000);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const response = await sendMessage({
|
|
95
|
+
type: isConnected ? "disconnect" : "connect"
|
|
96
|
+
});
|
|
97
|
+
setStatus(response.status);
|
|
98
|
+
};
|
|
99
|
+
toggleButton.addEventListener("click", () => {
|
|
100
|
+
toggle().catch(() => {
|
|
101
|
+
setStatus("disconnected");
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
autoPairInput.addEventListener("change", () => {
|
|
105
|
+
const enabled = autoPairInput.checked;
|
|
106
|
+
pairingTokenInput.disabled = !pairingEnabledInput.checked || enabled;
|
|
107
|
+
chrome.storage.local.set({ autoPair: enabled });
|
|
108
|
+
if (enabled) {
|
|
109
|
+
pairingTokenInput.placeholder = "Will be fetched automatically";
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
pairingTokenInput.placeholder = "Enter token or enable Auto-Pair";
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
pairingTokenInput.addEventListener("input", () => {
|
|
116
|
+
const value = pairingTokenInput.value.trim();
|
|
117
|
+
chrome.storage.local.set({ pairingToken: value });
|
|
118
|
+
});
|
|
119
|
+
pairingEnabledInput.addEventListener("change", () => {
|
|
120
|
+
const enabled = pairingEnabledInput.checked;
|
|
121
|
+
pairingTokenInput.disabled = !enabled || autoPairInput.checked;
|
|
122
|
+
const updates = { pairingEnabled: enabled };
|
|
123
|
+
if (enabled && !autoPairInput.checked) {
|
|
124
|
+
const tokenValue = pairingTokenInput.value.trim() || DEFAULT_PAIRING_TOKEN || "";
|
|
125
|
+
pairingTokenInput.value = tokenValue;
|
|
126
|
+
updates.pairingToken = tokenValue;
|
|
127
|
+
}
|
|
128
|
+
chrome.storage.local.set(updates);
|
|
129
|
+
});
|
|
130
|
+
relayPortInput.addEventListener("input", () => {
|
|
131
|
+
const raw = relayPortInput.value.trim();
|
|
132
|
+
if (!raw) {
|
|
133
|
+
chrome.storage.local.set({ relayPort: DEFAULT_RELAY_PORT });
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const parsed = Number(raw);
|
|
137
|
+
if (Number.isInteger(parsed) && parsed > 0 && parsed <= 65535) {
|
|
138
|
+
chrome.storage.local.set({ relayPort: parsed });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
refreshStatus().catch(() => {
|
|
142
|
+
setStatus("disconnected");
|
|
143
|
+
});
|
|
144
|
+
loadSettings().catch(() => {
|
|
145
|
+
autoPairInput.checked = DEFAULT_AUTO_PAIR;
|
|
146
|
+
pairingEnabledInput.checked = DEFAULT_PAIRING_ENABLED;
|
|
147
|
+
pairingTokenInput.disabled = !DEFAULT_PAIRING_ENABLED;
|
|
148
|
+
pairingTokenInput.value = DEFAULT_PAIRING_TOKEN || "";
|
|
149
|
+
relayPortInput.value = String(DEFAULT_RELAY_PORT);
|
|
150
|
+
});
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
export class CDPRouter {
|
|
2
|
+
debuggee = null;
|
|
3
|
+
callbacks = null;
|
|
4
|
+
handleEventBound = (source, method, params) => {
|
|
5
|
+
this.handleEvent(source, method, params);
|
|
6
|
+
};
|
|
7
|
+
handleDetachBound = (source) => {
|
|
8
|
+
this.handleDetach(source);
|
|
9
|
+
};
|
|
10
|
+
setCallbacks(callbacks) {
|
|
11
|
+
this.callbacks = callbacks;
|
|
12
|
+
}
|
|
13
|
+
async attach(tabId) {
|
|
14
|
+
if (this.debuggee?.tabId === tabId) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (this.debuggee) {
|
|
18
|
+
await this.detach();
|
|
19
|
+
}
|
|
20
|
+
this.debuggee = { tabId };
|
|
21
|
+
try {
|
|
22
|
+
await this.runDebuggerAction((done) => {
|
|
23
|
+
chrome.debugger.attach(this.debuggee, "1.3", done);
|
|
24
|
+
});
|
|
25
|
+
chrome.debugger.onEvent.addListener(this.handleEventBound);
|
|
26
|
+
chrome.debugger.onDetach.addListener(this.handleDetachBound);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
this.debuggee = null;
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async detach() {
|
|
34
|
+
if (!this.debuggee)
|
|
35
|
+
return;
|
|
36
|
+
const current = this.debuggee;
|
|
37
|
+
this.debuggee = null;
|
|
38
|
+
chrome.debugger.onEvent.removeListener(this.handleEventBound);
|
|
39
|
+
chrome.debugger.onDetach.removeListener(this.handleDetachBound);
|
|
40
|
+
await this.runDebuggerAction((done) => {
|
|
41
|
+
chrome.debugger.detach(current, done);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
getAttachedTabId() {
|
|
45
|
+
return this.debuggee?.tabId ?? null;
|
|
46
|
+
}
|
|
47
|
+
async handleCommand(command) {
|
|
48
|
+
if (!this.debuggee || !this.callbacks) {
|
|
49
|
+
this.callbacks?.onResponse({ id: command.id, error: { message: "No tab attached" } });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
const { method, params, sessionId } = command.params;
|
|
53
|
+
if (sessionId && method !== "Target.sendMessageToTarget") {
|
|
54
|
+
const message = JSON.stringify({ id: command.id, method, params });
|
|
55
|
+
try {
|
|
56
|
+
await this.sendCommand("Target.sendMessageToTarget", { sessionId, message });
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
this.callbacks.onResponse({ id: command.id, error: { message: getErrorMessage(error) } });
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const result = await this.sendCommand(method, params ?? {});
|
|
65
|
+
this.callbacks.onResponse({ id: command.id, result });
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
this.callbacks.onResponse({ id: command.id, error: { message: getErrorMessage(error) } });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async sendCommand(method, params) {
|
|
72
|
+
if (!this.debuggee) {
|
|
73
|
+
throw new Error("No tab attached");
|
|
74
|
+
}
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
chrome.debugger.sendCommand(this.debuggee, method, params, (result) => {
|
|
77
|
+
const lastError = chrome.runtime.lastError;
|
|
78
|
+
if (lastError) {
|
|
79
|
+
reject(new Error(lastError.message));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
resolve(result);
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async runDebuggerAction(action) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
action(() => {
|
|
89
|
+
const lastError = chrome.runtime.lastError;
|
|
90
|
+
if (lastError) {
|
|
91
|
+
reject(new Error(lastError.message));
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
resolve();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
handleEvent(source, method, params) {
|
|
99
|
+
if (!this.matchesDebuggee(source) || !this.callbacks) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (method === "Target.receivedMessageFromTarget" && params && isRecord(params)) {
|
|
103
|
+
const nested = parseNestedMessage(params.message);
|
|
104
|
+
const sessionId = typeof params.sessionId === "string" ? params.sessionId : undefined;
|
|
105
|
+
if (nested && (typeof nested.id === "string" || typeof nested.id === "number")) {
|
|
106
|
+
const error = normalizeError(nested.error);
|
|
107
|
+
this.callbacks.onResponse({
|
|
108
|
+
id: nested.id,
|
|
109
|
+
result: nested.result,
|
|
110
|
+
error,
|
|
111
|
+
sessionId
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (nested && typeof nested.method === "string") {
|
|
116
|
+
this.callbacks.onEvent({
|
|
117
|
+
method: "forwardCDPEvent",
|
|
118
|
+
params: {
|
|
119
|
+
method: nested.method,
|
|
120
|
+
params: nested.params,
|
|
121
|
+
sessionId
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
this.callbacks.onEvent({
|
|
128
|
+
method: "forwardCDPEvent",
|
|
129
|
+
params: {
|
|
130
|
+
method,
|
|
131
|
+
params
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
handleDetach(source) {
|
|
136
|
+
if (!this.matchesDebuggee(source) || !this.callbacks) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
this.callbacks.onDetach();
|
|
140
|
+
}
|
|
141
|
+
matchesDebuggee(source) {
|
|
142
|
+
if (!this.debuggee)
|
|
143
|
+
return false;
|
|
144
|
+
return source.tabId === this.debuggee.tabId;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
const parseNestedMessage = (value) => {
|
|
148
|
+
if (typeof value !== "string")
|
|
149
|
+
return null;
|
|
150
|
+
try {
|
|
151
|
+
const parsed = JSON.parse(value);
|
|
152
|
+
return parsed;
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
const isRecord = (value) => {
|
|
159
|
+
return typeof value === "object" && value !== null;
|
|
160
|
+
};
|
|
161
|
+
const getErrorMessage = (error) => {
|
|
162
|
+
if (error instanceof Error) {
|
|
163
|
+
return error.message;
|
|
164
|
+
}
|
|
165
|
+
return "Unknown error";
|
|
166
|
+
};
|
|
167
|
+
const normalizeError = (value) => {
|
|
168
|
+
if (!isRecord(value)) {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
const message = value.message;
|
|
172
|
+
if (typeof message !== "string") {
|
|
173
|
+
return undefined;
|
|
174
|
+
}
|
|
175
|
+
return { message };
|
|
176
|
+
};
|