pi-chrome 0.6.0 → 0.6.1

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Pi Existing Chrome Profile Bridge",
4
- "version": "0.6.0",
4
+ "version": "0.6.1",
5
5
  "description": "Lets Pi control tabs in this existing Chrome profile via a local bridge at 127.0.0.1.",
6
6
  "permissions": ["tabs", "scripting", "storage", "activeTab", "alarms"],
7
7
  "host_permissions": ["<all_urls>", "http://127.0.0.1:17318/*"],
@@ -196,10 +196,26 @@ async function executeInTab(params, func, args) {
196
196
  const results = await chrome.scripting.executeScript({
197
197
  target: { tabId: tab.id },
198
198
  world: "MAIN",
199
- func,
200
- args,
199
+ func: async (source, invocationArgs) => {
200
+ try {
201
+ const injected = (0, eval)(`(${source})`);
202
+ return { ok: true, value: await injected(...invocationArgs) };
203
+ } catch (error) {
204
+ return { ok: false, error: error?.stack || error?.message || String(error) };
205
+ }
206
+ },
207
+ args: [func.toString(), args],
201
208
  });
202
- return results?.[0]?.result;
209
+ const first = results?.[0];
210
+ if (first?.error) {
211
+ const message = typeof first.error === "string" ? first.error : (first.error.message || JSON.stringify(first.error));
212
+ throw new Error(message);
213
+ }
214
+ const envelope = first?.result;
215
+ if (envelope && typeof envelope === "object" && envelope.ok === false) {
216
+ throw new Error(envelope.error || "Chrome page script failed");
217
+ }
218
+ return envelope?.value;
203
219
  }
204
220
 
205
221
  async function bringToFront(tab) {
@@ -307,6 +323,17 @@ function resolvePoint(selector, x, y) {
307
323
  }
308
324
 
309
325
  function clickPage(selector, x, y) {
326
+ const resolvePoint = (selector, x, y) => {
327
+ if (selector) {
328
+ const element = document.querySelector(selector);
329
+ if (!element) throw new Error(`No element matches selector: ${selector}`);
330
+ element.scrollIntoView({ block: "center", inline: "center", behavior: "instant" });
331
+ const rect = element.getBoundingClientRect();
332
+ return { element, x: rect.left + rect.width / 2, y: rect.top + rect.height / 2, rect };
333
+ }
334
+ if (typeof x !== "number" || typeof y !== "number") throw new Error("Provide selector or x/y");
335
+ return { element: document.elementFromPoint(x, y), x, y, rect: undefined };
336
+ };
310
337
  const point = resolvePoint(selector, x, y);
311
338
  if (!point.element) throw new Error("No element at click point");
312
339
  for (const type of ["pointerdown", "mousedown", "pointerup", "mouseup", "click"]) {
@@ -316,6 +343,28 @@ function clickPage(selector, x, y) {
316
343
  }
317
344
 
318
345
  function typeIntoPage(selector, text, pressEnter) {
346
+ const normalizeKey = (key) => {
347
+ const table = {
348
+ enter: "Enter",
349
+ escape: "Escape",
350
+ tab: "Tab",
351
+ backspace: "Backspace",
352
+ delete: "Delete",
353
+ arrowup: "ArrowUp",
354
+ arrowdown: "ArrowDown",
355
+ arrowleft: "ArrowLeft",
356
+ arrowright: "ArrowRight",
357
+ };
358
+ return table[String(key).toLowerCase()] || key;
359
+ };
360
+ const pressKey = (key) => {
361
+ const target = document.activeElement || document.body;
362
+ const normalized = normalizeKey(key);
363
+ target.dispatchEvent(new KeyboardEvent("keydown", { key: normalized, bubbles: true, cancelable: true }));
364
+ target.dispatchEvent(new KeyboardEvent("keyup", { key: normalized, bubbles: true, cancelable: true }));
365
+ if (normalized === "Enter" && target instanceof HTMLFormElement) target.requestSubmit();
366
+ return { key: normalized };
367
+ };
319
368
  let element = selector ? document.querySelector(selector) : document.activeElement;
320
369
  if (!element) throw new Error(selector ? `No element matches selector: ${selector}` : "No active element");
321
370
  element.focus();
@@ -331,11 +380,25 @@ function typeIntoPage(selector, text, pressEnter) {
331
380
  } else {
332
381
  throw new Error("Focused element is not text-editable");
333
382
  }
334
- if (pressEnter) pressKeyInPage("Enter");
383
+ if (pressEnter) pressKey("Enter");
335
384
  return { selector, length: text.length, pressEnter };
336
385
  }
337
386
 
338
387
  function pressKeyInPage(key) {
388
+ const normalizeKey = (key) => {
389
+ const table = {
390
+ enter: "Enter",
391
+ escape: "Escape",
392
+ tab: "Tab",
393
+ backspace: "Backspace",
394
+ delete: "Delete",
395
+ arrowup: "ArrowUp",
396
+ arrowdown: "ArrowDown",
397
+ arrowleft: "ArrowLeft",
398
+ arrowright: "ArrowRight",
399
+ };
400
+ return table[String(key).toLowerCase()] || key;
401
+ };
339
402
  const target = document.activeElement || document.body;
340
403
  const normalized = normalizeKey(key);
341
404
  target.dispatchEvent(new KeyboardEvent("keydown", { key: normalized, bubbles: true, cancelable: true }));
@@ -46,7 +46,7 @@ type BridgeResult = {
46
46
  error?: string;
47
47
  };
48
48
 
49
- const PI_CHROME_VERSION = "0.6.0";
49
+ const PI_CHROME_VERSION = "0.6.1";
50
50
  const DEFAULT_HOST = process.env.PI_CHROME_BRIDGE_HOST ?? "127.0.0.1";
51
51
  const DEFAULT_PORT = Number(process.env.PI_CHROME_BRIDGE_PORT ?? "17318");
52
52
  const DEFAULT_TIMEOUT_MS = 30_000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-chrome",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Drive your existing logged-in Chrome from Pi \u2014 no re-login, no throwaway profile, watch the agent work in real time (or toggle quiet background mode).",
5
5
  "keywords": [
6
6
  "pi-package",