@btraut/browser-bridge 0.12.0 → 0.13.0
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/CHANGELOG.md +37 -1
- package/README.md +15 -17
- package/dist/api.js +953 -409
- package/dist/api.js.map +4 -4
- package/dist/index.js +600 -400
- package/dist/index.js.map +4 -4
- package/extension/assets/ui.css +13 -89
- package/extension/dist/background.js +318 -26
- package/extension/dist/background.js.map +3 -3
- package/extension/dist/content.js +42 -4
- package/extension/dist/content.js.map +3 -3
- package/extension/dist/options-ui.js +80 -0
- package/extension/dist/options-ui.js.map +2 -2
- package/extension/dist/popup-ui.js +12 -78
- package/extension/dist/popup-ui.js.map +2 -2
- package/extension/manifest.json +14 -4
- package/package.json +1 -1
- package/skills/browser-bridge/SKILL.md +3 -4
- package/skills/browser-bridge/skill.json +1 -1
package/extension/assets/ui.css
CHANGED
|
@@ -68,111 +68,35 @@ body.bb-page.bb-page--popup {
|
|
|
68
68
|
padding: 2px;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
.bb-
|
|
71
|
+
.bb-connection {
|
|
72
72
|
border: 1px solid var(--bb-border-2);
|
|
73
73
|
border-radius: var(--bb-radius-sm);
|
|
74
74
|
background: var(--bb-bg);
|
|
75
75
|
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.06);
|
|
76
76
|
padding: 10px 12px;
|
|
77
77
|
margin: 0 0 10px;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
.bb-health-head {
|
|
81
78
|
display: flex;
|
|
82
79
|
align-items: center;
|
|
83
80
|
justify-content: space-between;
|
|
84
|
-
margin-bottom: 8px;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
.bb-health-label {
|
|
88
|
-
font-size: 12px;
|
|
89
|
-
font-weight: 700;
|
|
90
|
-
letter-spacing: 0.04em;
|
|
91
|
-
color: var(--bb-ink-2);
|
|
92
|
-
text-transform: uppercase;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.bb-health-state {
|
|
96
|
-
border: 1px solid var(--bb-border);
|
|
97
|
-
border-radius: 999px;
|
|
98
|
-
padding: 2px 8px;
|
|
99
|
-
font-size: 11px;
|
|
100
|
-
font-weight: 700;
|
|
101
|
-
text-transform: capitalize;
|
|
102
|
-
color: var(--bb-ink);
|
|
103
|
-
background: rgba(0, 0, 0, 0.04);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
.bb-health-state[data-state='connected'] {
|
|
107
|
-
color: #1f6f3f;
|
|
108
|
-
border-color: rgba(31, 111, 63, 0.35);
|
|
109
|
-
background: rgba(31, 111, 63, 0.12);
|
|
110
81
|
}
|
|
111
82
|
|
|
112
|
-
.bb-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
border-color: rgba(138, 82, 0, 0.35);
|
|
116
|
-
background: rgba(138, 82, 0, 0.12);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
.bb-health-state[data-state='disconnected'] {
|
|
120
|
-
color: #a22b2b;
|
|
121
|
-
border-color: rgba(162, 43, 43, 0.35);
|
|
122
|
-
background: rgba(162, 43, 43, 0.12);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
.bb-health-grid {
|
|
126
|
-
display: grid;
|
|
127
|
-
grid-template-columns: auto 1fr;
|
|
128
|
-
gap: 6px 8px;
|
|
129
|
-
margin: 0;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
.bb-health-grid dt {
|
|
133
|
-
margin: 0;
|
|
134
|
-
font-size: 11px;
|
|
135
|
-
color: var(--bb-ink-2);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
.bb-health-grid dd {
|
|
139
|
-
margin: 0;
|
|
140
|
-
font-size: 12px;
|
|
141
|
-
color: var(--bb-ink);
|
|
142
|
-
overflow-wrap: anywhere;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
.bb-health-error {
|
|
146
|
-
min-height: 14px;
|
|
147
|
-
margin: 8px 0 0;
|
|
148
|
-
font-size: 11px;
|
|
149
|
-
color: #a22b2b;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
.bb-health-actions {
|
|
153
|
-
margin-top: 6px;
|
|
154
|
-
display: flex;
|
|
155
|
-
align-items: center;
|
|
156
|
-
gap: 8px;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.bb-health-copy {
|
|
160
|
-
border: 1px solid var(--bb-border);
|
|
161
|
-
border-radius: 8px;
|
|
162
|
-
background: var(--bb-bg);
|
|
83
|
+
.bb-connection-label {
|
|
84
|
+
font-size: 14px;
|
|
85
|
+
font-weight: 600;
|
|
163
86
|
color: var(--bb-ink);
|
|
164
|
-
padding: 5px 8px;
|
|
165
|
-
font-size: 12px;
|
|
166
|
-
cursor: pointer;
|
|
167
87
|
}
|
|
168
88
|
|
|
169
|
-
.bb-
|
|
170
|
-
|
|
89
|
+
.bb-connection-dot {
|
|
90
|
+
width: 11px;
|
|
91
|
+
height: 11px;
|
|
92
|
+
border-radius: 999px;
|
|
93
|
+
border: 1px solid rgba(162, 43, 43, 0.35);
|
|
94
|
+
background: #c93939;
|
|
171
95
|
}
|
|
172
96
|
|
|
173
|
-
.bb-
|
|
174
|
-
|
|
175
|
-
|
|
97
|
+
.bb-connection-dot[data-connected='true'] {
|
|
98
|
+
border-color: rgba(31, 111, 63, 0.4);
|
|
99
|
+
background: #2b9a52;
|
|
176
100
|
}
|
|
177
101
|
|
|
178
102
|
.bb-popup-head {
|
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
8
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
19
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
20
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
21
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
22
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
23
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
24
|
+
mod
|
|
25
|
+
));
|
|
26
|
+
|
|
27
|
+
// packages/shared/dist/contract-version.js
|
|
28
|
+
var require_contract_version = __commonJS({
|
|
29
|
+
"packages/shared/dist/contract-version.js"(exports) {
|
|
30
|
+
"use strict";
|
|
31
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
+
exports.resolveContractVersionMismatch = exports.DRIVE_WS_PROTOCOL_VERSION = exports.HTTP_CONTRACT_VERSION = exports.HTTP_CONTRACT_VERSION_HEADER = void 0;
|
|
33
|
+
exports.HTTP_CONTRACT_VERSION_HEADER = "x-browser-bridge-contract-version";
|
|
34
|
+
exports.HTTP_CONTRACT_VERSION = "2026-02-17.1";
|
|
35
|
+
exports.DRIVE_WS_PROTOCOL_VERSION = "2026-02-17.1";
|
|
36
|
+
var resolveContractVersionMismatch = (receivedVersion) => {
|
|
37
|
+
if (!receivedVersion || receivedVersion.trim().length === 0) {
|
|
38
|
+
return void 0;
|
|
39
|
+
}
|
|
40
|
+
if (receivedVersion === exports.HTTP_CONTRACT_VERSION) {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
expected: exports.HTTP_CONTRACT_VERSION,
|
|
45
|
+
received: receivedVersion
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
exports.resolveContractVersionMismatch = resolveContractVersionMismatch;
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
// packages/extension/src/background.ts
|
|
53
|
+
var import_contract_version = __toESM(require_contract_version());
|
|
54
|
+
|
|
1
55
|
// packages/extension/src/error-sanitizer.ts
|
|
2
56
|
var TRAILING_PUNCTUATION_RE = /[.,;:!?]+$/;
|
|
3
57
|
var stripTrailingPunctuation = (value) => {
|
|
@@ -97,7 +151,9 @@ var SITE_ALLOWLIST_KEY = "siteAllowlist";
|
|
|
97
151
|
var PERMISSION_PROMPT_WAIT_MS_KEY = "permissionPromptWaitMs";
|
|
98
152
|
var DEFAULT_PERMISSION_PROMPT_WAIT_MS = 3e4;
|
|
99
153
|
var SITE_PERMISSIONS_MODE_KEY = "sitePermissionsMode";
|
|
154
|
+
var DEBUGGER_CAPABILITY_ENABLED_KEY = "debuggerCapabilityEnabled";
|
|
100
155
|
var DEFAULT_SITE_PERMISSIONS_MODE = "granular";
|
|
156
|
+
var DEFAULT_DEBUGGER_CAPABILITY_ENABLED = false;
|
|
101
157
|
var siteKeyFromUrl = (rawUrl) => {
|
|
102
158
|
if (!rawUrl || typeof rawUrl !== "string") {
|
|
103
159
|
return null;
|
|
@@ -199,6 +255,27 @@ var readPermissionPromptWaitMs = async () => {
|
|
|
199
255
|
);
|
|
200
256
|
});
|
|
201
257
|
};
|
|
258
|
+
var readDebuggerCapabilityEnabled = async () => {
|
|
259
|
+
return await new Promise((resolve) => {
|
|
260
|
+
chrome.storage.local.get(
|
|
261
|
+
[DEBUGGER_CAPABILITY_ENABLED_KEY],
|
|
262
|
+
(result) => {
|
|
263
|
+
const raw = result?.[DEBUGGER_CAPABILITY_ENABLED_KEY];
|
|
264
|
+
if (typeof raw === "boolean") {
|
|
265
|
+
resolve(raw);
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
try {
|
|
269
|
+
chrome.storage.local.set({
|
|
270
|
+
[DEBUGGER_CAPABILITY_ENABLED_KEY]: DEFAULT_DEBUGGER_CAPABILITY_ENABLED
|
|
271
|
+
});
|
|
272
|
+
} catch {
|
|
273
|
+
}
|
|
274
|
+
resolve(DEFAULT_DEBUGGER_CAPABILITY_ENABLED);
|
|
275
|
+
}
|
|
276
|
+
);
|
|
277
|
+
});
|
|
278
|
+
};
|
|
202
279
|
var isSiteAllowed = async (siteKey) => {
|
|
203
280
|
const key = normalizeSiteKey(siteKey);
|
|
204
281
|
const allowlist = await readAllowlistRaw();
|
|
@@ -488,6 +565,8 @@ var ConnectionStateTracker = class {
|
|
|
488
565
|
var DEFAULT_CORE_PORT = 3210;
|
|
489
566
|
var CORE_PORT_KEY = "corePort";
|
|
490
567
|
var CORE_WS_PATH = "/drive";
|
|
568
|
+
var CORE_HEALTH_PATH = "/health";
|
|
569
|
+
var CORE_HEALTH_TIMEOUT_MS = 1200;
|
|
491
570
|
var DEBUGGER_PROTOCOL_VERSION = "1.3";
|
|
492
571
|
var DEBUGGER_IDLE_TIMEOUT_KEY = "debuggerIdleTimeoutMs";
|
|
493
572
|
var DEFAULT_DEBUGGER_IDLE_TIMEOUT_MS = 15e3;
|
|
@@ -499,9 +578,62 @@ var HISTORY_POST_NAV_DOM_GRACE_TIMEOUT_MS = 2e3;
|
|
|
499
578
|
var AGENT_TAB_ID_KEY = "agentTabId";
|
|
500
579
|
var AGENT_TAB_GROUP_TITLE = "Browser Bridge";
|
|
501
580
|
var AGENT_TAB_BOOTSTRAP_PATH = "agent-tab.html";
|
|
581
|
+
var AGENT_TAB_FAVICON_ASSET_PATH = "assets/icons/icon-32.png";
|
|
582
|
+
var AGENT_TAB_BRANDING_ACTION = "drive.agent_tab_branding";
|
|
583
|
+
var AGENT_TAB_GROUP_RETRY_DELAYS_MS = [0, 120, 300];
|
|
584
|
+
var AGENT_TAB_BRANDING_TIMEOUT_MS = 1500;
|
|
585
|
+
var BASE_NEGOTIATED_CAPABILITIES = Object.freeze({
|
|
586
|
+
"drive.navigate": true,
|
|
587
|
+
"drive.go_back": true,
|
|
588
|
+
"drive.go_forward": true,
|
|
589
|
+
"drive.click": true,
|
|
590
|
+
"drive.hover": true,
|
|
591
|
+
"drive.select": true,
|
|
592
|
+
"drive.type": true,
|
|
593
|
+
"drive.fill_form": true,
|
|
594
|
+
"drive.drag": true,
|
|
595
|
+
"drive.handle_dialog": true,
|
|
596
|
+
"drive.key": true,
|
|
597
|
+
"drive.key_press": true,
|
|
598
|
+
"drive.scroll": true,
|
|
599
|
+
"drive.screenshot": true,
|
|
600
|
+
"drive.wait_for": true,
|
|
601
|
+
"drive.tab_list": true,
|
|
602
|
+
"drive.tab_activate": true,
|
|
603
|
+
"drive.tab_close": true,
|
|
604
|
+
"drive.ping": true
|
|
605
|
+
});
|
|
606
|
+
var DEBUGGER_CAPABILITY_ACTIONS = [
|
|
607
|
+
"debugger.attach",
|
|
608
|
+
"debugger.detach",
|
|
609
|
+
"debugger.command"
|
|
610
|
+
];
|
|
611
|
+
var buildNegotiatedCapabilities = (debuggerCapabilityEnabled) => {
|
|
612
|
+
const capabilities = {
|
|
613
|
+
...BASE_NEGOTIATED_CAPABILITIES
|
|
614
|
+
};
|
|
615
|
+
for (const action of DEBUGGER_CAPABILITY_ACTIONS) {
|
|
616
|
+
capabilities[action] = debuggerCapabilityEnabled;
|
|
617
|
+
}
|
|
618
|
+
return capabilities;
|
|
619
|
+
};
|
|
620
|
+
var debuggerCapabilityDisabledError = () => {
|
|
621
|
+
return {
|
|
622
|
+
code: "ATTACH_DENIED",
|
|
623
|
+
message: "Debugger capability is disabled. Enable debugger-based inspect in extension options and retry.",
|
|
624
|
+
retryable: false,
|
|
625
|
+
details: {
|
|
626
|
+
reason: "debugger_capability_disabled",
|
|
627
|
+
next_step: "Open Browser Bridge extension options, enable debugger-based inspect, then retry."
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
};
|
|
502
631
|
var getAgentTabBootstrapUrl = () => {
|
|
503
632
|
return typeof chrome.runtime?.getURL === "function" ? chrome.runtime.getURL(AGENT_TAB_BOOTSTRAP_PATH) : AGENT_TAB_BOOTSTRAP_PATH;
|
|
504
633
|
};
|
|
634
|
+
var getAgentTabFaviconUrl = () => {
|
|
635
|
+
return typeof chrome.runtime?.getURL === "function" ? chrome.runtime.getURL(AGENT_TAB_FAVICON_ASSET_PATH) : AGENT_TAB_FAVICON_ASSET_PATH;
|
|
636
|
+
};
|
|
505
637
|
var nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
506
638
|
var makeEventId = /* @__PURE__ */ (() => {
|
|
507
639
|
let counter = 0;
|
|
@@ -870,23 +1002,38 @@ var ensureAgentTabGroup = async (tabId, windowId) => {
|
|
|
870
1002
|
if (!chrome.tabGroups || typeof chrome.tabGroups.update !== "function") {
|
|
871
1003
|
return;
|
|
872
1004
|
}
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
1005
|
+
let lastError;
|
|
1006
|
+
for (const retryDelayMs of AGENT_TAB_GROUP_RETRY_DELAYS_MS) {
|
|
1007
|
+
if (retryDelayMs > 0) {
|
|
1008
|
+
await delayMs(retryDelayMs);
|
|
1009
|
+
}
|
|
1010
|
+
try {
|
|
1011
|
+
const groupId = await wrapChromeCallback(
|
|
1012
|
+
(callback) => chrome.tabs.group(
|
|
1013
|
+
{ tabIds: tabId, createProperties: { windowId } },
|
|
1014
|
+
callback
|
|
1015
|
+
)
|
|
1016
|
+
);
|
|
1017
|
+
await wrapChromeVoid(
|
|
1018
|
+
(callback) => chrome.tabGroups.update(
|
|
1019
|
+
groupId,
|
|
1020
|
+
{ title: AGENT_TAB_GROUP_TITLE },
|
|
1021
|
+
() => callback()
|
|
1022
|
+
)
|
|
1023
|
+
);
|
|
1024
|
+
return;
|
|
1025
|
+
} catch (error) {
|
|
1026
|
+
lastError = error;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
console.debug("Failed to create/update agent tab group.", lastError);
|
|
1030
|
+
};
|
|
1031
|
+
var ensureAgentTabGroupForTab = async (tabId, tab) => {
|
|
1032
|
+
const windowId = tab.windowId;
|
|
1033
|
+
if (typeof windowId !== "number") {
|
|
1034
|
+
return;
|
|
889
1035
|
}
|
|
1036
|
+
await ensureAgentTabGroup(tabId, windowId);
|
|
890
1037
|
};
|
|
891
1038
|
var createAgentWindow = async () => {
|
|
892
1039
|
const created = await wrapChromeCallback(
|
|
@@ -941,6 +1088,8 @@ var getOrCreateAgentTabId = async () => {
|
|
|
941
1088
|
if (typeof url === "string" && isRestrictedUrl(url)) {
|
|
942
1089
|
throw new Error(`Agent tab points at restricted URL: ${url}`);
|
|
943
1090
|
}
|
|
1091
|
+
await ensureAgentTabGroupForTab(agentTabId, tab);
|
|
1092
|
+
void refreshAgentTabBranding(agentTabId);
|
|
944
1093
|
return agentTabId;
|
|
945
1094
|
} catch {
|
|
946
1095
|
clearAgentTarget();
|
|
@@ -957,6 +1106,8 @@ var getOrCreateAgentTabId = async () => {
|
|
|
957
1106
|
agentTabId = stored;
|
|
958
1107
|
ensureLastActiveAt(stored);
|
|
959
1108
|
markTabActive(stored);
|
|
1109
|
+
await ensureAgentTabGroupForTab(stored, tab);
|
|
1110
|
+
void refreshAgentTabBranding(stored);
|
|
960
1111
|
return stored;
|
|
961
1112
|
} catch {
|
|
962
1113
|
await writeAgentTabId(null);
|
|
@@ -1062,6 +1213,17 @@ var sendToTab = async (tabId, action, params, options) => {
|
|
|
1062
1213
|
}
|
|
1063
1214
|
};
|
|
1064
1215
|
};
|
|
1216
|
+
var refreshAgentTabBranding = async (tabId) => {
|
|
1217
|
+
const result = await sendToTab(
|
|
1218
|
+
tabId,
|
|
1219
|
+
AGENT_TAB_BRANDING_ACTION,
|
|
1220
|
+
{ favicon_url: getAgentTabFaviconUrl() },
|
|
1221
|
+
{ timeoutMs: AGENT_TAB_BRANDING_TIMEOUT_MS }
|
|
1222
|
+
);
|
|
1223
|
+
if (!result.ok) {
|
|
1224
|
+
return;
|
|
1225
|
+
}
|
|
1226
|
+
};
|
|
1065
1227
|
var waitForHistoryNavigationSignal = async (tabId, timeoutMs) => {
|
|
1066
1228
|
return await new Promise((resolve, reject) => {
|
|
1067
1229
|
let timeout;
|
|
@@ -1153,6 +1315,7 @@ var getWsEndpoint = async () => {
|
|
|
1153
1315
|
url: `ws://${endpoint.host}:${endpoint.port}${CORE_WS_PATH}`
|
|
1154
1316
|
};
|
|
1155
1317
|
};
|
|
1318
|
+
var getHealthEndpoint = (endpoint) => `http://${endpoint.host}:${endpoint.port}${CORE_HEALTH_PATH}`;
|
|
1156
1319
|
var DriveSocket = class {
|
|
1157
1320
|
constructor() {
|
|
1158
1321
|
this.socket = null;
|
|
@@ -1209,6 +1372,16 @@ var DriveSocket = class {
|
|
|
1209
1372
|
async connect() {
|
|
1210
1373
|
const { endpoint, url } = await getWsEndpoint();
|
|
1211
1374
|
this.connection.setEndpoint(endpoint);
|
|
1375
|
+
const health = await this.checkCoreHealth(endpoint);
|
|
1376
|
+
if (!health.ok) {
|
|
1377
|
+
this.connection.markDisconnected();
|
|
1378
|
+
this.recordConnectionFailure(
|
|
1379
|
+
"core unavailable",
|
|
1380
|
+
new Error(health.detail)
|
|
1381
|
+
);
|
|
1382
|
+
this.scheduleReconnect();
|
|
1383
|
+
return;
|
|
1384
|
+
}
|
|
1212
1385
|
try {
|
|
1213
1386
|
const socket2 = new WebSocket(url);
|
|
1214
1387
|
this.socket = socket2;
|
|
@@ -1241,9 +1414,47 @@ var DriveSocket = class {
|
|
|
1241
1414
|
this.scheduleReconnect();
|
|
1242
1415
|
}
|
|
1243
1416
|
}
|
|
1417
|
+
async checkCoreHealth(endpoint) {
|
|
1418
|
+
const controller = new AbortController();
|
|
1419
|
+
const timeoutId = self.setTimeout(() => {
|
|
1420
|
+
controller.abort();
|
|
1421
|
+
}, CORE_HEALTH_TIMEOUT_MS);
|
|
1422
|
+
try {
|
|
1423
|
+
const response = await fetch(getHealthEndpoint(endpoint), {
|
|
1424
|
+
method: "GET",
|
|
1425
|
+
cache: "no-store",
|
|
1426
|
+
signal: controller.signal
|
|
1427
|
+
});
|
|
1428
|
+
if (!response.ok) {
|
|
1429
|
+
return {
|
|
1430
|
+
ok: false,
|
|
1431
|
+
detail: `health returned HTTP ${response.status}`
|
|
1432
|
+
};
|
|
1433
|
+
}
|
|
1434
|
+
return { ok: true, detail: "ok" };
|
|
1435
|
+
} catch (error) {
|
|
1436
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
1437
|
+
return {
|
|
1438
|
+
ok: false,
|
|
1439
|
+
detail: `health timed out after ${CORE_HEALTH_TIMEOUT_MS}ms`
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
return {
|
|
1443
|
+
ok: false,
|
|
1444
|
+
detail: error instanceof Error && error.message.length > 0 ? error.message : "health check failed"
|
|
1445
|
+
};
|
|
1446
|
+
} finally {
|
|
1447
|
+
clearTimeout(timeoutId);
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1244
1450
|
getConnectionStatus() {
|
|
1245
1451
|
return this.connection.getStatus();
|
|
1246
1452
|
}
|
|
1453
|
+
refreshCapabilities() {
|
|
1454
|
+
void this.sendHello().catch((error) => {
|
|
1455
|
+
console.error("DriveSocket refreshCapabilities failed:", error);
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1247
1458
|
handleSocketUnavailable(socket2, reason) {
|
|
1248
1459
|
if (this.socket !== socket2) {
|
|
1249
1460
|
return;
|
|
@@ -1273,6 +1484,7 @@ var DriveSocket = class {
|
|
|
1273
1484
|
async sendHello() {
|
|
1274
1485
|
const manifest = chrome.runtime.getManifest();
|
|
1275
1486
|
const endpoint = await readCoreEndpointConfig();
|
|
1487
|
+
const debuggerCapabilityEnabled = await readDebuggerCapabilityEnabled();
|
|
1276
1488
|
let tabs = [];
|
|
1277
1489
|
try {
|
|
1278
1490
|
tabs = await queryTabs();
|
|
@@ -1282,6 +1494,8 @@ var DriveSocket = class {
|
|
|
1282
1494
|
}
|
|
1283
1495
|
const params = {
|
|
1284
1496
|
version: manifest.version,
|
|
1497
|
+
protocol_version: import_contract_version.DRIVE_WS_PROTOCOL_VERSION,
|
|
1498
|
+
capabilities: buildNegotiatedCapabilities(debuggerCapabilityEnabled),
|
|
1285
1499
|
core_host: endpoint.host,
|
|
1286
1500
|
core_port: endpoint.port,
|
|
1287
1501
|
core_port_source: endpoint.portSource,
|
|
@@ -1356,6 +1570,19 @@ var DriveSocket = class {
|
|
|
1356
1570
|
});
|
|
1357
1571
|
}
|
|
1358
1572
|
}
|
|
1573
|
+
async refreshDebuggerCapabilityState() {
|
|
1574
|
+
const enabled = await readDebuggerCapabilityEnabled();
|
|
1575
|
+
if (!enabled) {
|
|
1576
|
+
await this.detachAllDebuggerSessions();
|
|
1577
|
+
}
|
|
1578
|
+
this.refreshCapabilities();
|
|
1579
|
+
}
|
|
1580
|
+
async handleDebuggerCapabilityChange(enabled) {
|
|
1581
|
+
if (!enabled) {
|
|
1582
|
+
await this.detachAllDebuggerSessions();
|
|
1583
|
+
}
|
|
1584
|
+
this.refreshCapabilities();
|
|
1585
|
+
}
|
|
1359
1586
|
async handleRequest(message) {
|
|
1360
1587
|
let driveMessage = null;
|
|
1361
1588
|
let gatedSiteKey = null;
|
|
@@ -1394,6 +1621,15 @@ var DriveSocket = class {
|
|
|
1394
1621
|
return;
|
|
1395
1622
|
}
|
|
1396
1623
|
if (message.action.startsWith("debugger.")) {
|
|
1624
|
+
if (!await readDebuggerCapabilityEnabled()) {
|
|
1625
|
+
this.sendMessage({
|
|
1626
|
+
id: message.id,
|
|
1627
|
+
action: message.action,
|
|
1628
|
+
status: "error",
|
|
1629
|
+
error: sanitizeDriveErrorInfo(debuggerCapabilityDisabledError())
|
|
1630
|
+
});
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1397
1633
|
await this.handleDebuggerRequest(message);
|
|
1398
1634
|
return;
|
|
1399
1635
|
}
|
|
@@ -1405,8 +1641,6 @@ var DriveSocket = class {
|
|
|
1405
1641
|
"drive.navigate",
|
|
1406
1642
|
"drive.go_back",
|
|
1407
1643
|
"drive.go_forward",
|
|
1408
|
-
"drive.back",
|
|
1409
|
-
"drive.forward",
|
|
1410
1644
|
"drive.click",
|
|
1411
1645
|
"drive.hover",
|
|
1412
1646
|
"drive.select",
|
|
@@ -1599,13 +1833,14 @@ var DriveSocket = class {
|
|
|
1599
1833
|
return;
|
|
1600
1834
|
}
|
|
1601
1835
|
}
|
|
1836
|
+
if (tabId === agentTabId) {
|
|
1837
|
+
void refreshAgentTabBranding(tabId);
|
|
1838
|
+
}
|
|
1602
1839
|
respondOk({ ok: true });
|
|
1603
1840
|
return;
|
|
1604
1841
|
}
|
|
1605
1842
|
case "drive.go_back":
|
|
1606
|
-
case "drive.
|
|
1607
|
-
case "drive.go_forward":
|
|
1608
|
-
case "drive.forward": {
|
|
1843
|
+
case "drive.go_forward": {
|
|
1609
1844
|
const params = message.params ?? {};
|
|
1610
1845
|
let tabId = params.tab_id;
|
|
1611
1846
|
if (tabId !== void 0 && typeof tabId !== "number") {
|
|
@@ -1651,6 +1886,9 @@ var DriveSocket = class {
|
|
|
1651
1886
|
return;
|
|
1652
1887
|
}
|
|
1653
1888
|
}
|
|
1889
|
+
if (tabId === agentTabId) {
|
|
1890
|
+
void refreshAgentTabBranding(tabId);
|
|
1891
|
+
}
|
|
1654
1892
|
respondOk({ ok: true });
|
|
1655
1893
|
return;
|
|
1656
1894
|
}
|
|
@@ -3036,6 +3274,9 @@ var DriveSocket = class {
|
|
|
3036
3274
|
}
|
|
3037
3275
|
}
|
|
3038
3276
|
async handleDebuggerEvent(source, method, params) {
|
|
3277
|
+
if (!await readDebuggerCapabilityEnabled()) {
|
|
3278
|
+
return;
|
|
3279
|
+
}
|
|
3039
3280
|
const tabId = source.tabId;
|
|
3040
3281
|
if (typeof tabId !== "number") {
|
|
3041
3282
|
return;
|
|
@@ -3054,6 +3295,9 @@ var DriveSocket = class {
|
|
|
3054
3295
|
return;
|
|
3055
3296
|
}
|
|
3056
3297
|
this.clearDebuggerSession(tabId);
|
|
3298
|
+
if (!await readDebuggerCapabilityEnabled()) {
|
|
3299
|
+
return;
|
|
3300
|
+
}
|
|
3057
3301
|
this.sendDebuggerEvent({
|
|
3058
3302
|
tab_id: tabId,
|
|
3059
3303
|
method: "Debugger.detached",
|
|
@@ -3192,6 +3436,15 @@ var DriveSocket = class {
|
|
|
3192
3436
|
}
|
|
3193
3437
|
return null;
|
|
3194
3438
|
}
|
|
3439
|
+
async detachAllDebuggerSessions() {
|
|
3440
|
+
const tabIds = Array.from(this.debuggerSessions.keys());
|
|
3441
|
+
for (const tabId of tabIds) {
|
|
3442
|
+
const error = await this.detachDebugger(tabId);
|
|
3443
|
+
if (error) {
|
|
3444
|
+
console.warn("DriveSocket detachDebugger failed:", tabId, error);
|
|
3445
|
+
}
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3195
3448
|
async sendDebuggerCommand(tabId, method, params, timeoutMs) {
|
|
3196
3449
|
return await new Promise((resolve, reject) => {
|
|
3197
3450
|
let finished = false;
|
|
@@ -3322,14 +3575,53 @@ chrome.debugger.onDetach.addListener(
|
|
|
3322
3575
|
);
|
|
3323
3576
|
chrome.runtime.onMessage.addListener(
|
|
3324
3577
|
(message, _sender, sendResponse) => {
|
|
3325
|
-
if (!message || typeof message !== "object"
|
|
3578
|
+
if (!message || typeof message !== "object") {
|
|
3326
3579
|
return void 0;
|
|
3327
3580
|
}
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3581
|
+
const action = message.action;
|
|
3582
|
+
if (action === "drive.connection_status") {
|
|
3583
|
+
sendResponse({
|
|
3584
|
+
ok: true,
|
|
3585
|
+
result: socket.getConnectionStatus()
|
|
3586
|
+
});
|
|
3587
|
+
return true;
|
|
3588
|
+
}
|
|
3589
|
+
if (action === "drive.refresh_capabilities") {
|
|
3590
|
+
void socket.refreshDebuggerCapabilityState().then(() => {
|
|
3591
|
+
sendResponse({ ok: true, result: { refreshed: true } });
|
|
3592
|
+
}).catch((error) => {
|
|
3593
|
+
const message2 = error instanceof Error ? error.message : "Failed to refresh capabilities.";
|
|
3594
|
+
sendResponse({ ok: false, error: { message: message2 } });
|
|
3595
|
+
});
|
|
3596
|
+
return true;
|
|
3597
|
+
}
|
|
3598
|
+
return void 0;
|
|
3599
|
+
}
|
|
3600
|
+
);
|
|
3601
|
+
chrome.storage.onChanged.addListener(
|
|
3602
|
+
(changes, areaName) => {
|
|
3603
|
+
if (areaName !== "local") {
|
|
3604
|
+
return;
|
|
3605
|
+
}
|
|
3606
|
+
const debuggerChange = changes[DEBUGGER_CAPABILITY_ENABLED_KEY];
|
|
3607
|
+
if (!debuggerChange) {
|
|
3608
|
+
return;
|
|
3609
|
+
}
|
|
3610
|
+
if (typeof debuggerChange.newValue === "boolean") {
|
|
3611
|
+
void socket.handleDebuggerCapabilityChange(debuggerChange.newValue).catch((error) => {
|
|
3612
|
+
console.error(
|
|
3613
|
+
"DriveSocket handleDebuggerCapabilityChange failed:",
|
|
3614
|
+
error
|
|
3615
|
+
);
|
|
3616
|
+
});
|
|
3617
|
+
return;
|
|
3618
|
+
}
|
|
3619
|
+
void socket.refreshDebuggerCapabilityState().catch((error) => {
|
|
3620
|
+
console.error(
|
|
3621
|
+
"DriveSocket refreshDebuggerCapabilityState failed:",
|
|
3622
|
+
error
|
|
3623
|
+
);
|
|
3331
3624
|
});
|
|
3332
|
-
return true;
|
|
3333
3625
|
}
|
|
3334
3626
|
);
|
|
3335
3627
|
socket.start();
|