browser-pilot-cli 0.3.0-rc.4 → 0.3.0-rc.5

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 CHANGED
@@ -182,7 +182,7 @@ A project can pin Browser Pilot locally and run the same CLI without a global
182
182
  installation:
183
183
 
184
184
  ```bash
185
- npm install --save-exact browser-pilot-cli@0.3.0-rc.4
185
+ npm install --save-exact browser-pilot-cli@0.3.0-rc.5
186
186
  npx --no-install browser-pilot tabs
187
187
  ```
188
188
 
package/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import { writeFileSync as writeFileSync2, readFileSync as readFileSync4, existsS
6
6
  import { resolve as resolvePath } from "path";
7
7
 
8
8
  // src/version.ts
9
- var BROWSER_PILOT_VERSION = "0.3.0-rc.4";
9
+ var BROWSER_PILOT_VERSION = "0.3.0-rc.5";
10
10
 
11
11
  // src/protocol/errors.ts
12
12
  var ERROR_CODES = [
@@ -1804,7 +1804,7 @@ var scrollEvidenceSchema = objectSchema({
1804
1804
  afterX: numberSchema({ minimum: 0 }),
1805
1805
  afterY: numberSchema({ minimum: 0 }),
1806
1806
  matchedText: sensitive(stringSchema({ maxLength: 4096 }), "browser_data"),
1807
- reason: stringSchema({ enum: ["at_boundary", "text_not_found"] })
1807
+ reason: stringSchema({ enum: ["at_boundary", "text_not_found", "text_not_revealed"] })
1808
1808
  }, [
1809
1809
  "action",
1810
1810
  "status",
@@ -4368,9 +4368,6 @@ program.command("close").description("Close current browser tab").option("-a, --
4368
4368
  context: { failedTargetIds: failed }
4369
4369
  });
4370
4370
  }
4371
- if (remainingTabs.length > 0 && !remainingTabs.some((tab) => tab.active)) {
4372
- await client.callTool("browser.tabs.switch", { targetId: remainingTabs[0].targetId });
4373
- }
4374
4371
  emit(
4375
4372
  { ok: true, closed, remaining: remainingTabs.length },
4376
4373
  `\u2713 Closed ${closed} Pilot tab(s)`
@@ -4381,8 +4378,8 @@ program.command("close").description("Close current browser tab").option("-a, --
4381
4378
  const remainingTabs = await client.listTabs("all");
4382
4379
  if (remainingTabs.length > 0) {
4383
4380
  if (!remainingTabs.some((tab) => tab.active)) {
4384
- const fallback = remainingTabs.find((tab) => tab.origin !== "user_tab") ?? remainingTabs[0];
4385
- await client.callTool("browser.tabs.switch", { targetId: fallback.targetId });
4381
+ const fallback = remainingTabs.find((tab) => tab.origin !== "user_tab");
4382
+ if (fallback) await client.callTool("browser.tabs.switch", { targetId: fallback.targetId });
4386
4383
  }
4387
4384
  emit({ ok: true, remaining: remainingTabs.length }, "\u2713 Tab closed");
4388
4385
  } else {
package/dist/daemon.js CHANGED
@@ -747,7 +747,7 @@ var scrollEvidenceSchema = objectSchema({
747
747
  afterX: numberSchema({ minimum: 0 }),
748
748
  afterY: numberSchema({ minimum: 0 }),
749
749
  matchedText: sensitive(stringSchema({ maxLength: 4096 }), "browser_data"),
750
- reason: stringSchema({ enum: ["at_boundary", "text_not_found"] })
750
+ reason: stringSchema({ enum: ["at_boundary", "text_not_found", "text_not_revealed"] })
751
751
  }, [
752
752
  "action",
753
753
  "status",
@@ -9012,6 +9012,10 @@ var ScrollService = class {
9012
9012
  const exact=${exact};
9013
9013
  const normalize=value=>String(value||'').replace(/\\s+/g,' ').trim();
9014
9014
  const needle=normalize(query).toLocaleLowerCase();
9015
+ const slash=String.fromCharCode(92);
9016
+ const regexSpecials='^$.*+?()[]{}|'+slash;
9017
+ const escapeRegex=value=>Array.from(value,char=>regexSpecials.includes(char)?slash+char:char).join('');
9018
+ const queryPattern=exact?null:new RegExp(normalize(query).split(' ').map(escapeRegex).join(slash+'s+'),'iu');
9015
9019
  const roots=[document];
9016
9020
  let visited=0;
9017
9021
  while(roots.length&&visited<20000){
@@ -9029,21 +9033,50 @@ var ScrollService = class {
9029
9033
  if(!element)continue;
9030
9034
  const tag=String(element.tagName||'').toLowerCase();
9031
9035
  if(['script','style','noscript','template'].includes(tag))continue;
9032
- const content=normalize(node.nodeValue);
9036
+ const raw=String(node.nodeValue||'');
9037
+ const content=normalize(raw);
9033
9038
  const normalized=content.toLocaleLowerCase();
9034
9039
  if(!(exact?normalized===needle:normalized.includes(needle)))continue;
9040
+ let matchStart=0;
9041
+ let matchEnd=raw.length;
9042
+ if(exact){
9043
+ matchStart=Math.max(0,raw.search(/\\S/u));
9044
+ matchEnd=Math.max(matchStart,raw.length-((raw.match(/\\s*$/u)||[''])[0].length));
9045
+ }else{
9046
+ const match=queryPattern.exec(raw);
9047
+ if(!match)continue;
9048
+ matchStart=match.index;
9049
+ matchEnd=match.index+match[0].length;
9050
+ }
9035
9051
  const style=getComputedStyle(element);
9036
9052
  if(style.display==='none'||style.visibility==='hidden'||style.visibility==='collapse'||element.getClientRects().length===0)continue;
9053
+ const viewportWidth=Math.max(0,window.innerWidth||document.documentElement.clientWidth||0);
9054
+ const viewportHeight=Math.max(0,window.innerHeight||document.documentElement.clientHeight||0);
9055
+ const range=document.createRange();
9056
+ range.setStart(node,matchStart);
9057
+ range.setEnd(node,matchEnd);
9058
+ const rangeRect=()=>range.getBoundingClientRect();
9059
+ const intersectsViewport=rect=>rect.width>0&&rect.height>0&&rect.bottom>0&&rect.right>0&&rect.top<viewportHeight&&rect.left<viewportWidth;
9037
9060
  const beforeX=Math.max(0,window.scrollX||document.documentElement.scrollLeft||0);
9038
9061
  const beforeY=Math.max(0,window.scrollY||document.documentElement.scrollTop||0);
9039
9062
  element.scrollIntoView({block:'center',inline:'nearest',behavior:'instant'});
9040
- const afterX=Math.max(0,window.scrollX||document.documentElement.scrollLeft||0);
9041
- const afterY=Math.max(0,window.scrollY||document.documentElement.scrollTop||0);
9063
+ let afterX=Math.max(0,window.scrollX||document.documentElement.scrollLeft||0);
9064
+ let afterY=Math.max(0,window.scrollY||document.documentElement.scrollTop||0);
9065
+ let rect=rangeRect();
9066
+ if(!intersectsViewport(rect)){
9067
+ const left=Math.max(0,afterX+rect.left-Math.max(0,(viewportWidth-rect.width)/2));
9068
+ const top=Math.max(0,afterY+rect.top-Math.max(0,(viewportHeight-rect.height)/2));
9069
+ window.scrollTo({left,top,behavior:'instant'});
9070
+ afterX=Math.max(0,window.scrollX||document.documentElement.scrollLeft||0);
9071
+ afterY=Math.max(0,window.scrollY||document.documentElement.scrollTop||0);
9072
+ rect=rangeRect();
9073
+ }
9042
9074
  const round=value=>Math.round(Number(value)||0);
9043
9075
  const moved=Math.abs(afterX-beforeX)>=1||Math.abs(afterY-beforeY)>=1;
9044
- return{ok:true,action:'scroll',status:'verified',mode:'text',target:'text',moved,
9076
+ const revealed=intersectsViewport(rect);
9077
+ return{ok:true,action:'scroll',status:revealed?'verified':'mismatch',mode:'text',target:'text',moved,
9045
9078
  deltaX:round(afterX-beforeX),deltaY:round(afterY-beforeY),beforeX:round(beforeX),beforeY:round(beforeY),
9046
- afterX:round(afterX),afterY:round(afterY),matchedText:content.slice(0,4096)};
9079
+ afterX:round(afterX),afterY:round(afterY),matchedText:content.slice(0,4096),...(revealed?{}:{reason:'text_not_revealed'})};
9047
9080
  }
9048
9081
  }
9049
9082
  return{ok:true,action:'scroll',status:'mismatch',mode:'text',target:'text',moved:false,
@@ -9066,11 +9099,13 @@ var ScrollService = class {
9066
9099
  var MAX_ANNOTATIONS = 200;
9067
9100
  function annotationScript(data, annotations, viewport) {
9068
9101
  return `(async() => {
9069
- const source=${JSON.stringify(`data:image/png;base64,${data}`)};
9102
+ const base64=${JSON.stringify(data)};
9070
9103
  const annotations=${JSON.stringify(annotations)};
9071
9104
  const viewport=${JSON.stringify(viewport)};
9072
- const response=await fetch(source);
9073
- const bitmap=await createImageBitmap(await response.blob());
9105
+ const binary=atob(base64);
9106
+ const bytes=new Uint8Array(binary.length);
9107
+ for(let index=0;index<binary.length;index+=1)bytes[index]=binary.charCodeAt(index);
9108
+ const bitmap=await createImageBitmap(new Blob([bytes],{type:'image/png'}));
9074
9109
  const canvas=document.createElement('canvas');
9075
9110
  canvas.width=bitmap.width;canvas.height=bitmap.height;
9076
9111
  const context=canvas.getContext('2d');
@@ -13979,7 +14014,7 @@ var ManagedTargetJanitorClient = class {
13979
14014
  };
13980
14015
 
13981
14016
  // src/version.ts
13982
- var BROWSER_PILOT_VERSION = "0.3.0-rc.4";
14017
+ var BROWSER_PILOT_VERSION = "0.3.0-rc.5";
13983
14018
 
13984
14019
  // src/daemon.ts
13985
14020
  var CLI_EXECUTABLE_PATH = publicExecutablePath(import.meta.url);
@@ -1,7 +1,7 @@
1
1
  # Browser Pilot v0.3.0 Stabilization Plan
2
2
 
3
3
  Status: **In progress**
4
- Candidate baseline: `v0.3.0-rc.3`; correction target: `v0.3.0-rc.4`, protocol `1.2`
4
+ Candidate baseline: `v0.3.0-rc.4`; correction target: `v0.3.0-rc.5`, protocol `1.2`
5
5
 
6
6
  ## Goal
7
7
 
@@ -35,8 +35,10 @@ products depend on. No Agent-specific behavior enters Browser Pilot.
35
35
  integration checks against the installed standalone release executable.
36
36
  - [x] Run one controlled user-Chrome host acceptance with explicit Profile
37
37
  choices and a single authorization attempt.
38
- - [ ] Publish `v0.3.0-rc.4` with Broker-backed CLI browser status and verify
38
+ - [x] Publish `v0.3.0-rc.4` with Broker-backed CLI browser status and verify
39
39
  that passive discovery reports the live authorized connection.
40
+ - [ ] Publish `v0.3.0-rc.5` with the real-site soak corrections and rerun the
41
+ same public information, form, capture, and managed-cleanup tasks.
40
42
  - [ ] Soak several real Agent tasks through the installed candidate.
41
43
  - [ ] Tag `v0.3.0`, publish npm `latest`, and verify upgrade/rollback metadata.
42
44
 
@@ -0,0 +1,26 @@
1
+ # Browser Pilot 0.3.0-rc.5
2
+
3
+ This release candidate fixes three edge cases found by running RC.4 through
4
+ real Agent tasks in two live Chrome Profiles.
5
+
6
+ - Text-targeted scrolling now verifies that the matched text actually entered
7
+ the viewport. If a site's `scrollIntoView` path is ineffective, Browser Pilot
8
+ falls back to the matched text Range's page coordinates; an unrevealed match
9
+ returns an explicit mismatch instead of a false success.
10
+ - Screenshot annotations no longer decode captured PNGs through
11
+ `fetch(data:)`. They use in-memory base64, Blob, and ImageBitmap conversion in
12
+ the isolated world, so restrictive site `connect-src` policies such as
13
+ GitHub's cannot break annotation.
14
+ - Tab cleanup no longer switches control to a user tab when Chrome has not yet
15
+ reported its next active target. `bp close --all` still closes only managed
16
+ tabs.
17
+
18
+ The soak otherwise completed public GitHub reading and targeted search, a
19
+ disposable HTTP form with verified input and control state, submission result
20
+ verification, multi-Profile routing, ordinary screenshots, annotated form
21
+ screenshots, PDF export, connection-status checks, and preservation of every
22
+ user tab and exported file.
23
+
24
+ RC.5 keeps protocol 1.2 and the Agent-neutral stdio contract unchanged.
25
+ Candidate gates passed with 276 Node tests, 109 isolated Playwright tests, and
26
+ TypeScript type checking.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "browser-pilot-cli",
3
- "version": "0.3.0-rc.4",
3
+ "version": "0.3.0-rc.5",
4
4
  "description": "CLI tool to control your browser via Chrome DevTools Protocol",
5
5
  "repository": "https://github.com/relixiaobo/browser-pilot",
6
6
  "type": "module",
@@ -48,6 +48,7 @@
48
48
  "docs/releases/v0.3.0-rc.2.md",
49
49
  "docs/releases/v0.3.0-rc.3.md",
50
50
  "docs/releases/v0.3.0-rc.4.md",
51
+ "docs/releases/v0.3.0-rc.5.md",
51
52
  "README.md",
52
53
  "LICENSE"
53
54
  ],