diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..a7d54f5 --- /dev/null +++ b/CHANGELOG.md @@ -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. diff --git a/Cargo.lock b/Cargo.lock index eeb978b..d4ff61c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", diff --git a/Cargo.toml b/Cargo.toml index e123726..78ee9cd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/apps/extension/package.json b/apps/extension/package.json index 546a019..fdd9d4d 100644 --- a/apps/extension/package.json +++ b/apps/extension/package.json @@ -1,6 +1,6 @@ { "name": "@browser-skill/extension", - "version": "0.1.7", + "version": "0.2.0", "private": true, "type": "module", "scripts": { diff --git a/apps/extension/src/entrypoints/popup/App.test.tsx b/apps/extension/src/entrypoints/popup/App.test.tsx index daec637..2d71235 100644 --- a/apps/extension/src/entrypoints/popup/App.test.tsx +++ b/apps/extension/src/entrypoints/popup/App.test.tsx @@ -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" }); diff --git a/apps/extension/src/entrypoints/popup/App.tsx b/apps/extension/src/entrypoints/popup/App.tsx index 0756e9c..cc9287b 100644 --- a/apps/extension/src/entrypoints/popup/App.tsx +++ b/apps/extension/src/entrypoints/popup/App.tsx @@ -267,9 +267,9 @@ export function App() { data-slot="popup-meta" >
- {extensionVersion} + CLI {daemonVersion} / - {daemonVersion} + Ext {extensionVersion}
{t("popup.instanceTitle")} diff --git a/packages/dsh-plugin-browserskill/package.json b/packages/dsh-plugin-browserskill/package.json index b1ebf92..427b435 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 browser automation (browser_* tools) to the model", - "version": "0.1.2", + "version": "0.2.0", "license": "MIT", "publishConfig": { "access": "public", diff --git a/scripts/release.mjs b/scripts/release.mjs new file mode 100755 index 0000000..f7a6e95 --- /dev/null +++ b/scripts/release.mjs @@ -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 # bump + commit + tag + * node scripts/release.mjs --dry-run # preview changes only + * node scripts/release.mjs --push # also push to origin + * node scripts/release.mjs --push --only ext # push only ext tag + * node scripts/release.mjs --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 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 [--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" }); +}