From 1ef93463dd3a7efb8dcb659add1f15e981fdc505 Mon Sep 17 00:00:00 2001 From: drakezhang Date: Wed, 19 Aug 2026 11:19:47 +0800 Subject: [PATCH 1/3] docs: list the Edge Add-ons build alongside the Chrome Web Store The extension is now published to Edge Add-ons, but every install path still pointed Edge users at the Chrome Web Store. Both READMEs and AGENT_INSTALL.md now name a store per browser, and `bsk doctor` offers both listings when no browser is connected. Two Edge-specific gaps went with it: Edge blocks content-script injection on its own storefront the same way Chrome does, so the borrow-confirmation overlay must treat microsoftedge.microsoft.com/addons as non-injectable; and the Chinese README still described the pre-install.ps1 manual Windows setup. Also give the DeepSeek Harness section its npm package link and the profile-start step, so plugin users have a complete path. Co-authored-by: Cursor --- AGENT_INSTALL.md | 14 ++++-- README.md | 26 ++++++++-- README.zh-CN.md | 26 ++++++++-- .../__tests__/borrow-confirmation.test.ts | 10 ++++ .../src/tools/borrow-confirmation.ts | 8 ++-- crates/bsk-cli/src/cli/doctor.rs | 48 ++++++++++++++----- packages/dsh-plugin-browserskill/README.md | 9 ++-- 7 files changed, 109 insertions(+), 32 deletions(-) diff --git a/AGENT_INSTALL.md b/AGENT_INSTALL.md index 359d83d..02a78a0 100644 --- a/AGENT_INSTALL.md +++ b/AGENT_INSTALL.md @@ -34,13 +34,17 @@ where only `extension connected` fails is expected; go to Step 3. ## 3. Open the extension install page If `extension connected` is `FAIL` (`0 browsers connected`), the user likely -has not installed or enabled the browser extension yet. Open the -[Chrome Web Store install page](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) -in the user's browser, then tell the user: +has not installed or enabled the browser extension yet. Open the install page +matching the user's browser — +[Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) +for Chrome and other Chromium browsers, +[Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg) +for Microsoft Edge — then tell the user: > Install **BrowserSkill** from the -> [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi), -> open the popup, and wait until it turns green. Reply when done. +> [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) +> (or [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg) +> on Edge), open the popup, and wait until it turns green. Reply when done. If opening the page fails, give the user the same link. Then run `bsk doctor` once more. All `ok`/`na` → tell the user it's ready. diff --git a/README.md b/README.md index 16d755b..86f96eb 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,8 @@ Set up browser-skill on this machine by following https://raw.githubusercontent.
-Install the CLI, then install the extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi). +Install the CLI, then install the extension from the [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) +or [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg). #### 1. Install the `bsk` CLI @@ -91,7 +92,14 @@ bsk --version #### 2. Install the browser extension -Install BrowserSkill from the [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi). +Install BrowserSkill from your browser's store: + +| Browser | Store listing | +| --- | --- | +| Chrome | [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) | +| Microsoft Edge | [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg) | + +On other Chromium-based browsers, install the Chrome Web Store build. #### 3. Install the skill @@ -136,14 +144,24 @@ Start a new Agent session and write a prompt that needs the browser, for example ## DeepSeek Harness plugin -A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) plugin that -injects native `browser_*` tools (no shelling out to `bsk`) and a live Web UI +Using [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (`dsh`)? +BrowserSkill ships a first-class dsh plugin on npm as +[`@wxg-prc-cpg/browser-skill-dsh-plugin`](https://www.npmjs.com/package/@wxg-prc-cpg/browser-skill-dsh-plugin). +It injects native `browser_*` tools (no shelling out to `bsk`) and a live Web UI overlay of each Agent Window. +Add it to a dsh profile, then start that profile: + ```sh dsh plugin --profile web add @wxg-prc-cpg/browser-skill-dsh-plugin +dsh --profile web ``` +The plugin carries its own copy of the skill, so `bsk install-skill` is not needed +for dsh — but the `bsk` CLI and the browser extension are still prerequisites. See +the [plugin README](packages/dsh-plugin-browserskill/README.md) for the tool list, +configuration, and the observation overlay. + ## How It Works BrowserSkill is a local bridge between your agent harness and your browser. diff --git a/README.zh-CN.md b/README.zh-CN.md index 0ca6f81..4a2a1b1 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -54,7 +54,8 @@ BrowserSkill 由两个本地运行组件组成:`bsk` CLI/daemon 和浏览器
-先安装 CLI,再从 [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) 安装浏览器扩展。 +先安装 CLI,再从 [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) +或 [Edge 加载项商店](https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg) 安装浏览器扩展。 #### 1. 安装 `bsk` CLI @@ -64,8 +65,11 @@ BrowserSkill 由两个本地运行组件组成:`bsk` CLI/daemon 和浏览器 curl -fsSL https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.sh | sh ``` -**Windows**:从 [最新 CLI release](https://github.com/Tencent/BrowserSkill/releases/latest) -下载 `bsk-v-x86_64-pc-windows-msvc.zip`,解压后将 `bsk.exe` 加入 `PATH`。 +**Windows**(PowerShell,安装到 `~/.local/bin`): + +```powershell +irm https://raw.githubusercontent.com/Tencent/BrowserSkill/main/install.ps1 | iex +``` 验证二进制: @@ -75,7 +79,14 @@ bsk --version #### 2. 安装浏览器扩展 -从 [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) 安装 BrowserSkill。 +在对应浏览器的商店安装 BrowserSkill: + +| 浏览器 | 商店页面 | +| --- | --- | +| Chrome | [Chrome Web Store](https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi) | +| Microsoft Edge | [Edge 加载项商店](https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg) | + +其他基于 Chromium 的浏览器,安装 Chrome Web Store 版本即可。 #### 3. 安装 skill @@ -114,12 +125,17 @@ bsk install-skill ## DeepSeek Harness 插件 -[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 插件:注入原生 `browser_*` 工具(无需再通过 Shell 调用 `bsk`),并在 Web UI 中实时观察每个 Agent Window。 +在用 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)?BrowserSkill 提供了官方 dsh 插件,已发布到 npm:[`@wxg-prc-cpg/browser-skill-dsh-plugin`](https://www.npmjs.com/package/@wxg-prc-cpg/browser-skill-dsh-plugin)。它会注入原生 `browser_*` 工具(无需再通过 Shell 调用 `bsk`),并在 Web UI 中实时观察每个 Agent Window。 + +把它装进某个 dsh profile,然后启动该 profile: ```sh dsh plugin --profile web add @wxg-prc-cpg/browser-skill-dsh-plugin +dsh --profile web ``` +插件自带 skill,所以在 dsh 下无需执行 `bsk install-skill`;但 `bsk` CLI 和浏览器扩展仍是前置条件。工具清单、配置项与观察浮层见[插件 README](packages/dsh-plugin-browserskill/README.md)。 + ## 工作原理 BrowserSkill 是 Agent 运行时与浏览器之间的本地桥接层。 diff --git a/apps/extension/src/tools/__tests__/borrow-confirmation.test.ts b/apps/extension/src/tools/__tests__/borrow-confirmation.test.ts index 6ecfd23..0cb106b 100644 --- a/apps/extension/src/tools/__tests__/borrow-confirmation.test.ts +++ b/apps/extension/src/tools/__tests__/borrow-confirmation.test.ts @@ -612,4 +612,14 @@ describe("isInjectableContentScriptUrl", () => { ).toBe(false); expect(isInjectableContentScriptUrl("https://chromewebstore.google.com/")).toBe(false); }); + + it("rejects the Edge Add-ons store", () => { + expect( + isInjectableContentScriptUrl( + "https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg", + ), + ).toBe(false); + // Non-store paths on the same host stay injectable. + expect(isInjectableContentScriptUrl("https://microsoftedge.microsoft.com/")).toBe(true); + }); }); diff --git a/apps/extension/src/tools/borrow-confirmation.ts b/apps/extension/src/tools/borrow-confirmation.ts index 060f935..68a889f 100644 --- a/apps/extension/src/tools/borrow-confirmation.ts +++ b/apps/extension/src/tools/borrow-confirmation.ts @@ -250,9 +250,10 @@ const DEFAULT_NOTIFICATION_COPY: BorrowNotificationCopy = { // URL injection-eligibility test // --------------------------------------------------------------------------- -const CHROME_WEB_STORE_RES = [ +const EXTENSION_STORE_RES = [ /^https:\/\/chrome\.google\.com\/webstore/i, /^https:\/\/chromewebstore\.google\.com/i, + /^https:\/\/microsoftedge\.microsoft\.com\/addons/i, ]; /** @@ -264,14 +265,15 @@ const CHROME_WEB_STORE_RES = [ * - `ftp://` no longer hosts content scripts reliably in modern Chrome. * - `about:` / `chrome:` / `chrome-extension:` / `edge:` / `devtools:` / * `view-source:` / `data:` / `blob:` are blocked by the platform. - * - The Chrome Web Store (both legacy and new domains) is also blocked. + * - Extension storefronts are blocked by their own browser: the Chrome Web + * Store (legacy and new domains) under Chrome, and Edge Add-ons under Edge. * * Mirrors the implicit scheme list documented in * https://developer.chrome.com/docs/extensions/develop/concepts/match-patterns. */ export function isInjectableContentScriptUrl(url: string | undefined): boolean { if (!url) return false; - for (const re of CHROME_WEB_STORE_RES) { + for (const re of EXTENSION_STORE_RES) { if (re.test(url)) return false; } return /^https?:\/\//i.test(url); diff --git a/crates/bsk-cli/src/cli/doctor.rs b/crates/bsk-cli/src/cli/doctor.rs index fa33718..3d0c57a 100644 --- a/crates/bsk-cli/src/cli/doctor.rs +++ b/crates/bsk-cli/src/cli/doctor.rs @@ -18,6 +18,12 @@ use crate::daemon::state::PROTOCOL_VERSION; const EXTENSION_STORE_URL: &str = "https://chromewebstore.google.com/detail/hhcmgoofomhgciiibhipgmgkgnoenaoi"; +/// Edge Add-ons listing for the browser-skill extension. +const EXTENSION_STORE_URL_EDGE: &str = "https://microsoftedge.microsoft.com/addons/detail/browserskill/emacgiaaaiojkkpkddmmdfhmokgmnikg"; + +/// Store listings highlighted in repair hints, in the order they appear. +const EXTENSION_STORE_URLS: [&str; 2] = [EXTENSION_STORE_URL, EXTENSION_STORE_URL_EDGE]; + /// Status of a single doctor check. `Ok` / `Fail` are the legacy two /// states; `NotApplicable` (review M2) is reported as "N/A" in human /// output and as `"status": "na"` in `--json` output, so a check that @@ -420,7 +426,10 @@ fn check_extension_connected(status: Option<&StatusResult>) -> CheckResult { CheckResult::fail( name, "0 browsers connected", - format!("install the extension from {EXTENSION_STORE_URL} and load it in Chromium"), + format!( + "install the extension from {EXTENSION_STORE_URL} (Chrome) \ + or {EXTENSION_STORE_URL_EDGE} (Edge) and load it in the browser" + ), ) } } @@ -428,17 +437,14 @@ fn check_extension_connected(status: Option<&StatusResult>) -> CheckResult { /// Highlight known URLs in repair hints for terminal output. Plain /// text is preserved in `--json` and in stored [`CheckResult::hint`]. fn style_hint(hint: &str) -> String { - if !hint.contains(EXTENSION_STORE_URL) { - return hint.to_string(); + let mut styled = hint.to_string(); + for url in EXTENSION_STORE_URLS { + if !styled.contains(url) { + continue; + } + styled = styled.replace(url, &style(url).cyan().bold().underlined().to_string()); } - hint.replace( - EXTENSION_STORE_URL, - &style(EXTENSION_STORE_URL) - .cyan() - .bold() - .underlined() - .to_string(), - ) + styled } fn render_human(checks: &[CheckResult]) { @@ -504,6 +510,26 @@ mod m2_tests { hint.contains(EXTENSION_STORE_URL), "hint should include Chrome Web Store URL: {hint}" ); + assert!( + hint.contains(EXTENSION_STORE_URL_EDGE), + "hint should include Edge Add-ons URL: {hint}" + ); + } + + #[test] + fn style_hint_preserves_every_store_url() { + let hint = check_extension_connected(Some(&fake_status(Vec::new(), Vec::new()))) + .hint + .expect("extension disconnected should include a hint"); + let styled = style_hint(&hint); + // Whether or not the terminal accepts colors, styling must never drop or + // mangle a URL the user has to click. + for url in EXTENSION_STORE_URLS { + assert!( + styled.contains(url), + "styled hint should still contain {url}: {styled}" + ); + } } #[test] diff --git a/packages/dsh-plugin-browserskill/README.md b/packages/dsh-plugin-browserskill/README.md index 592bdad..22e1273 100644 --- a/packages/dsh-plugin-browserskill/README.md +++ b/packages/dsh-plugin-browserskill/README.md @@ -6,7 +6,7 @@ A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (dsh) tool [BrowserSkill](https://github.com/Tencent/BrowserSkill) (`bsk`) browser automation to the model. Each tool maps to one `bsk --json` invocation: the plugin spawns the bsk CLI, parses its -structured JSON output, and returns a canonical typed value. The bsk daemon, browser, and Chrome +structured JSON output, and returns a canonical typed value. The bsk daemon, browser, and browser extension keep owning the actual browser control — this package is a thin, well-typed bridge. ## Tools @@ -64,9 +64,10 @@ dsh plugin --profile add @wxg-prc-cpg/browser-skill-dsh-plugin dsh --profile ``` -Prerequisite: the `bsk` CLI must be installed and on `PATH`, and the BrowserSkill Chrome extension -must be connected — see the [BrowserSkill README](https://github.com/Tencent/BrowserSkill). When bsk -is missing, tool calls fail with install guidance instead of a bare spawn error. +Prerequisite: the `bsk` CLI must be installed and on `PATH`, and the BrowserSkill browser extension +(Chrome or Edge) must be connected — see the +[BrowserSkill README](https://github.com/Tencent/BrowserSkill). When bsk is missing, tool calls fail +with install guidance instead of a bare spawn error. ## Configuration From 2b7e10ca2b447094f9083eedc8629c4797051d27 Mon Sep 17 00:00:00 2001 From: drakezhang Date: Wed, 19 Aug 2026 11:20:07 +0800 Subject: [PATCH 2/3] fix(dsh-plugin-browserskill): identify the plugin by its published name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renaming the package to @wxg-prc-cpg/browser-skill-dsh-plugin left two identifiers behind on the old directory name, and both are resolved by name rather than by path. cordis.patch.yml is the specifier the loader imports, so dsh failed with ERR_MODULE_NOT_FOUND on startup and took the whole plugin tree down with it — installing the published package made dsh unusable. The client bundle registers itself under the same kind of key, which the web shell looks up by package name, so the screenshot toolview and the observation overlay were unreachable too. The bundle id now reads package.json instead of repeating the name, so it cannot drift again. Quoting is required in the YAML because a leading '@' is reserved in plain scalars. Co-authored-by: Cursor --- packages/dsh-plugin-browserskill/README.md | 2 +- packages/dsh-plugin-browserskill/cordis.patch.yml | 4 +++- packages/dsh-plugin-browserskill/package.json | 2 +- packages/dsh-plugin-browserskill/tsdown.config.ts | 6 +++++- 4 files changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/dsh-plugin-browserskill/README.md b/packages/dsh-plugin-browserskill/README.md index 22e1273..d892631 100644 --- a/packages/dsh-plugin-browserskill/README.md +++ b/packages/dsh-plugin-browserskill/README.md @@ -77,7 +77,7 @@ All fields are optional and validated through the plugin's Schemastery `Config`: # cordis.patch.yml override example - insert: - id: browserskill - name: dsh-plugin-browserskill + name: "@wxg-prc-cpg/browser-skill-dsh-plugin" config: bskPath: bsk # path to the bsk binary (default: resolve from PATH) defaultTimeoutMs: 120000 diff --git a/packages/dsh-plugin-browserskill/cordis.patch.yml b/packages/dsh-plugin-browserskill/cordis.patch.yml index 8c04006..882eb62 100644 --- a/packages/dsh-plugin-browserskill/cordis.patch.yml +++ b/packages/dsh-plugin-browserskill/cordis.patch.yml @@ -1,3 +1,5 @@ - insert: - id: browserskill - name: dsh-plugin-browserskill + # Must be the published package name — the loader imports this specifier. + # Quoted because YAML reserves a leading '@' in plain scalars. + name: "@wxg-prc-cpg/browser-skill-dsh-plugin" diff --git a/packages/dsh-plugin-browserskill/package.json b/packages/dsh-plugin-browserskill/package.json index 8fd52b5..72191df 100644 --- a/packages/dsh-plugin-browserskill/package.json +++ b/packages/dsh-plugin-browserskill/package.json @@ -1,7 +1,7 @@ { "name": "@wxg-prc-cpg/browser-skill-dsh-plugin", "description": "DeepSeek Harness tool plugin that exposes BrowserSkill (bsk) browser automation to the model", - "version": "0.1.0", + "version": "0.1.1", "license": "MIT", "publishConfig": { "access": "public", diff --git a/packages/dsh-plugin-browserskill/tsdown.config.ts b/packages/dsh-plugin-browserskill/tsdown.config.ts index 9fcce3f..f5f1137 100644 --- a/packages/dsh-plugin-browserskill/tsdown.config.ts +++ b/packages/dsh-plugin-browserskill/tsdown.config.ts @@ -2,6 +2,7 @@ import { readFile } from "node:fs/promises"; import { basename, dirname, resolve as resolvePath } from "node:path"; import { transform } from "lightningcss"; import { defineConfig, type UserConfig } from "tsdown"; +import pkg from "./package.json" with { type: "json" }; /** * Two build faces of the dual-face package: @@ -33,7 +34,10 @@ const VENDORED_LIBRARY = /^@deepseek-ai\/(cosmokit|schemastery)(\/|$)/; const CSS_VIRTUAL_PREFIX = "\0bsk-css:"; const CSS_VIRTUAL_SUFFIX = ".mjs"; -const CLIENT_ID = "dsh-plugin-browserskill"; +// The web shell resolves client bundles from its module table by package name +// (dsh's own bundles register the same way), so this must never drift from +// package.json. +const CLIENT_ID = pkg.name; const client: UserConfig = { name: `${CLIENT_ID}/client`, From e720f1ffe51889a008f8587c602a7ba47ddb1d7b Mon Sep 17 00:00:00 2001 From: drakezhang Date: Wed, 19 Aug 2026 11:20:26 +0800 Subject: [PATCH 3/3] ci(dsh-plugin): publish from the npm-publish environment The publish job named its GitHub Environment NPM_TOKEN, which is the secret name rather than a deployment target, so the repository's Deployments list showed a bogus NPM_TOKEN entry next to chrome-web-store. The environment is now npm-publish; the secret it holds keeps its name. Co-authored-by: Cursor --- .github/workflows/release-dsh-plugin.yml | 4 ++-- packages/dsh-plugin-browserskill/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release-dsh-plugin.yml b/.github/workflows/release-dsh-plugin.yml index 69ae0bd..c7dacdb 100644 --- a/.github/workflows/release-dsh-plugin.yml +++ b/.github/workflows/release-dsh-plugin.yml @@ -66,8 +66,8 @@ jobs: publish: needs: [resolve, guard] runs-on: ubuntu-latest - # Secret NPM_TOKEN is stored on the GitHub Environment also named NPM_TOKEN. - environment: NPM_TOKEN + # Secret NPM_TOKEN is stored on the "npm-publish" GitHub Environment. + environment: npm-publish permissions: contents: read steps: diff --git a/packages/dsh-plugin-browserskill/README.md b/packages/dsh-plugin-browserskill/README.md index d892631..d90596c 100644 --- a/packages/dsh-plugin-browserskill/README.md +++ b/packages/dsh-plugin-browserskill/README.md @@ -187,8 +187,8 @@ git tag dsh-plugin-v0.1.0 git push origin dsh-plugin-v0.1.0 ``` -Or run the workflow from the Actions tab (`workflow_dispatch`). The job reads -`NPM_TOKEN` from the GitHub Environment of the same name. +Or run the workflow from the Actions tab (`workflow_dispatch`). The job reads the +`NPM_TOKEN` secret from the `npm-publish` GitHub Environment. ## License