Merge pull request #173 from Tencent/release/0.2.0

Release/0.2.0
This commit is contained in:
Zhang GH
2026-09-02 19:00:21 +08:00
committed by GitHub
8 changed files with 401 additions and 10 deletions
+92
View File
@@ -0,0 +1,92 @@
# Changelog
All notable changes to BrowserSkill will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/).
Starting from 0.2.0, CLI / Extension / DSH Plugin share the same version number.
## [Unreleased]
## [0.2.0] - 2026-09-02
### Added
- File transfer: upload and download support across CLI, Extension, and DSH Plugin
- File transfer: drag-and-drop upload (`drop-to-upload`)
- VOM semantic graph, name enrichment, and hover perception modules
- VOM hover probing (opt-in via `observe` parameter)
- DSH Plugin: browser tool parity with CLI commands
- Edge Add-ons automated publishing in CI
- Protocol upgrade reminder when CLI / Extension protocol versions differ
- Browser evaluation harness (`evals/browser/`)
- Unified release script (`scripts/release.mjs`)
### Changed
- **Version scheme**: all three components now share the same semver
- VOM rendering algorithm optimizations
- Leaner SKILL.md agent instructions
- DSH Plugin: simplified browser commands
- Borrow confirmation UX — proactive focus and longer timeout
- PiP window now has a close button
### Fixed
- Screenshot media type detection (was hard-coded to `image/png`)
- DSH Plugin session lifecycle stability
- DSH Plugin Cordis package ID mismatch
- Observation thumbnail media type sniffing
- Upload/download race conditions and layout bypass issues
- VOM repeated name and safety policy issues
---
*Previous releases used independent version numbers per component.*
## CLI 0.1.11 / Extension 0.1.7 / DSH Plugin 0.1.2 — 2026-08-26 ~ 2026-08-29
### Added
- DSH Plugin sidebar integration, session lifecycle, and archive cleanup
- Recorder iframe and OOPIF support
### Changed
- VOM functional refactor
### Fixed
- Recorder safety policy and bug fixes
## CLI 0.1.10 / Extension 0.1.6 — 2026-08-08
### Added
- VOM observation recording and settled-state detection
- Record overlay timer
### Fixed
- Browser keepalive disconnect handling
## CLI 0.1.9 / Extension 0.1.5 — 2026-07-29
### Added
- CLI auto-update mechanism
- Trace v3 protocol and recorder
### Fixed
- MV3 keepalive disconnect
## CLI 0.1.8 / Extension 0.1.4 — 2026-07-22
### Added
- More browser interaction actions
### Fixed
- Windows named-pipe hash-only path issue
## CLI 0.1.7 / Extension 0.1.3 — 2026-07-07
Initial public release pair.
## CLI 0.1.6 — 2026-06-30
### Fixed
- Minor CLI fixes
## CLI 0.1.5 / Extension 0.1.2 — 2026-06-22
First tagged releases.
Generated
+2 -2
View File
@@ -134,7 +134,7 @@ dependencies = [
[[package]]
name = "bsk"
version = "0.1.11"
version = "0.2.0"
dependencies = [
"anyhow",
"base64",
@@ -169,7 +169,7 @@ dependencies = [
[[package]]
name = "bsk-protocol"
version = "0.1.11"
version = "0.2.0"
dependencies = [
"schemars",
"semver",
+2 -2
View File
@@ -7,7 +7,7 @@ members = [
default-members = ["crates/bsk-cli"]
[workspace.package]
version = "0.1.11"
version = "0.2.0"
edition = "2024"
rust-version = "1.85"
license = "MIT"
@@ -43,7 +43,7 @@ rand = "0.8"
uuid = { version = "1", features = ["v4", "serde"] }
# Workspace crates (version required for `cargo publish` of dependents)
bsk-protocol = { version = "0.1.9", path = "crates/bsk-protocol" }
bsk-protocol = { version = "0.2.0", path = "crates/bsk-protocol" }
[profile.release]
strip = "symbols"
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@browser-skill/extension",
"version": "0.1.7",
"version": "0.2.0",
"private": true,
"type": "module",
"scripts": {
@@ -122,8 +122,8 @@ describe("App", () => {
expect(screen.queryByText(/^扩展 v/)).toBeNull();
expect(screen.queryByText(/^daemon v/)).toBeNull();
expect(screen.getByTitle("扩展版本").textContent).toBe(EXTENSION_VERSION);
expect(screen.getByTitle("bsk 版本").textContent).toBe(mockDaemonVersion);
expect(screen.getByTitle("扩展版本").textContent).toBe(`Ext ${EXTENSION_VERSION}`);
expect(screen.getByTitle("bsk 版本").textContent).toBe(`CLI ${mockDaemonVersion}`);
expect(screen.getByText("03c3e47f")).toBeTruthy();
const copyButton = screen.getByRole("button", { name: "复制实例 ID" });
+2 -2
View File
@@ -267,9 +267,9 @@ export function App() {
data-slot="popup-meta"
>
<div className="flex shrink-0 items-center gap-1">
<span title={t("popup.extensionVersionHint")}>{extensionVersion}</span>
<span title={t("popup.daemonVersionHint")}>CLI {daemonVersion}</span>
<span aria-hidden>/</span>
<span title={t("popup.daemonVersionHint")}>{daemonVersion}</span>
<span title={t("popup.extensionVersionHint")}>Ext {extensionVersion}</span>
</div>
<div className="flex min-w-0 items-center justify-end gap-1">
<span className="shrink-0">{t("popup.instanceTitle")}</span>
@@ -1,7 +1,7 @@
{
"name": "@wxg-prc-cpg/browser-skill-dsh-plugin",
"description": "DeepSeek Harness tool plugin that exposes BrowserSkill browser automation (browser_* tools) to the model",
"version": "0.1.2",
"version": "0.2.0",
"license": "MIT",
"publishConfig": {
"access": "public",
+299
View File
@@ -0,0 +1,299 @@
#!/usr/bin/env node
/**
* Unified release script for BrowserSkill.
*
* Bumps CLI, Extension, and DSH Plugin to the same version, updates
* CHANGELOG.md, commits, tags, and optionally pushes.
*
* Usage:
* node scripts/release.mjs <version> # bump + commit + tag
* node scripts/release.mjs <version> --dry-run # preview changes only
* node scripts/release.mjs <version> --push # also push to origin
* node scripts/release.mjs <version> --push --only ext # push only ext tag
* node scripts/release.mjs <version> --push --only cli,dsh # push only cli + dsh tags
* node scripts/release.mjs --push-tags --only ext # push tag(s) for an already-committed release
*
* Options:
* --dry-run Show what would change without writing anything
* --push After commit+tag, push the branch and all release tags
* --push-tags Push release tags only (skip version bump; useful for staged rollout)
* --only <list> Comma-separated subset of: cli, ext, dsh (default: all three)
* --no-commit Write files but skip git commit and tagging
*/
import { execSync } from "node:child_process";
import { readFileSync, writeFileSync } from "node:fs";
import { parseArgs } from "node:util";
const TAG_PREFIX = { cli: "cli-v", ext: "ext-v", dsh: "dsh-plugin-v" };
const ALL_COMPONENTS = ["cli", "ext", "dsh"];
const VERSION_FILES = {
cargo: "Cargo.toml",
extension: "apps/extension/package.json",
dshPlugin: "packages/dsh-plugin-browserskill/package.json",
};
// ---------------------------------------------------------------------------
// CLI argument parsing
// ---------------------------------------------------------------------------
const { values, positionals } = parseArgs({
allowPositionals: true,
options: {
"dry-run": { type: "boolean", default: false },
push: { type: "boolean", default: false },
"push-tags": { type: "boolean", default: false },
"no-commit": { type: "boolean", default: false },
only: { type: "string", default: "" },
help: { type: "boolean", short: "h", default: false },
},
});
if (values.help) {
console.log(
readFileSync(new URL(import.meta.url), "utf8").match(/\/\*\*([\s\S]*?)\*\//)?.[1] ?? "",
);
process.exit(0);
}
const dryRun = values["dry-run"];
const pushAfter = values.push;
const pushTagsOnly = values["push-tags"];
const noCommit = values["no-commit"];
const onlyRaw = values.only;
const components = onlyRaw ? onlyRaw.split(",").map((s) => s.trim().toLowerCase()) : ALL_COMPONENTS;
for (const c of components) {
if (!ALL_COMPONENTS.includes(c)) {
console.error(`Unknown component "${c}". Valid: ${ALL_COMPONENTS.join(", ")}`);
process.exit(1);
}
}
// ---------------------------------------------------------------------------
// Push-tags-only mode (no version bump)
// ---------------------------------------------------------------------------
if (pushTagsOnly) {
const currentVersion = readCurrentVersions();
const ver = positionals[0] ?? currentVersion.cargo;
for (const c of components) {
const tag = `${TAG_PREFIX[c]}${ver}`;
console.log(` pushing tag ${tag}`);
if (!dryRun) {
run(`git push origin ${tag}`);
}
}
console.log("\nDone.");
process.exit(0);
}
// ---------------------------------------------------------------------------
// Normal mode — require version argument
// ---------------------------------------------------------------------------
const newVersion = positionals[0];
if (!newVersion) {
console.error(
"Usage: node scripts/release.mjs <version> [--dry-run] [--push] [--only cli,ext,dsh]",
);
process.exit(1);
}
if (!/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(newVersion)) {
console.error(`Invalid semver: "${newVersion}"`);
process.exit(1);
}
// ---------------------------------------------------------------------------
// Read current versions
// ---------------------------------------------------------------------------
function readCurrentVersions() {
const cargo = readFileSync(VERSION_FILES.cargo, "utf8");
const cargoVer =
cargo.match(/^\[workspace\.package\]\s*\nversion\s*=\s*"([^"]+)"/m)?.[1] ??
cargo.match(/^version\s*=\s*"([^"]+)"/m)?.[1];
const extPkg = JSON.parse(readFileSync(VERSION_FILES.extension, "utf8"));
const dshPkg = JSON.parse(readFileSync(VERSION_FILES.dshPlugin, "utf8"));
return { cargo: cargoVer, extension: extPkg.version, dshPlugin: dshPkg.version };
}
const current = readCurrentVersions();
console.log("Current versions:");
console.log(` CLI/Daemon (Cargo.toml): ${current.cargo}`);
console.log(` Extension (package.json): ${current.extension}`);
console.log(` DSH Plugin (package.json): ${current.dshPlugin}`);
console.log(`\nBumping all → ${newVersion}`);
if (dryRun) console.log(" (dry-run mode — no files will be written)\n");
// ---------------------------------------------------------------------------
// Bump Cargo.toml
// ---------------------------------------------------------------------------
function bumpCargo(version) {
let content = readFileSync(VERSION_FILES.cargo, "utf8");
// workspace.package version
content = content.replace(
/^(\[workspace\.package\]\s*\nversion\s*=\s*")([^"]+)(")/m,
`$1${version}$3`,
);
// workspace.dependencies.bsk-protocol version (for cargo publish)
content = content.replace(
/(bsk-protocol\s*=\s*\{\s*version\s*=\s*")([^"]+)(")/,
`$1${version}$3`,
);
return content;
}
function bumpPackageJson(filePath, version) {
const pkg = JSON.parse(readFileSync(filePath, "utf8"));
pkg.version = version;
return JSON.stringify(pkg, null, 2) + "\n";
}
// ---------------------------------------------------------------------------
// Update CHANGELOG.md
// ---------------------------------------------------------------------------
function updateChangelog(version) {
const changelogPath = "CHANGELOG.md";
let content;
try {
content = readFileSync(changelogPath, "utf8");
} catch {
return null;
}
const today = new Date().toISOString().slice(0, 10);
const escapedVer = version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
// First: fill in any date-placeholder line for this version (e.g. "## [0.2.0] - 2026-09-XX")
const versionPlaceholder = new RegExp(`^## \\[${escapedVer}\\]\\s*-\\s*\\d{4}-\\d{2}-XX`, "m");
if (versionPlaceholder.test(content)) {
content = content.replace(versionPlaceholder, `## [${version}] - ${today}`);
} else {
// Otherwise: insert a new section after [Unreleased]
const unreleased = /^## \[Unreleased\]/m;
if (unreleased.test(content)) {
content = content.replace(unreleased, `## [Unreleased]\n\n## [${version}] - ${today}`);
}
}
return { path: changelogPath, content };
}
// ---------------------------------------------------------------------------
// Write files
// ---------------------------------------------------------------------------
const writes = [
{ path: VERSION_FILES.cargo, content: bumpCargo(newVersion), label: "Cargo.toml" },
{
path: VERSION_FILES.extension,
content: bumpPackageJson(VERSION_FILES.extension, newVersion),
label: "Extension package.json",
},
{
path: VERSION_FILES.dshPlugin,
content: bumpPackageJson(VERSION_FILES.dshPlugin, newVersion),
label: "DSH Plugin package.json",
},
];
const changelogUpdate = updateChangelog(newVersion);
if (changelogUpdate) {
writes.push({
path: changelogUpdate.path,
content: changelogUpdate.content,
label: "CHANGELOG.md",
});
}
for (const w of writes) {
console.log(` ✓ ${w.label}`);
if (!dryRun) {
writeFileSync(w.path, w.content);
}
}
// ---------------------------------------------------------------------------
// Update Cargo.lock (cargo check triggers it)
// ---------------------------------------------------------------------------
if (!dryRun) {
console.log("\n Updating Cargo.lock...");
run("cargo check --workspace 2>&1 || true");
}
// ---------------------------------------------------------------------------
// Git commit + tag
// ---------------------------------------------------------------------------
if (noCommit || dryRun) {
if (dryRun) {
console.log(`\nWould commit: "chore(release): ${newVersion}"`);
console.log("Would create tags:");
for (const c of components) {
console.log(` ${TAG_PREFIX[c]}${newVersion}`);
}
}
console.log("\nDone.");
process.exit(0);
}
console.log("\nCommitting...");
run(
"git add Cargo.toml Cargo.lock apps/extension/package.json packages/dsh-plugin-browserskill/package.json CHANGELOG.md 2>/dev/null || true",
);
run(`git commit -m "chore(release): ${newVersion}"`);
console.log("Tagging...");
for (const c of components) {
const tag = `${TAG_PREFIX[c]}${newVersion}`;
run(`git tag ${tag}`);
console.log(` ✓ ${tag}`);
}
// ---------------------------------------------------------------------------
// Push (optional)
// ---------------------------------------------------------------------------
if (pushAfter) {
console.log("\nPushing...");
run("git push origin HEAD");
for (const c of components) {
const tag = `${TAG_PREFIX[c]}${newVersion}`;
console.log(` pushing tag ${tag}`);
run(`git push origin ${tag}`);
}
}
console.log(`\nRelease ${newVersion} prepared successfully.`);
if (!pushAfter) {
console.log("\nNext steps:");
console.log(" # Push branch");
console.log(" git push origin main");
console.log("");
console.log(" # Push extension tag first (store review takes time)");
console.log(` git push origin ext-v${newVersion}`);
console.log("");
console.log(" # Then push CLI and DSH plugin tags");
console.log(` git push origin cli-v${newVersion}`);
console.log(` git push origin dsh-plugin-v${newVersion}`);
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function run(cmd) {
execSync(cmd, { stdio: "inherit" });
}