@hanzo/browser-extension 1.7.6 → 1.9.33

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 (144) hide show
  1. package/LICENSE +9 -18
  2. package/dist/browser-extension/content-script.js +1642 -123
  3. package/images/icon128.png +0 -0
  4. package/images/icon16.png +0 -0
  5. package/images/icon48.png +0 -0
  6. package/manifest.json +7 -2
  7. package/package.json +28 -10
  8. package/src/answer/AnswerEngine.tsx +553 -0
  9. package/src/chat-widget.tsx +96 -0
  10. package/src/gui-primitives.tsx +116 -0
  11. package/src/icon128.png +0 -0
  12. package/src/icon16.png +0 -0
  13. package/src/icon32.png +0 -0
  14. package/src/icon48.png +0 -0
  15. package/src/manifest-firefox.json +32 -13
  16. package/src/manifest.json +35 -11
  17. package/src/newtab.css +277 -0
  18. package/src/newtab.html +13 -0
  19. package/src/package.json +1 -1
  20. package/src/popup.css +352 -110
  21. package/src/popup.html +186 -47
  22. package/src/popup.js +196 -0
  23. package/src/sidebar.css +772 -156
  24. package/src/sidebar.html +268 -85
  25. package/src/sidebar.js +885 -0
  26. package/.github/workflows/docs.yml +0 -44
  27. package/.github/workflows/release.yml +0 -55
  28. package/README.md +0 -126
  29. package/dist/browser-extension/README.md +0 -43
  30. package/dist/browser-extension/ai-worker.js +0 -30
  31. package/dist/browser-extension/background-firefox.js +0 -875
  32. package/dist/browser-extension/background.js +0 -2732
  33. package/dist/browser-extension/browser-control.js +0 -683
  34. package/dist/browser-extension/cdp-bridge-server.js +0 -444
  35. package/dist/browser-extension/chrome/ai-worker.js +0 -571
  36. package/dist/browser-extension/chrome/background.js +0 -2732
  37. package/dist/browser-extension/chrome/callback.html +0 -17
  38. package/dist/browser-extension/chrome/content-script.js +0 -1270
  39. package/dist/browser-extension/chrome/icon128.png +0 -0
  40. package/dist/browser-extension/chrome/icon16.png +0 -0
  41. package/dist/browser-extension/chrome/icon32.png +0 -0
  42. package/dist/browser-extension/chrome/icon48.png +0 -0
  43. package/dist/browser-extension/chrome/manifest.json +0 -57
  44. package/dist/browser-extension/chrome/popup.css +0 -519
  45. package/dist/browser-extension/chrome/popup.html +0 -242
  46. package/dist/browser-extension/chrome/popup.js +0 -278
  47. package/dist/browser-extension/chrome/sidebar.css +0 -1176
  48. package/dist/browser-extension/chrome/sidebar.html +0 -280
  49. package/dist/browser-extension/chrome/sidebar.js +0 -779
  50. package/dist/browser-extension/cli.js +0 -233
  51. package/dist/browser-extension/firefox/ai-worker.js +0 -571
  52. package/dist/browser-extension/firefox/background.js +0 -875
  53. package/dist/browser-extension/firefox/callback.html +0 -17
  54. package/dist/browser-extension/firefox/content-script.js +0 -1270
  55. package/dist/browser-extension/firefox/icon128.png +0 -0
  56. package/dist/browser-extension/firefox/icon16.png +0 -0
  57. package/dist/browser-extension/firefox/icon32.png +0 -0
  58. package/dist/browser-extension/firefox/icon48.png +0 -0
  59. package/dist/browser-extension/firefox/manifest.json +0 -66
  60. package/dist/browser-extension/firefox/popup.css +0 -519
  61. package/dist/browser-extension/firefox/popup.html +0 -242
  62. package/dist/browser-extension/firefox/popup.js +0 -278
  63. package/dist/browser-extension/firefox/sidebar.css +0 -1176
  64. package/dist/browser-extension/firefox/sidebar.html +0 -280
  65. package/dist/browser-extension/firefox/sidebar.js +0 -779
  66. package/dist/browser-extension/hanzo-browser-extension-firefox-1.1.0.zip +0 -0
  67. package/dist/browser-extension/icon128.png +0 -0
  68. package/dist/browser-extension/icon16.png +0 -0
  69. package/dist/browser-extension/icon32.png +0 -0
  70. package/dist/browser-extension/icon48.png +0 -0
  71. package/dist/browser-extension/manifest.json +0 -57
  72. package/dist/browser-extension/package.json +0 -41
  73. package/dist/browser-extension/popup.css +0 -468
  74. package/dist/browser-extension/popup.html +0 -191
  75. package/dist/browser-extension/popup.js +0 -278
  76. package/dist/browser-extension/safari/Info.plist +0 -21
  77. package/dist/browser-extension/safari/ai-worker.js +0 -571
  78. package/dist/browser-extension/safari/background.js +0 -2732
  79. package/dist/browser-extension/safari/callback.html +0 -17
  80. package/dist/browser-extension/safari/content-script.js +0 -1270
  81. package/dist/browser-extension/safari/icon128.png +0 -0
  82. package/dist/browser-extension/safari/icon16.png +0 -0
  83. package/dist/browser-extension/safari/icon32.png +0 -0
  84. package/dist/browser-extension/safari/icon48.png +0 -0
  85. package/dist/browser-extension/safari/popup.css +0 -519
  86. package/dist/browser-extension/safari/popup.html +0 -242
  87. package/dist/browser-extension/safari/popup.js +0 -278
  88. package/dist/browser-extension/safari/sidebar.css +0 -1176
  89. package/dist/browser-extension/safari/sidebar.html +0 -280
  90. package/dist/browser-extension/safari/sidebar.js +0 -779
  91. package/dist/browser-extension/sidebar.css +0 -718
  92. package/dist/browser-extension/sidebar.html +0 -184
  93. package/dist/browser-extension/sidebar.js +0 -779
  94. package/dist/browser-extension/webgpu-ai.js +0 -495
  95. package/dist/cdp-bridge-server.js +0 -411
  96. package/dist/hanzo-ai-chrome-1.7.2.zip +0 -0
  97. package/dist/hanzo-ai-chrome-1.7.3.zip +0 -0
  98. package/dist/hanzo-ai-firefox-1.7.2.zip +0 -0
  99. package/dist/hanzo-ai-firefox-1.7.3.zip +0 -0
  100. package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.pbxproj +0 -1047
  101. package/dist/safari/Hanzo AI/Hanzo AI.xcodeproj/project.xcworkspace/contents.xcworkspacedata +0 -7
  102. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AccentColor.colorset/Contents.json +0 -11
  103. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/Contents.json +0 -98
  104. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@1x.png +0 -0
  105. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-128@2x.png +0 -0
  106. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@1x.png +0 -0
  107. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-16@2x.png +0 -0
  108. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@1x.png +0 -0
  109. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-256@2x.png +0 -0
  110. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@1x.png +0 -0
  111. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-32@2x.png +0 -0
  112. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@1x.png +0 -0
  113. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/mac-icon-512@2x.png +0 -0
  114. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/AppIcon.appiconset/universal-icon-1024@1x.png +0 -0
  115. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/Contents.json +0 -6
  116. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/Contents.json +0 -21
  117. package/dist/safari/Hanzo AI/Shared (App)/Assets.xcassets/LargeIcon.imageset/icon128.png +0 -0
  118. package/dist/safari/Hanzo AI/Shared (App)/Resources/Base.lproj/Main.html +0 -20
  119. package/dist/safari/Hanzo AI/Shared (App)/Resources/Icon.png +0 -0
  120. package/dist/safari/Hanzo AI/Shared (App)/Resources/Script.js +0 -24
  121. package/dist/safari/Hanzo AI/Shared (App)/Resources/Style.css +0 -61
  122. package/dist/safari/Hanzo AI/Shared (App)/ViewController.swift +0 -81
  123. package/dist/safari/Hanzo AI/Shared (Extension)/SafariWebExtensionHandler.swift +0 -42
  124. package/dist/safari/Hanzo AI/iOS (App)/AppDelegate.swift +0 -24
  125. package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/LaunchScreen.storyboard +0 -36
  126. package/dist/safari/Hanzo AI/iOS (App)/Base.lproj/Main.storyboard +0 -38
  127. package/dist/safari/Hanzo AI/iOS (App)/Info.plist +0 -27
  128. package/dist/safari/Hanzo AI/iOS (App)/SceneDelegate.swift +0 -18
  129. package/dist/safari/Hanzo AI/iOS (Extension)/Info.plist +0 -13
  130. package/dist/safari/Hanzo AI/macOS (App)/AppDelegate.swift +0 -21
  131. package/dist/safari/Hanzo AI/macOS (App)/Base.lproj/Main.storyboard +0 -124
  132. package/dist/safari/Hanzo AI/macOS (App)/Info.plist +0 -8
  133. package/dist/safari/Hanzo AI/macOS (Extension)/Info.plist +0 -13
  134. package/docs/_config.yml +0 -13
  135. package/docs/_layouts/default.html +0 -435
  136. package/docs/index.md +0 -54
  137. package/hanzo-ai-chrome-v1.7.6.zip +0 -0
  138. package/hanzo-ai-firefox-v1.7.6.zip +0 -0
  139. package/install-extension.sh +0 -75
  140. package/test/claude-integration.test.ts +0 -251
  141. package/test/server-frameworks-edge-cases.test.ts +0 -145
  142. package/test/server-frameworks.test.ts +0 -395
  143. package/test-cdp-connection.js +0 -140
  144. package/tsconfig.json +0 -19
@@ -1,683 +0,0 @@
1
- // src/browser-control.ts
2
- var BrowserControl = class {
3
- constructor() {
4
- this.tabFS = /* @__PURE__ */ new Map();
5
- this.aiWorkers = /* @__PURE__ */ new Map();
6
- this.agents = /* @__PURE__ */ new Map();
7
- this.initializeTabFileSystem();
8
- this.setupMessageHandlers();
9
- }
10
- initializeTabFileSystem() {
11
- if (typeof chrome === "undefined" || !chrome.tabs) return;
12
- chrome.tabs.query({}, (tabs) => {
13
- tabs.forEach((tab, index) => {
14
- if (tab.id && tab.url) {
15
- const path = `/tabs/${index}/${this.sanitizePath(tab.title || "untitled")}`;
16
- this.tabFS.set(path, {
17
- path,
18
- tabId: tab.id,
19
- url: tab.url,
20
- title: tab.title || "Untitled"
21
- });
22
- }
23
- });
24
- });
25
- chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
26
- if (changeInfo.status === "complete") {
27
- this.updateTabFS(tab);
28
- }
29
- });
30
- chrome.tabs.onRemoved.addListener((tabId) => {
31
- for (const [path, entry] of this.tabFS) {
32
- if (entry.tabId === tabId) {
33
- this.tabFS.delete(path);
34
- break;
35
- }
36
- }
37
- });
38
- }
39
- sanitizePath(title) {
40
- return title.replace(/[^a-zA-Z0-9-_]/g, "_").toLowerCase().substring(0, 60);
41
- }
42
- updateTabFS(tab) {
43
- if (!tab.id || !tab.url) return;
44
- const existingEntry = Array.from(this.tabFS.values()).find(
45
- (entry) => entry.tabId === tab.id
46
- );
47
- const path = existingEntry?.path || `/tabs/${this.tabFS.size}/${this.sanitizePath(tab.title || "untitled")}`;
48
- this.tabFS.set(path, {
49
- path,
50
- tabId: tab.id,
51
- url: tab.url,
52
- title: tab.title || "Untitled"
53
- });
54
- }
55
- // ===========================================================================
56
- // FUSE-like Tab Filesystem
57
- // ===========================================================================
58
- async readTab(path) {
59
- const entry = this.tabFS.get(path);
60
- if (!entry) throw new Error(`Tab not found: ${path}`);
61
- return new Promise((resolve, reject) => {
62
- chrome.tabs.sendMessage(entry.tabId, { action: "getContent" }, (response) => {
63
- if (chrome.runtime.lastError) {
64
- reject(new Error(chrome.runtime.lastError.message));
65
- } else {
66
- resolve(response?.content || "");
67
- }
68
- });
69
- });
70
- }
71
- async writeTab(path, content) {
72
- const entry = this.tabFS.get(path);
73
- if (!entry) throw new Error(`Tab not found: ${path}`);
74
- return new Promise((resolve, reject) => {
75
- chrome.tabs.sendMessage(entry.tabId, { action: "setContent", content }, (response) => {
76
- if (chrome.runtime.lastError) {
77
- reject(new Error(chrome.runtime.lastError.message));
78
- } else {
79
- resolve();
80
- }
81
- });
82
- });
83
- }
84
- async listTabs() {
85
- return Array.from(this.tabFS.keys());
86
- }
87
- // ===========================================================================
88
- // Native Browser Actions
89
- // ===========================================================================
90
- async click(tabId, selector) {
91
- return this.executeScript(tabId, `
92
- (() => {
93
- const el = document.querySelector(${JSON.stringify(selector)});
94
- if (!el) return false;
95
- el.scrollIntoView({ behavior: 'smooth', block: 'center' });
96
- el.click();
97
- return true;
98
- })()
99
- `);
100
- }
101
- async clickAtPoint(tabId, x, y) {
102
- return this.executeScript(tabId, `
103
- (() => {
104
- const el = document.elementFromPoint(${x}, ${y});
105
- if (!el) return false;
106
- el.dispatchEvent(new MouseEvent('click', { bubbles: true, clientX: ${x}, clientY: ${y} }));
107
- return true;
108
- })()
109
- `);
110
- }
111
- async doubleClick(tabId, selector) {
112
- return this.executeScript(tabId, `
113
- (() => {
114
- const el = document.querySelector(${JSON.stringify(selector)});
115
- if (!el) return false;
116
- el.scrollIntoView({ behavior: 'smooth', block: 'center' });
117
- el.dispatchEvent(new MouseEvent('dblclick', { bubbles: true }));
118
- return true;
119
- })()
120
- `);
121
- }
122
- async type(tabId, selector, text) {
123
- return this.executeScript(tabId, `
124
- (() => {
125
- const el = document.querySelector(${JSON.stringify(selector)});
126
- if (!el) return false;
127
- el.focus();
128
- for (const char of ${JSON.stringify(text)}) {
129
- el.dispatchEvent(new KeyboardEvent('keydown', { key: char, bubbles: true }));
130
- el.dispatchEvent(new KeyboardEvent('keypress', { key: char, bubbles: true }));
131
- if ('value' in el) el.value += char;
132
- el.dispatchEvent(new InputEvent('input', { data: char, inputType: 'insertText', bubbles: true }));
133
- el.dispatchEvent(new KeyboardEvent('keyup', { key: char, bubbles: true }));
134
- }
135
- el.dispatchEvent(new Event('change', { bubbles: true }));
136
- return true;
137
- })()
138
- `);
139
- }
140
- async fill(tabId, selector, value) {
141
- return this.executeScript(tabId, `
142
- (() => {
143
- const el = document.querySelector(${JSON.stringify(selector)});
144
- if (!el) return false;
145
- el.focus();
146
- const nativeSetter = Object.getOwnPropertyDescriptor(
147
- Object.getPrototypeOf(el), 'value'
148
- )?.set || Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set;
149
- if (nativeSetter) {
150
- nativeSetter.call(el, ${JSON.stringify(value)});
151
- } else {
152
- el.value = ${JSON.stringify(value)};
153
- }
154
- el.dispatchEvent(new Event('input', { bubbles: true }));
155
- el.dispatchEvent(new Event('change', { bubbles: true }));
156
- return true;
157
- })()
158
- `);
159
- }
160
- async select(tabId, selector, value) {
161
- return this.executeScript(tabId, `
162
- (() => {
163
- const el = document.querySelector(${JSON.stringify(selector)});
164
- if (!el || el.tagName !== 'SELECT') return false;
165
- el.value = ${JSON.stringify(value)};
166
- el.dispatchEvent(new Event('change', { bubbles: true }));
167
- return true;
168
- })()
169
- `);
170
- }
171
- async check(tabId, selector, checked) {
172
- return this.executeScript(tabId, `
173
- (() => {
174
- const el = document.querySelector(${JSON.stringify(selector)});
175
- if (!el) return false;
176
- if (el.checked !== ${checked}) el.click();
177
- return true;
178
- })()
179
- `);
180
- }
181
- async hover(tabId, selector) {
182
- return this.executeScript(tabId, `
183
- (() => {
184
- const el = document.querySelector(${JSON.stringify(selector)});
185
- if (!el) return false;
186
- el.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
187
- el.dispatchEvent(new MouseEvent('mouseover', { bubbles: true }));
188
- return true;
189
- })()
190
- `);
191
- }
192
- async focus(tabId, selector) {
193
- return this.executeScript(tabId, `
194
- (() => {
195
- const el = document.querySelector(${JSON.stringify(selector)});
196
- if (!el) return false;
197
- el.focus();
198
- return true;
199
- })()
200
- `);
201
- }
202
- async blur(tabId, selector) {
203
- return this.executeScript(tabId, `
204
- (() => {
205
- const el = document.querySelector(${JSON.stringify(selector)});
206
- if (!el) return false;
207
- el.blur();
208
- return true;
209
- })()
210
- `);
211
- }
212
- async scroll(tabId, x, y, selector) {
213
- if (selector) {
214
- return this.executeScript(tabId, `
215
- (() => {
216
- const el = document.querySelector(${JSON.stringify(selector)});
217
- if (!el) return false;
218
- el.scrollBy({ left: ${x}, top: ${y}, behavior: 'smooth' });
219
- return true;
220
- })()
221
- `);
222
- }
223
- return this.executeScript(tabId, `
224
- (() => { window.scrollBy({ left: ${x}, top: ${y}, behavior: 'smooth' }); return true; })()
225
- `);
226
- }
227
- async scrollIntoView(tabId, selector) {
228
- return this.executeScript(tabId, `
229
- (() => {
230
- const el = document.querySelector(${JSON.stringify(selector)});
231
- if (!el) return false;
232
- el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
233
- return true;
234
- })()
235
- `);
236
- }
237
- async pressKey(tabId, key, modifiers = {}) {
238
- return this.executeScript(tabId, `
239
- (() => {
240
- const opts = {
241
- key: ${JSON.stringify(key)},
242
- code: ${JSON.stringify(key)},
243
- bubbles: true,
244
- ctrlKey: ${!!modifiers.ctrl},
245
- shiftKey: ${!!modifiers.shift},
246
- altKey: ${!!modifiers.alt},
247
- metaKey: ${!!modifiers.meta},
248
- };
249
- document.activeElement.dispatchEvent(new KeyboardEvent('keydown', opts));
250
- document.activeElement.dispatchEvent(new KeyboardEvent('keypress', opts));
251
- document.activeElement.dispatchEvent(new KeyboardEvent('keyup', opts));
252
- return true;
253
- })()
254
- `);
255
- }
256
- async drag(tabId, fromSelector, toSelector) {
257
- return this.executeScript(tabId, `
258
- (() => {
259
- const from = document.querySelector(${JSON.stringify(fromSelector)});
260
- const to = document.querySelector(${JSON.stringify(toSelector)});
261
- if (!from || !to) return false;
262
- const fromRect = from.getBoundingClientRect();
263
- const toRect = to.getBoundingClientRect();
264
- const fromX = fromRect.left + fromRect.width / 2;
265
- const fromY = fromRect.top + fromRect.height / 2;
266
- const toX = toRect.left + toRect.width / 2;
267
- const toY = toRect.top + toRect.height / 2;
268
- from.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, clientX: fromX, clientY: fromY }));
269
- from.dispatchEvent(new MouseEvent('mousemove', { bubbles: true, clientX: toX, clientY: toY }));
270
- to.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, clientX: toX, clientY: toY }));
271
- to.dispatchEvent(new DragEvent('drop', { bubbles: true }));
272
- return true;
273
- })()
274
- `);
275
- }
276
- async waitForSelector(tabId, selector, timeoutMs = 1e4) {
277
- return this.executeScript(tabId, `
278
- new Promise((resolve) => {
279
- const el = document.querySelector(${JSON.stringify(selector)});
280
- if (el) { resolve(true); return; }
281
- const observer = new MutationObserver(() => {
282
- if (document.querySelector(${JSON.stringify(selector)})) {
283
- observer.disconnect();
284
- resolve(true);
285
- }
286
- });
287
- observer.observe(document.body, { childList: true, subtree: true });
288
- setTimeout(() => { observer.disconnect(); resolve(false); }, ${timeoutMs});
289
- })
290
- `);
291
- }
292
- async waitForNavigation(tabId, timeoutMs = 3e4) {
293
- return new Promise((resolve) => {
294
- const timer = setTimeout(() => {
295
- chrome.tabs.onUpdated.removeListener(listener);
296
- resolve(false);
297
- }, timeoutMs);
298
- const listener = (updatedTabId, changeInfo) => {
299
- if (updatedTabId === tabId && changeInfo.status === "complete") {
300
- clearTimeout(timer);
301
- chrome.tabs.onUpdated.removeListener(listener);
302
- resolve(true);
303
- }
304
- };
305
- chrome.tabs.onUpdated.addListener(listener);
306
- });
307
- }
308
- async evaluate(tabId, expression) {
309
- return this.executeScript(tabId, expression);
310
- }
311
- async getElementInfo(tabId, selector) {
312
- return this.executeScript(tabId, `
313
- (() => {
314
- const el = document.querySelector(${JSON.stringify(selector)});
315
- if (!el) return null;
316
- const rect = el.getBoundingClientRect();
317
- const styles = window.getComputedStyle(el);
318
- return {
319
- tagName: el.tagName.toLowerCase(),
320
- id: el.id,
321
- className: el.className,
322
- textContent: el.textContent?.substring(0, 500),
323
- innerText: el.innerText?.substring(0, 500),
324
- value: el.value,
325
- href: el.href,
326
- src: el.src,
327
- type: el.type,
328
- checked: el.checked,
329
- disabled: el.disabled,
330
- visible: styles.display !== 'none' && styles.visibility !== 'hidden' && rect.width > 0 && rect.height > 0,
331
- rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
332
- attributes: Object.fromEntries(Array.from(el.attributes).map(a => [a.name, a.value])),
333
- };
334
- })()
335
- `);
336
- }
337
- async getPageInfo(tabId) {
338
- return this.executeScript(tabId, `
339
- (() => ({
340
- url: location.href,
341
- title: document.title,
342
- readyState: document.readyState,
343
- scrollX: window.scrollX,
344
- scrollY: window.scrollY,
345
- innerWidth: window.innerWidth,
346
- innerHeight: window.innerHeight,
347
- documentWidth: document.documentElement.scrollWidth,
348
- documentHeight: document.documentElement.scrollHeight,
349
- forms: document.forms.length,
350
- links: document.links.length,
351
- images: document.images.length,
352
- }))()
353
- `);
354
- }
355
- async querySelectorAll(tabId, selector) {
356
- return this.executeScript(tabId, `
357
- (() => Array.from(document.querySelectorAll(${JSON.stringify(selector)})).slice(0, 100).map((el, i) => ({
358
- index: i,
359
- tagName: el.tagName.toLowerCase(),
360
- id: el.id,
361
- className: el.className,
362
- textContent: el.textContent?.substring(0, 200),
363
- visible: el.offsetWidth > 0 && el.offsetHeight > 0,
364
- })))()
365
- `);
366
- }
367
- // ===========================================================================
368
- // Navigation
369
- // ===========================================================================
370
- async navigateTo(tabId, url) {
371
- chrome.tabs.update(tabId, { url });
372
- }
373
- async reload(tabId) {
374
- chrome.tabs.reload(tabId);
375
- }
376
- async goBack(tabId) {
377
- chrome.tabs.goBack(tabId);
378
- }
379
- async goForward(tabId) {
380
- chrome.tabs.goForward(tabId);
381
- }
382
- async createTab(url, active = true) {
383
- return new Promise((resolve) => {
384
- chrome.tabs.create({ url, active }, (tab) => {
385
- resolve(tab.id);
386
- });
387
- });
388
- }
389
- async closeTab(tabId) {
390
- chrome.tabs.remove(tabId);
391
- }
392
- // ===========================================================================
393
- // Screenshots
394
- // ===========================================================================
395
- async captureScreenshot(tabId, options) {
396
- return new Promise((resolve, reject) => {
397
- const format = options?.format || "jpeg";
398
- const quality = options?.quality || (format === "jpeg" ? 80 : void 0);
399
- const captureOpts = { format };
400
- if (quality !== void 0) captureOpts.quality = quality;
401
- chrome.tabs.captureVisibleTab(captureOpts, (dataUrl) => {
402
- if (chrome.runtime.lastError) {
403
- reject(new Error(chrome.runtime.lastError.message));
404
- } else {
405
- resolve(dataUrl);
406
- }
407
- });
408
- });
409
- }
410
- async captureFullPage(tabId) {
411
- const pageInfo = await this.getPageInfo(tabId);
412
- return this.captureScreenshot(tabId, { format: "png" });
413
- }
414
- // ===========================================================================
415
- // AI Worker Management
416
- // ===========================================================================
417
- async launchAIWorker(tabId, modelName) {
418
- const worker = new Worker(new URL("./ai-worker.js", import.meta.url), {
419
- type: "module"
420
- });
421
- const agentId = `agent-${Date.now().toString(36)}-${Math.random().toString(36).substring(2, 8)}`;
422
- worker.postMessage({ type: "init", payload: { tabId, modelName } });
423
- worker.onmessage = (event) => {
424
- this.handleAIWorkerMessage(tabId, agentId, event.data);
425
- };
426
- worker.onerror = (event) => {
427
- console.error(`[Hanzo] AI worker error for tab ${tabId}:`, event.message);
428
- const agent = this.agents.get(agentId);
429
- if (agent) agent.status = "error";
430
- };
431
- this.aiWorkers.set(tabId, worker);
432
- this.agents.set(agentId, {
433
- id: agentId,
434
- name: modelName,
435
- tabId,
436
- status: "running",
437
- worker
438
- });
439
- return agentId;
440
- }
441
- stopAgent(agentId) {
442
- const agent = this.agents.get(agentId);
443
- if (!agent) return false;
444
- agent.worker.terminate();
445
- agent.status = "stopped";
446
- this.aiWorkers.delete(agent.tabId);
447
- this.agents.delete(agentId);
448
- return true;
449
- }
450
- getAgents() {
451
- return Array.from(this.agents.values()).map((a) => ({
452
- id: a.id,
453
- name: a.name,
454
- tabId: a.tabId,
455
- status: a.status
456
- }));
457
- }
458
- handleAIWorkerMessage(tabId, agentId, data) {
459
- switch (data.type) {
460
- case "click":
461
- this.click(tabId, data.selector);
462
- break;
463
- case "type":
464
- this.type(tabId, data.selector, data.text);
465
- break;
466
- case "fill":
467
- this.fill(tabId, data.selector, data.value);
468
- break;
469
- case "navigate":
470
- this.navigateTo(tabId, data.url);
471
- break;
472
- case "scroll":
473
- this.scroll(tabId, data.x || 0, data.y || 0, data.selector);
474
- break;
475
- case "hover":
476
- this.hover(tabId, data.selector);
477
- break;
478
- case "select":
479
- this.select(tabId, data.selector, data.value);
480
- break;
481
- case "pressKey":
482
- this.pressKey(tabId, data.key, data.modifiers);
483
- break;
484
- case "waitForSelector":
485
- this.waitForSelector(tabId, data.selector, data.timeout).then((found) => {
486
- const worker = this.aiWorkers.get(tabId);
487
- worker?.postMessage({ type: "waitResult", found });
488
- });
489
- break;
490
- case "evaluate":
491
- this.evaluate(tabId, data.expression).then((result) => {
492
- const worker = this.aiWorkers.get(tabId);
493
- worker?.postMessage({ type: "evalResult", result });
494
- });
495
- break;
496
- case "screenshot":
497
- this.captureScreenshot(tabId).then((screenshot) => {
498
- const worker = this.aiWorkers.get(tabId);
499
- worker?.postMessage({ type: "screenshot", data: screenshot });
500
- });
501
- break;
502
- case "getElementInfo":
503
- this.getElementInfo(tabId, data.selector).then((info) => {
504
- const worker = this.aiWorkers.get(tabId);
505
- worker?.postMessage({ type: "elementInfo", info });
506
- });
507
- break;
508
- case "getPageInfo":
509
- this.getPageInfo(tabId).then((info) => {
510
- const worker = this.aiWorkers.get(tabId);
511
- worker?.postMessage({ type: "pageInfo", info });
512
- });
513
- break;
514
- case "querySelectorAll":
515
- this.querySelectorAll(tabId, data.selector).then((elements) => {
516
- const worker = this.aiWorkers.get(tabId);
517
- worker?.postMessage({ type: "elements", elements });
518
- });
519
- break;
520
- case "done":
521
- case "complete": {
522
- const agent = this.agents.get(agentId);
523
- if (agent) {
524
- agent.status = "stopped";
525
- agent.worker.terminate();
526
- this.aiWorkers.delete(tabId);
527
- this.agents.delete(agentId);
528
- }
529
- break;
530
- }
531
- }
532
- }
533
- // ===========================================================================
534
- // Cross-tab Communication
535
- // ===========================================================================
536
- async broadcastToAI(message) {
537
- this.aiWorkers.forEach((worker) => {
538
- worker.postMessage({ type: "broadcast", message });
539
- });
540
- }
541
- // ===========================================================================
542
- // Content Script Message Handler
543
- // ===========================================================================
544
- setupMessageHandlers() {
545
- if (typeof chrome === "undefined" || !chrome.runtime) return;
546
- chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
547
- if (request.from === "content" && sender.tab?.id) {
548
- this.handleContentMessage(sender.tab.id, request, sendResponse);
549
- return true;
550
- }
551
- if (request.action?.startsWith("browser.")) {
552
- this.handleBrowserAction(request, sendResponse);
553
- return true;
554
- }
555
- if (request.action === "listAgents") {
556
- sendResponse({ success: true, agents: this.getAgents() });
557
- return true;
558
- }
559
- if (request.action === "stopAgent") {
560
- const stopped = this.stopAgent(request.agentId);
561
- sendResponse({ success: stopped });
562
- return true;
563
- }
564
- if (request.action === "checkWebGPU") {
565
- if (navigator.gpu) {
566
- navigator.gpu.requestAdapter().then((adapter) => {
567
- sendResponse({
568
- available: !!adapter,
569
- adapter: adapter?.name || "Unknown GPU"
570
- });
571
- }).catch(() => {
572
- sendResponse({ available: false });
573
- });
574
- } else {
575
- sendResponse({ available: false });
576
- }
577
- return true;
578
- }
579
- });
580
- }
581
- async handleBrowserAction(request, sendResponse) {
582
- const tabId = request.tabId;
583
- try {
584
- switch (request.action) {
585
- case "browser.click":
586
- sendResponse({ success: await this.click(tabId, request.selector) });
587
- break;
588
- case "browser.type":
589
- sendResponse({ success: await this.type(tabId, request.selector, request.text) });
590
- break;
591
- case "browser.fill":
592
- sendResponse({ success: await this.fill(tabId, request.selector, request.value) });
593
- break;
594
- case "browser.select":
595
- sendResponse({ success: await this.select(tabId, request.selector, request.value) });
596
- break;
597
- case "browser.scroll":
598
- sendResponse({ success: await this.scroll(tabId, request.x, request.y, request.selector) });
599
- break;
600
- case "browser.hover":
601
- sendResponse({ success: await this.hover(tabId, request.selector) });
602
- break;
603
- case "browser.pressKey":
604
- sendResponse({ success: await this.pressKey(tabId, request.key, request.modifiers) });
605
- break;
606
- case "browser.evaluate":
607
- sendResponse({ success: true, result: await this.evaluate(tabId, request.expression) });
608
- break;
609
- case "browser.screenshot":
610
- sendResponse({ success: true, data: await this.captureScreenshot(tabId, request.options) });
611
- break;
612
- case "browser.navigate":
613
- await this.navigateTo(tabId, request.url);
614
- sendResponse({ success: true });
615
- break;
616
- case "browser.waitForSelector":
617
- sendResponse({ success: await this.waitForSelector(tabId, request.selector, request.timeout) });
618
- break;
619
- case "browser.getElementInfo":
620
- sendResponse({ success: true, info: await this.getElementInfo(tabId, request.selector) });
621
- break;
622
- case "browser.getPageInfo":
623
- sendResponse({ success: true, info: await this.getPageInfo(tabId) });
624
- break;
625
- case "browser.querySelectorAll":
626
- sendResponse({ success: true, elements: await this.querySelectorAll(tabId, request.selector) });
627
- break;
628
- default:
629
- sendResponse({ success: false, error: `Unknown action: ${request.action}` });
630
- }
631
- } catch (e) {
632
- sendResponse({ success: false, error: e.message });
633
- }
634
- }
635
- handleContentMessage(tabId, request, sendResponse) {
636
- const worker = this.aiWorkers.get(tabId);
637
- if (worker) {
638
- worker.postMessage({ type: "contentMessage", data: request });
639
- const responseHandler = (event) => {
640
- if (event.data.type === "contentResponse") {
641
- sendResponse(event.data.response);
642
- worker.removeEventListener("message", responseHandler);
643
- }
644
- };
645
- worker.addEventListener("message", responseHandler);
646
- setTimeout(() => {
647
- worker.removeEventListener("message", responseHandler);
648
- }, 1e4);
649
- } else {
650
- sendResponse({ error: "No AI worker for this tab" });
651
- }
652
- }
653
- // ===========================================================================
654
- // Script Execution Helper
655
- // ===========================================================================
656
- executeScript(tabId, code) {
657
- return new Promise((resolve, reject) => {
658
- if (chrome.scripting) {
659
- chrome.scripting.executeScript({
660
- target: { tabId },
661
- func: new Function(`return (${code})`)
662
- }, (results) => {
663
- if (chrome.runtime.lastError) {
664
- reject(new Error(chrome.runtime.lastError.message));
665
- } else {
666
- resolve(results?.[0]?.result);
667
- }
668
- });
669
- } else {
670
- chrome.tabs.executeScript(tabId, { code }, (results) => {
671
- if (chrome.runtime.lastError) {
672
- reject(new Error(chrome.runtime.lastError.message));
673
- } else {
674
- resolve(results?.[0]);
675
- }
676
- });
677
- }
678
- });
679
- }
680
- };
681
- export {
682
- BrowserControl
683
- };