diff --git a/cli/package.json b/cli/package.json index a7a6cb1..9b3817d 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@bproxy/cli", - "version": "0.9.0", + "version": "0.9.1", "private": true, "type": "module", "bin": { diff --git a/docs/internal/plans/phases/11-extension-popup-compliance.md b/docs/internal/plans/phases/11-extension-popup-compliance.md new file mode 100644 index 0000000..a1a9a28 --- /dev/null +++ b/docs/internal/plans/phases/11-extension-popup-compliance.md @@ -0,0 +1,176 @@ +--- +title: "Phase 11: Extension popup compliance and presentation" +status: complete +date: 2026-06-23 +--- + +## Phase 11: Extension popup compliance and presentation + +**Motivation:** The extension popup was functional but too minimal for first-run clarity and Chrome Web Store review. It needed to explain what bproxy is, show version/protocol metadata, link to docs and privacy information, and present pairing state more clearly without turning the popup into a dashboard. + +**Source decisions:** + +- [ADR-001](../../decisions.md#adr-001-default-instrumentation-strategy--read-mode) — programmatic injection only +- [ADR-011](../../decisions.md#adr-011-extension-token-bootstrap-via-popup-driven-pairing) — popup-driven pairing +- [ADR-016](../../decisions.md#adr-016-web_accessible_resources-default-deny) — no WAR by default +- [ADR-017](../../decisions.md#adr-017-sensoractuator-boundary) — popup remains a thin pairing surface, not a dashboard +- [ADR-024](../../decisions.md#adr-024-no-arbitrary-page-eval-and-no-scroll-target-inference) — no extra control/eval surface +- [ADR-025](../../decisions.md#adr-025-security-scanner-findings-are-remediated-in-code) — compliance changes land in code/tests + +--- + +## Scope shipped + +| # | Deliverable | Outcome | +|---|-------------|---------| +| 1 | Popup markup/style redesign | Shipped | +| 2 | Version and protocol metadata rendering | Shipped | +| 3 | Footer links (docs, license, credits) | Shipped | +| 4 | Paired/unpaired state presentation | Shipped | +| 5 | Privacy policy page for CWS | Shipped | +| 6 | Store listing manifest description | Shipped | +| 7 | Popup test updates | Shipped | + +Out of scope remained unchanged: +- No session counts, tab counts, recent activity, or operator dashboard UI +- No options page +- No new permissions or `web_accessible_resources` +- No strategy, selector repair, retries, or fallback chains in the popup + +--- + +## Major changes + +### Popup UI rewrite + +`extension/src/entrypoints/popup/index.html` was redesigned into a compact single-column utility layout with: +- inline cable icon + product name +- subtitle: `Human-in-the-loop browser bridge for AI agents.` +- explicit pairing status line above the form +- pairing help text +- footer metadata and outbound links + +The popup stayed fully static in HTML/CSS for presentation: +- inline styles only +- no external assets +- native form semantics preserved +- no new runtime permissions or WAR entries + +### Pairing-state presentation + +`extension/src/entrypoints/popup/main.ts` now: +- reads local bootstrap state from `chrome.storage.local` +- renders `Not paired` vs `Paired with local daemon` +- shows a gray or green status dot +- updates status immediately after successful pairing + +Important honesty rule preserved: "Paired" means a valid local bootstrap record exists; it does not claim the daemon is currently reachable. + +### Version and protocol metadata + +The popup now renders: +- `Extension ${VERSION}` +- `Protocol ${PROTOCOL_VERSION}` + +Both values come from `@bproxy/shared`. + +### Footer links and attribution + +The popup footer now includes static external links to: +- documentation +- privacy page +- MIT license +- GitHub repository (icon link) + +Attribution is shown as plain text: `Created by Dim Kharitonov`. + +### Re-pair UX follow-up + +After manual review, the paired-state submit button was clarified: +- unpaired: `Pair extension` +- paired: `Re-pair with new code` + +This matches actual architecture behavior: entering a new valid pairing code replaces the stored bootstrap and reconnects the background worker with a fresh extension token. + +### Manual presentation tweak after review + +After loading the built extension in Chrome, popup spacing was increased to improve visual separation from the page behind it: +- outer padding increased +- major section spacing increased + +This was a presentation-only adjustment; no behavioral changes. + +### Manifest and privacy materials + +- `extension/wxt.config.ts` manifest description was updated to CWS-safe wording: + - `Companion extension for bproxy. Connects Chrome to a local bproxy daemon for explicit human-in-the-loop browser actions.` +- `docs/public/privacy.md` was rewritten as a short factual privacy policy page for `/privacy/` + +### Tests + +Added `extension/src/entrypoints/popup/__tests__/presentation.test.ts` to cover: +- title/subtitle/copy +- footer metadata and links +- attribution and GitHub link +- paired/unpaired view-model behavior +- version/protocol rendering fallback behavior + +Existing pairing-flow tests remained intact. + +--- + +## Files changed + +| Package | File | +|---|---| +| extension | `src/entrypoints/popup/index.html` | +| extension | `src/entrypoints/popup/main.ts` | +| extension | `src/entrypoints/popup/__tests__/presentation.test.ts` | +| extension | `wxt.config.ts` | +| docs/public | `privacy.md` | + +No changes were required in `shared/`, `service/`, `cli/`, the background SW, content scripts, or the protocol wire shape. + +--- + +## Shipped outcome + +The popup now presents bproxy as a clear pairing utility instead of a bare form: +- product identity and purpose are visible immediately +- pairing state is visible immediately +- version/protocol metadata is available for support/debugging +- documentation/privacy/license/credits are one click away +- paired users see `Re-pair with new code`, which matches the actual token-rotation flow + +This improved first-run clarity and CWS readiness without expanding the popup into an operator surface or violating the extension's sensor/actuator boundary. + +--- + +## Validation + +Validated during implementation: +- `pnpm --filter @bproxy/extension typecheck` +- `pnpm --filter @bproxy/extension test` +- `pnpm docs:build` +- `pnpm check` +- `pnpm --filter @bproxy/extension build` + +Manual verification in Chrome confirmed: +- updated layout and spacing render correctly +- paired/unpaired states display correctly +- successful pairing updates the status line +- paired state shows `Re-pair with new code` +- footer links open correctly + +--- + +## Deferred / rejected + +| Item | Reason | +|---|---| +| Session/tab/activity dashboard in popup | Rejected for this phase; daemon remains the authority and popup is not an operator dashboard | +| WebSocket live connection state in popup | Deferred; badge already covers transport state | +| Options page | Explicitly out of scope | +| Store icons/screenshots/promo assets | Deferred; non-code store assets | +| Dark mode | Deferred; no requirement for this increment | +| Localization | Deferred; English-only for now | diff --git a/docs/internal/solution/extension-popup-compliance.md b/docs/internal/solution/extension-popup-compliance.md new file mode 100644 index 0000000..cdb9889 --- /dev/null +++ b/docs/internal/solution/extension-popup-compliance.md @@ -0,0 +1,249 @@ +--- +title: Extension Popup Compliance and Presentation +--- + +Solution note for making the bproxy Chrome extension popup clearer, more transparent, and easier to review against Chrome Web Store expectations. + +**Decisions that constrain this:** [ADR-001](../decisions.md#adr-001-default-instrumentation-strategy--read-mode) (programmatic injection only), [ADR-011](../decisions.md#adr-011-extension-token-bootstrap-via-popup-driven-pairing) (popup-driven pairing), [ADR-016](../decisions.md#adr-016-web_accessible_resources-default-deny) (no WAR by default), [ADR-017](../decisions.md#adr-017-sensoractuator-boundary) (extension is sensor/actuator only), [ADR-024](../decisions.md#adr-024-no-arbitrary-page-eval-and-no-scroll-target-inference) (no eval / no scroll inference), and [ADR-025](../decisions.md#adr-025-security-scanner-findings-are-remediated-in-code) (security findings remediated in code). + +## Inputs + +- Current popup screenshots show a minimal pairing form with title, pairing-code input, pair button, and one status sentence. It is functional, but too terse for a user or reviewer to understand scope, authorship, version compatibility, license, or where to read more. +- Current extension spec: [public extension solution](../../public/solution/extension.md). +- Current public documentation URL: . +- Current root package metadata: version `0.9.0`, license `MIT`, description `Human-in-the-loop browser proxy for AI agents.` +- Current protocol version source: `shared/src/version.ts` exports `PROTOCOL_VERSION = 2`. +- Current license file: [`LICENSE`](../../../LICENSE). User-facing credits use `Dim Kharitonov`. + +## Chrome Web Store requirements observed + +Chrome's policy pages use five themes that apply directly to this popup and listing. + +| Theme | Requirement | Source | +|---|---|---| +| Be honest | Extension functionality should be clearly disclosed to users, with no surprises. | [Program Policies — Chrome Web Store Principles](https://developer.chrome.com/docs/webstore/program-policies) | +| Be useful / quality | Extensions should have a narrow, understandable purpose and a respectful user experience. Persistent UI should help the current task and avoid distraction. | [Quality guidelines](https://developer.chrome.com/docs/webstore/program-policies/quality-guidelines) | +| Minimum functionality | Extension must provide real utility, not just launch/link to another site. | [Minimum Functionality](https://developer.chrome.com/docs/webstore/program-policies/minimum-functionality) | +| Permission minimality | Request the narrowest permissions needed for implemented features; do not future-proof permissions. | [Use of Permissions](https://developer.chrome.com/docs/webstore/program-policies/permissions) | +| Data transparency and handling | Disclose collection/use/sharing of user data, and handle auth/user data securely. | [Disclosure Requirements](https://developer.chrome.com/docs/webstore/program-policies/disclosure-requirements), [Handling Requirements](https://developer.chrome.com/docs/webstore/program-policies/data-handling) | +| Limited Use | Use of collected data must be limited to the disclosed single purpose. No transfer to third parties except for providing/improving that purpose, legal compliance, or security. An affirmative compliance statement must appear on a website belonging to the extension — but this applies specifically to extensions that access Google user account data via OAuth/Google APIs. Extensions that only use Chrome platform APIs (tabs, scripting, storage, etc.) for local functionality and do not collect or transmit user data are not subject to this requirement. | [Program Policies — Limited Use](https://developer.chrome.com/docs/webstore/program-policies/policies#limited_use) | +| Listing requirements (mandatory) | Submissions missing a description, icon, or screenshots are **rejected**. Privacy field declarations must be accurate and consistent with the privacy policy and actual extension behavior. | [Program Policies — Listing Requirements](https://developer.chrome.com/docs/webstore/program-policies/policies#listing_requirements) | +| Listing metadata (quality) | Provide a detailed description, graphics, homepage URL, support URL, and content rating/details. | [Complete your listing information](https://developer.chrome.com/docs/webstore/cws-dashboard-listing), [Creating a great listing page](https://developer.chrome.com/docs/webstore/best-listing) | +| Code readability (MV3) | Full functionality must be easily discernible from submitted code. No obfuscation, no remote logic execution. Minification is allowed. | [Program Policies — Code Readability Requirements](https://developer.chrome.com/docs/webstore/program-policies/policies#code_readability_requirements) | + +## Presentation goal + +The popup should answer four questions immediately: + +1. **What is this?** The Chrome-side piece of bproxy: a local, human-in-the-loop browser bridge for AI agents and agentic systems. +2. **What does it do?** Pairs this Chrome profile with the local bproxy daemon so an agent can request explicit browser actions through the user's real browser session. +3. **Who made it and under what terms?** Created by Dim Kharitonov, MIT licensed. +4. **Where can I verify details?** Documentation, license, and credits links. + +Avoid decorative product-dashboard language. Do **not** introduce badges, chips, gradients, icon clutter, or trend-driven UI elements. The popup should feel like a small browser utility: restrained, readable, explicit. + +## Proposed popup content + +```text +bproxy +Human-in-the-loop browser bridge for AI agents. + +Status: Not paired + +Pairing code +[ VQE7-KNBM ] +[ Pair extension ] + +Run `bproxy service start` and paste the one-time code shown by the daemon. + +Extension 0.9.0 · Protocol 2 +Documentation · License · Credits +Created by Dim Kharitonov · MIT +``` + +When paired: + +```text +Status: Paired with local daemon +``` + +When pairing fails, preserve the existing specific error messages and codes, but keep them below the form and use plain text color treatment only. + +## Required metadata + +The popup should render these values from source-of-truth constants where practical: + +| Field | Value / source | Notes | +|---|---|---| +| Extension version | `VERSION` from `@bproxy/shared` or WXT/manifest package version | Must match release package version. | +| Protocol version | `PROTOCOL_VERSION` from `@bproxy/shared` | Useful for daemon/extension mismatch support. | +| Documentation | `https://dimdasci.github.io/bproxy/` | Use a normal external link. | +| License | MIT, linked to repository license or docs license page | Link target can be GitHub `LICENSE` until a docs page exists. | +| Credits | Created by Dim Kharitonov | Link to repository or credits section/page. | + +## Copy requirements + +Use copy that is transparent without over-explaining internals: + +- Product subtitle: `Human-in-the-loop browser bridge for AI agents.` +- Purpose text: `This extension pairs Chrome with your local bproxy daemon so AI agents and agentic systems can request explicit browser actions while you stay in control.` +- Human-control sentence: `You handle logins, CAPTCHAs, consent screens, and final submits.` +- Pairing help: `Run bproxy service start and paste the one-time code shown by the daemon.` +- Success text: `Paired with local daemon. You can close this popup.` +- Footer: `Created by Dim Kharitonov · MIT` + +Avoid claims such as "undetectable", "bypass", "stealth", "anti-bot", or anything suggesting circumvention. The public framing should remain human-in-the-loop browser control, not evasion marketing. + +## Layout requirements + +- Use one compact column, about `340px` to `380px` wide. +- Keep native form semantics: real `form`, `label`, `input`, `button`, and `output aria-live="polite"`. +- Use text hierarchy, spacing, and a thin divider for clarity. +- Links live in a simple footer: `Documentation · License · Credits`. +- No badge/chip components. +- No external fonts or remote assets. +- No options page is required for this increment. + +## Compliance notes for manifest and listing + +The popup cannot carry all compliance burden. Store listing and privacy fields should also disclose: + +- bproxy is a companion extension for a local daemon/CLI. +- It can read page text/structure and perform explicit browser actions only after pairing. +- It stores pairing bootstrap material in Chrome extension storage. +- It communicates with `ws://127.0.0.1:9615/ws` / `http://127.0.0.1:9615/pair/claim` only for local daemon pairing/control. +- Broad host access exists because the user/agent may work on arbitrary pages; this must be justified in listing/privacy text. +- Permission rationale: + - `tabs`: route actions to tabs and manage user-requested tab actions. + - `scripting`: programmatically inject the isolated content script on first command. + - `webNavigation`: track top-level navigation state for session/page identity. + - `alarms`: background service-worker keepalive/reconnect scheduling. + - `storage`: persist bootstrap token and bounded session diagnostics. + - ``: support user-selected pages across the web. + +### Limited Use — does not apply to bproxy + +The Limited Use affirmative statement requirement applies to extensions that access Google user account data via OAuth/Google service APIs (Gmail, Drive, Calendar, etc.). bproxy does not use any Google account APIs. It uses only Chrome extension platform APIs (`tabs`, `scripting`, `webNavigation`, `storage`, `alarms`) for local functionality. + +The extension does not collect user data: + +- Page content is read on demand and passed to a localhost daemon on the same machine — nothing leaves the user's device. +- The only persisted value is the pairing bootstrap token in `chrome.storage.local`. +- No browsing history, page content, or user activity is stored or transmitted externally. + +In the CWS privacy fields, the correct declaration is: **"This extension does not collect user data."** No Limited Use disclosure page is needed. + +### Privacy policy page + +The CWS dashboard requires a privacy policy URL. The page should be published on the docs site at `https://dimdasci.github.io/bproxy/privacy/` and state in plain English: + +- The extension does not collect, store, or transmit any user data. +- It reads page content only when an agent sends a command, and passes it to a daemon running on the same machine (`127.0.0.1`). +- Nothing leaves the user's device. There is no remote server, no analytics, no telemetry. +- The only value the extension persists is a pairing token in Chrome's local storage, used to authenticate the connection to the local daemon. +- The extension communicates exclusively with `localhost:9615`. It never contacts any external service. + +Keep it short. No legal boilerplate. No "we may update this policy" padding. Just the facts about how it works and why there is nothing to disclose. + +### Listing mandatory assets + +The store will **reject** submissions that are missing any of: + +- A non-blank detailed description. +- A 128×128 px store icon. +- At least one 1280×800 px (or 640×400 px) screenshot. + +Screenshots should show the popup in both unpaired and paired states. A third screenshot may show a terminal with `bproxy service start` output to illustrate the pairing flow. + +### Privacy fields consistency + +The CWS developer dashboard privacy fields must: + +- Accurately declare what user data is collected (page text/structure read on demand, pairing token stored locally). +- State that data is not transferred to any third party. +- Be consistent with the privacy policy text and actual extension behavior. + +If privacy field declarations contradict the privacy policy or observed behavior, the extension may be removed from the store. + +### Code readability (MV3) + +bproxy already satisfies this requirement: + +- Source maps are preserved in production output (`sourcemap: true` in `wxt.config.ts`). +- No obfuscation; code is minified only. +- No remote logic execution — all functionality is self-contained in the extension package. +- No `eval()`, no ` -

bproxy pairing

-
- - - -
- +
+

+ + bproxy +

+

Human-in-the-loop browser bridge for AI agents.

+

+
+ +
+
+ + + +
+

Run bproxy service start and paste the one-time code shown by the daemon.

+ +
+ + diff --git a/extension/src/entrypoints/popup/main.ts b/extension/src/entrypoints/popup/main.ts index 25df41a..14dcde1 100644 --- a/extension/src/entrypoints/popup/main.ts +++ b/extension/src/entrypoints/popup/main.ts @@ -1,4 +1,5 @@ -import { bootstrapItem } from "../../background/storage"; +import { PROTOCOL_VERSION, VERSION } from "@bproxy/shared"; +import { bootstrapItem, type PairingBootstrap } from "../../background/storage"; import { type PairingErrorCode, type PairingResult, runPairing } from "./pairing"; // Thin DOM wiring for the popup. Real flow logic lives in small helpers: @@ -23,6 +24,17 @@ const STATUS_FRIENDLY: Record = { PAIR_NOTIFY_FAILED: "Paired, but failed to wake the background worker. Try reopening the popup.", }; +export interface ConnectionStatusViewModel { + text: string; + tone: "muted" | "ok"; + submitLabel: string; +} + +interface PopupInitDeps { + storage: { getValue(): Promise }; + now: () => number; +} + function $(id: string): T { const el = document.getElementById(id); if (!el) throw new Error(`#${id} not found in popup DOM`); @@ -30,14 +42,84 @@ function $(id: string): T { } function setStatus(state: "idle" | "pending" | "success" | "error", text: string): void { - const status = $("status"); + const status = $("status"); status.dataset["state"] = state; status.textContent = text; } +export function formatVersionInfo( + version: unknown = VERSION, + protocolVersion: unknown = PROTOCOL_VERSION, +): string { + const versionText = typeof version === "string" ? version : ""; + const protocolText = + typeof protocolVersion === "number" || typeof protocolVersion === "string" + ? String(protocolVersion) + : ""; + const extensionPart = `Extension ${versionText}`.trimEnd(); + const protocolPart = `Protocol ${protocolText}`.trimEnd(); + return `${extensionPart} · ${protocolPart}`; +} + +export function getConnectionStatusViewModel( + bootstrap: PairingBootstrap | null, + now: number, +): ConnectionStatusViewModel { + if ( + bootstrap && + typeof bootstrap.extensionToken === "string" && + bootstrap.extensionToken.length > 0 && + typeof bootstrap.expiresAt === "number" && + bootstrap.expiresAt > now + ) { + return { + text: "Paired with local daemon", + tone: "ok", + submitLabel: "Re-pair with new code", + }; + } + return { + text: "Not paired", + tone: "muted", + submitLabel: "Pair extension", + }; +} + +function renderVersionInfo(text: string): void { + $("version-info").textContent = text; +} + +function createStatusDot(tone: ConnectionStatusViewModel["tone"]): SVGSVGElement { + const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); + svg.setAttribute("aria-hidden", "true"); + svg.setAttribute("width", "8"); + svg.setAttribute("height", "8"); + svg.setAttribute("viewBox", "0 0 8 8"); + + const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle"); + circle.setAttribute("cx", "4"); + circle.setAttribute("cy", "4"); + circle.setAttribute("r", "4"); + circle.setAttribute("fill", tone === "ok" ? "var(--c-ok)" : "var(--c-muted)"); + svg.append(circle); + + return svg; +} + +function renderConnectionStatus(model: ConnectionStatusViewModel): void { + const status = $("connection-status"); + status.replaceChildren(createStatusDot(model.tone), document.createTextNode(model.text)); + $("submit").textContent = model.submitLabel; +} + function renderResult(result: PairingResult): void { if (result.ok) { - setStatus("success", "Paired. You can close this popup."); + renderConnectionStatus({ + text: "Paired with local daemon", + tone: "ok", + submitLabel: "Re-pair with new code", + }); + setStatus("success", "Paired with local daemon. You can close this popup."); return; } const friendly = STATUS_FRIENDLY[result.code]; @@ -75,9 +157,23 @@ async function onSubmit(ev: SubmitEvent): Promise { } } -document.addEventListener("DOMContentLoaded", () => { - $("pair-form").addEventListener("submit", (ev) => { - void onSubmit(ev); +export async function initializePopup( + deps: PopupInitDeps = { + storage: bootstrapItem, + now: () => Date.now(), + }, +): Promise { + renderVersionInfo(formatVersionInfo()); + const bootstrap = await deps.storage.getValue().catch(() => null); + renderConnectionStatus(getConnectionStatusViewModel(bootstrap, deps.now())); + setStatus("idle", ""); +} + +if (typeof document !== "undefined") { + document.addEventListener("DOMContentLoaded", () => { + $("pair-form").addEventListener("submit", (ev) => { + void onSubmit(ev); + }); + void initializePopup(); }); - setStatus("idle", "Enter the one-time code issued by the daemon."); -}); +} diff --git a/extension/wxt.config.ts b/extension/wxt.config.ts index 91b7f9b..6e797c2 100644 --- a/extension/wxt.config.ts +++ b/extension/wxt.config.ts @@ -24,7 +24,8 @@ export default defineConfig({ }), manifest: { name: "bproxy", - description: "Browser proxy companion extension for bproxy daemon.", + description: + "Companion extension for bproxy. Connects Chrome to a local bproxy daemon for explicit human-in-the-loop browser actions.", permissions: ["tabs", "scripting", "webNavigation", "alarms", "storage"], host_permissions: [""], action: { diff --git a/package.json b/package.json index 21debc4..5db23f6 100644 --- a/package.json +++ b/package.json @@ -2,8 +2,8 @@ "name": "bproxy", "private": true, "type": "module", - "version": "0.9.0", - "description": "Browser proxy for code agents.", + "version": "0.9.1", + "description": "Human-in-the-loop browser proxy for AI agents.", "license": "MIT", "packageManager": "pnpm@9.15.0", "scripts": { diff --git a/scripts/release-package.json b/scripts/release-package.json index 3d2c56e..44a1b1f 100644 --- a/scripts/release-package.json +++ b/scripts/release-package.json @@ -1,7 +1,7 @@ { "name": "@dimdasci/bproxy", "version": "VERSION_PLACEHOLDER", - "description": "Browser proxy for code agents — control your real Chrome from CLI.", + "description": "Human-in-the-loop browser proxy for AI agents — use your real Chrome session via a localhost daemon and extension.", "license": "MIT", "homepage": "https://dimdasci.github.io/bproxy/", "bugs": { diff --git a/service/package.json b/service/package.json index 0fa3973..6bc1fc1 100644 --- a/service/package.json +++ b/service/package.json @@ -1,6 +1,6 @@ { "name": "@bproxy/service", - "version": "0.9.0", + "version": "0.9.1", "private": true, "type": "module", "bin": { diff --git a/shared/package.json b/shared/package.json index ffbfb63..8a68743 100644 --- a/shared/package.json +++ b/shared/package.json @@ -1,6 +1,6 @@ { "name": "@bproxy/shared", - "version": "0.9.0", + "version": "0.9.1", "private": true, "type": "module", "exports": { diff --git a/shared/src/version.ts b/shared/src/version.ts index 73029a6..4eaa82c 100644 --- a/shared/src/version.ts +++ b/shared/src/version.ts @@ -6,7 +6,7 @@ */ /** Current bproxy package version (semver). */ -export const VERSION = "0.9.0"; +export const VERSION = "0.9.1"; /** Protocol version for the daemon↔CLI↔extension wire format. */ export const PROTOCOL_VERSION = 2; diff --git a/skills/bproxy/SKILL.md b/skills/bproxy/SKILL.md index 426180c..4b91477 100644 --- a/skills/bproxy/SKILL.md +++ b/skills/bproxy/SKILL.md @@ -9,7 +9,7 @@ description: >- compatibility: Node >=24, bproxy installed (npm install -g @dimdasci/bproxy), daemon running, extension paired license: MIT metadata: - version: "0.9.0" + version: "0.9.1" --- # bproxy diff --git a/views/astro.config.mjs b/views/astro.config.mjs index ca5fc53..282fdc7 100644 --- a/views/astro.config.mjs +++ b/views/astro.config.mjs @@ -144,6 +144,7 @@ export default defineConfig({ }, ], components: { + Footer: "./src/components/Footer.astro", PageFrame: "./src/components/PageFrame.astro", }, customCss: ["./src/styles/custom.css"], diff --git a/views/package.json b/views/package.json index 4a323eb..55815f6 100644 --- a/views/package.json +++ b/views/package.json @@ -1,7 +1,7 @@ { "name": "@bproxy/views", "private": true, - "version": "0.9.0", + "version": "0.9.1", "type": "module", "scripts": { "dev": "astro dev", diff --git a/views/public/assets/bproxy-hero.png b/views/public/assets/bproxy-hero.png new file mode 120000 index 0000000..e2e8968 --- /dev/null +++ b/views/public/assets/bproxy-hero.png @@ -0,0 +1 @@ +../../../assets/bproxy-hero.png \ No newline at end of file diff --git a/views/src/components/Footer.astro b/views/src/components/Footer.astro new file mode 100644 index 0000000..d86919e --- /dev/null +++ b/views/src/components/Footer.astro @@ -0,0 +1,33 @@ +--- +import DefaultFooter from "@astrojs/starlight/components/Footer.astro"; +import pkg from "../../package.json"; + +const version = pkg.version; +--- + + + +

+ bproxy v{version} · + + MIT License + + · © Dim Kharitonov +

+ +