Skip to content

Prepare Browser Control 0.2.1 for the Chrome Web Store - #1

Merged
afif-reap merged 17 commits into
mainfrom
afif/chrome-web-store-release
Sep 28, 2026
Merged

afif-reap merged 17 commits into
mainfrom
afif/chrome-web-store-release

Conversation

@afif-reap

Copy link
Copy Markdown
Contributor

Summary

This brings the installed 0.2.0 extension source onto origin and prepares Browser Control 0.2.1 for the Chrome Web Store.

  • Source baseline. The branch starts from 4e734a0 on the local afif/native-fallback-controls branch, plus the edits that were uncommitted in its archived worktree. Built together, they reproduce the installed 0.2.0 dist/extension byte for byte. The installed build is the one the fast-chrome controller loads.
  • Rename. "Chrome Control" becomes "Browser Control", because the Web Store branding guidelines bar Google trademarks in an item name. The native host name com.opzero.chrome and protocol v2 do not change, so fast-chrome keeps working.
  • Narrower permissions. history and downloads are removed. No client, including fast-chrome, calls getUserHistory or reads download events. pnpm run check now pins the exact permission list and refuses a manifest key.
  • Host controls. New unit tests cover Reload host, Pause host and Resume host. Pause must survive reconnect alarms and a service-worker restart, reload must open a fresh port and end sessions, and the reconnect alarm must recover after the host exits.
  • Website. site/ holds the homepage, privacy policy, support page and reviewer steps, served at https://browser-control.pages.dev/. Version 0.1.3 was rejected because its privacy link was a GitHub file.
  • Listing. store/listing.md has every dashboard field, traced to the source. store/assets has real-use screenshots and promo tiles, and store/capture can recreate them.
  • Keyless publishing. The Chrome Web Store workflow signs in as a Google service account through Workload Identity Federation. It replaces the OAuth refresh token, which had expired. The workflow can read the item status, upload a draft, or upload and submit.

Verification

  • pnpm run check: 148 tests pass. With OPZERO_SYNTHETIC_CHROME set, the 37 headless private-input tests pass too.
  • Apart from the rename strings, the built extension is byte-identical to the installed 0.2.0 build (verified before the permission removal).
  • The Chrome Web Store workflow with action: status signs in without stored credentials and reads the item (HTTP 200; 0.1.2 published).
  • https://browser-control.pages.dev/, /privacy/, /support/ and /support/reviewers/ return 200. Search Console verified ownership of the site.

Follow-ups

  • The coordinator will replace skills/chrome-control. It still mentions getUserHistory and the old name.
  • The old CHROME_CLIENT_ID, CHROME_CLIENT_SECRET and CHROME_REFRESH_TOKEN secrets are no longer used.

…build

These edits were uncommitted in the archived afif/native-fallback-controls
worktree. Built together with 4e734a0 they reproduce the installed 0.2.0
extension byte for byte.
The previous chunks embedded a machine-local node_modules path in
source-map region comments.
Chrome Web Store branding guidelines bar Google trademarks in an item
name. The native host name com.opzero.chrome and protocol v2 stay the
same, so the installed fast-chrome controller keeps working.
Pause must survive reconnect alarms and a service worker restart until
the user resumes. Reload must drop active sessions, open a fresh native
port, and the reconnect alarm must recover after the host exits.
No client calls getUserHistory or consumes onDownloadChange, including
the fast-chrome controller. The Web Store rejects versions that request
permissions the item does not need. The project check now pins the exact
permission list and refuses a manifest key in the store package.
The OAuth refresh token expired because its consent screen was in
testing mode. GitHub Actions now signs in as the cws-publisher service
account through Workload Identity Federation, so the repository holds
no Google credential. The workflow can read the item status, upload a
draft package, or upload and submit for review.

Also update the README and developer guide for the Browser Control
name, the store and unpacked extension IDs, and the host controls.
The site is served from Cloudflare Pages at browser-control.pages.dev
and holds the homepage, privacy policy, support page and reviewer
steps. Google rejected 0.1.3 because its privacy link pointed to a
GitHub file; the listing now points to the hosted policy.

store/listing.md holds every dashboard field, with permission
justifications and data disclosures traced to the source. The store
images are captures of the real extension in a disposable Chrome for
Testing profile, and store/capture can recreate them.
…st as connected

A host killed without cleanup left its socket file behind, and every
respawn refused to start, so the extension never reconnected. The host
now removes the file only when a connection to it is refused.

connectNative returns a port even when no host is installed, so the
popup showed Connected for a missing host. The extension now reports
Connecting until the host sends its first message. The popup also opens
chrome://extensions through the tabs API, which a plain link cannot.
The latest GitHub release is still 0.1.3, so the reviewer steps would
download a host that predates protocol v2. The site now serves the
freshly built chrome-control-skill.zip, and RELEASE.md copies it in
before each deploy.
Two hosts recovering the same stale socket could both unlink it, and a
host shutting down could delete the socket that replaced its own.
Recovery now runs under an exclusive lock file and checks the socket's
identity before unlinking it. Shutdown removes only the socket the host
bound, and a failed chmod shuts down cleanly.

The extension now drops a host that sends nothing within 15 seconds or
stops answering the heartbeat, so the reconnect alarm can start a new
one instead of waiting forever.

The listing and privacy policy now disclose personally identifiable
information. Private fill handles user names and email addresses, and
the agent can type personal details into forms.
A fresh host could bind while a recovering host probed the same path,
and the recovery lock's age rule could evict a live owner. Every Unix
startup now holds one lock from its first look at the socket until the
new socket listens. The lock appears atomically with its owner's pid
and is stale only when that process is dead.

A heartbeat that fails after a reload no longer stops the new
connection's sessions.
Chrome refuses to inject into a new tab until its first navigation
commits, so waitFor failed about once in nine reviewer runs. The retry
covers only that exact message, and it only observes, so no input is
replayed. The capture script also stops deleting a socket that another
host may still own.
@afif-reap
afif-reap merged commit 4cd5dd9 into main Sep 28, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant