From 2a0b17116373284628ae398a7c46cd2b29b27222 Mon Sep 17 00:00:00 2001 From: Lakshman Turlapati Date: Sun, 18 Oct 2026 22:19:32 -0500 Subject: [PATCH] Add official Cline support; ship extension 0.9.95 and MCP 0.11.5 Current Cline (VS Code extension, JetBrains plugin, and CLI) reads one shared settings file, ~/.cline/data/settings/cline_mcp_settings.json, and imports its old VS Code extension-storage file only once. The installer kept writing that old file, so install --cline reported success while FSB never appeared. It now targets the shared file, honoring Cline's CLINE_MCP_SETTINGS_PATH, CLINE_DATA_DIR, and CLINE_DIR overrides, and uses the old file only when no shared settings directory exists. Cline entries set timeout: 1800 so run_task outlives Cline's fixed 60-second request timeout, and reinstalls keep the timeout, disabled, and autoApprove settings Cline stores on the entry. Cline also joins the first-class client surfaces: the onboarding and showcase install pickers with its logo, the client alias table and owner-chip label (VS Code reports "Cline", the CLI "@cline/core"), the telemetry label on both the collector and the server allowlist, and a conservative pricing fallback. Adding it to the home install picker shifts $localize line numbers, so the seven xlf catalogs were re-extracted and synced; only line numbers changed. Versions move to extension 0.9.95 and fsb-mcp-server 0.11.5 through version:set:mcp and version:set:extension, with matching changelog entries, and the Grok Bot plugin now pins fsb-mcp-server@0.11.5. Merging publishes fsb-mcp-server@0.11.5 to npm automatically. --- CHANGELOG.md | 14 +- README.md | 24 ++- extension/README.md | 2 +- .../assets/onboarding/providers/cline.svg | 1 + extension/manifest.json | 4 +- extension/ui/onboarding.js | 6 +- extension/utils/mcp-client-aliases.js | 3 + extension/utils/mcp-pricing-data.json | 6 + extension/utils/telemetry-collector.js | 1 + extension/ws/mcp-tool-dispatcher.js | 1 + mcp/CHANGELOG.md | 22 +++ mcp/README.md | 38 +++- mcp/build/config-writer.js | 43 ++++- mcp/build/install.js | 26 ++- mcp/build/platforms.js | 92 ++++++---- mcp/build/version.d.ts | 2 +- mcp/build/version.js | 2 +- mcp/data/mcp-pricing-data.json | 6 + mcp/native-host/runtime-integrity.json | 4 +- mcp/package-lock.json | 4 +- mcp/package.json | 2 +- mcp/server.json | 4 +- mcp/src/config-writer.ts | 57 +++++- mcp/src/install.ts | 26 ++- mcp/src/platforms.ts | 104 +++++++---- mcp/src/version.ts | 2 +- package-lock.json | 4 +- package.json | 4 +- plugins/grok-bot/README.md | 6 +- plugins/grok-bot/mcp.json | 2 +- showcase/about.html | 6 +- showcase/angular/package-lock.json | 4 +- showcase/angular/package.json | 2 +- showcase/angular/public/llms-full.txt | 4 +- showcase/angular/public/llms.txt | 2 +- showcase/angular/scripts/llms-full.source.md | 4 +- showcase/angular/scripts/llms.source.md | 2 +- showcase/angular/src/app/core/seo/version.ts | 2 +- .../app/pages/agents/agents-page.component.ts | 2 + .../src/app/pages/home/home-page.component.ts | 3 + showcase/angular/src/locale/messages.de.xlf | 28 +-- showcase/angular/src/locale/messages.es.xlf | 28 +-- showcase/angular/src/locale/messages.ja.xlf | 28 +-- showcase/angular/src/locale/messages.ko.xlf | 28 +-- showcase/angular/src/locale/messages.xlf | 28 +-- .../angular/src/locale/messages.zh-CN.xlf | 28 +-- .../angular/src/locale/messages.zh-TW.xlf | 28 +-- showcase/assets/providers/cline.svg | 1 + showcase/server/package-lock.json | 4 +- showcase/server/package.json | 2 +- showcase/server/src/routes/telemetry.js | 5 +- showcase/server/src/telemetry/housekeeper.js | 4 +- skills/fsb/SKILL.md | 2 +- skills/fsb/references/multi-agent-contract.md | 2 +- store-assets/chrome-web-store/listing-copy.md | 2 +- tests/delegation-sidepanel-tab-follow.test.js | 2 + tests/mcp-client-merged-view.test.js | 3 + tests/mcp-install-platforms.test.js | 163 ++++++++++++++++++ tests/mcp-pricing.test.js | 27 +-- tests/mcp-setup-guidance.test.js | 4 + .../onboarding-agent-provider-clicks.test.js | 7 +- tests/onboarding-first-run.test.js | 2 +- tests/server-telemetry-allowlist.test.js | 12 +- 63 files changed, 737 insertions(+), 244 deletions(-) create mode 100644 extension/assets/onboarding/providers/cline.svg create mode 100644 showcase/assets/providers/cline.svg diff --git a/CHANGELOG.md b/CHANGELOG.md index 096eaf376..da05b0680 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,11 +1,23 @@ # Changelog -All notable changes to the FSB product (Chrome extension + showcase dashboard) are documented in this file. Entries are organized by FSB milestone. The extension version is `0.9.92`; milestone versions such as `v0.11.0` and `v0.12.0` are the meaningful release units. +All notable changes to the FSB product (Chrome extension + showcase dashboard) are documented in this file. Entries are organized by FSB milestone. The extension version is `0.9.95`; milestone versions such as `v0.11.0` and `v0.12.0` are the meaningful release units. The independently published `fsb-mcp-server` npm package keeps its own semver changelog in [`mcp/CHANGELOG.md`](./mcp/CHANGELOG.md). ## [Unreleased] +## v0.9.95 — Official Cline Support — 2026-10-18 + +The extension, showcase, skill, documentation, and store metadata align at `0.9.95`. The independently versioned `fsb-mcp-server` advances to `0.11.5` with the accompanying Cline installer fix; see [`mcp/CHANGELOG.md`](./mcp/CHANGELOG.md) for its package-specific release notes and compatibility requirements. + +### Added + +- **Cline is an officially supported MCP client.** The onboarding and showcase install pickers offer `install --cline` with the Cline logo, and the READMEs document Cline's shared settings file. A Cline session is labeled Cline on the owner chip and in the client inventory whether it connects from the VS Code extension or the SDK-based Cline CLI, and anonymous usage stats count it as Cline instead of `unknown`, with the same conservative cost fallback as other bring-your-own-model clients. The companion installer fix ships in `fsb-mcp-server` 0.11.5. + +### Changed + +- The Grok Bot plugin launches `fsb-mcp-server@0.11.5`; it previously pinned 0.11.0. + ## v0.9.92 — Bridge Reliability and Editing Fixes — 2026-10-07 The extension, showcase, skill, documentation, and store metadata align at `0.9.92`. The independently versioned `fsb-mcp-server` advances to `0.11.1` with the accompanying patch; see [`mcp/CHANGELOG.md`](./mcp/CHANGELOG.md) for its package-specific release notes and compatibility requirements. diff --git a/README.md b/README.md index 4bf743172..dbd8bf670 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# FSB v0.9.92 Full Self Browsing +# FSB v0.9.95 Full Self Browsing
@@ -9,7 +9,7 @@ ![FSB](https://img.shields.io/badge/FSB-Full_Self_Browsing-000000?style=for-the-badge) -![Version](https://img.shields.io/badge/version-0.9.92-0078D4?style=for-the-badge) +![Version](https://img.shields.io/badge/version-0.9.95-0078D4?style=for-the-badge) ![Manifest V3](https://img.shields.io/badge/Manifest_V3-Chrome-34A853?style=for-the-badge&logo=googlechrome&logoColor=white) ![License](https://img.shields.io/badge/license-MIT-F5C518?style=for-the-badge) @@ -32,7 +32,7 @@ FSB (Full Self Browsing) is an open source Chrome extension for AI powered browser automation. Describe a task in plain English; FSB reads the live DOM, builds a plan, executes browser actions, verifies results, and reports progress through the popup, side panel, or MCP. -> FSB v0.9.92 is functional and production ready for supervised automation. Browser automation can still behave unpredictably on complex or sensitive sites, so monitor actions and test on non critical pages first. +> FSB v0.9.95 is functional and production ready for supervised automation. Browser automation can still behave unpredictably on complex or sensitive sites, so monitor actions and test on non critical pages first. ### Why DOM First @@ -57,6 +57,7 @@ Project Mariner, Claude Computer Use, and OpenAI Operator rely heavily on visual | VS Code | `npx -y fsb-mcp-server@latest install --vscode` | | Windsurf | `npx -y fsb-mcp-server@latest install --windsurf` | | Codex | `npx -y fsb-mcp-server@latest install --codex` | +| Cline | `npx -y fsb-mcp-server@latest install --cline` | | All at once | `npx -y fsb-mcp-server@latest install --all` | Preview before writing: append `--dry-run`. Sanity check with `npx -y fsb-mcp-server@latest doctor`. Restart the client so the new MCP server appears. @@ -85,7 +86,7 @@ Want to run FSB standalone from the extension popup/side panel? Open settings, p - Uploads real local files to file inputs through the supervised `upload_file` tool with sensitive-path safeguards. - Maintains long term memory for past sites, workflows, selectors, and task outcomes. - Includes secure credential and payment vault flows for supervised autofill. -- Exposes a local MCP server so Claude Code, Codex, Cursor, VS Code, Windsurf, and other MCP clients can drive the browser. +- Exposes a local MCP server so Claude Code, Codex, Cursor, VS Code, Windsurf, Cline, and other MCP clients can drive the browser. Background agents are retired. Existing `chrome.storage.local['bgAgents']` data is preserved for users who had old agents, but scheduled or recurring automation is no longer an active FSB feature. @@ -302,6 +303,7 @@ npx -y fsb-mcp-server@latest install --cursor npx -y fsb-mcp-server@latest install --vscode npx -y fsb-mcp-server@latest install --windsurf npx -y fsb-mcp-server@latest install --codex +npx -y fsb-mcp-server@latest install --cline npx -y fsb-mcp-server@latest install --all ``` @@ -342,6 +344,20 @@ VS Code (`mcp.json`): } ``` +Cline (`~/.cline/data/settings/cline_mcp_settings.json`, shared by the VS Code extension, JetBrains plugin, and CLI). `timeout` is in seconds; Cline's 60-second default would cut off long `run_task` calls: + +```json +{ + "mcpServers": { + "fsb": { + "command": "npx", + "args": ["-y", "fsb-mcp-server@latest"], + "timeout": 1800 + } + } +} +``` + Diagnostics: ```bash diff --git a/extension/README.md b/extension/README.md index b79bb5812..0b7f9725d 100644 --- a/extension/README.md +++ b/extension/README.md @@ -1,6 +1,6 @@ # FSB Chrome Extension -`extension/` is the unpacked Chrome extension package for FSB v0.9.92. Public users should install FSB from the Chrome Web Store so Chrome can apply release updates automatically. Load this directory only for local development or an urgent unreleased fix. +`extension/` is the unpacked Chrome extension package for FSB v0.9.95. Public users should install FSB from the Chrome Web Store so Chrome can apply release updates automatically. Load this directory only for local development or an urgent unreleased fix. ## Load Unpacked diff --git a/extension/assets/onboarding/providers/cline.svg b/extension/assets/onboarding/providers/cline.svg new file mode 100644 index 000000000..956a2eb3e --- /dev/null +++ b/extension/assets/onboarding/providers/cline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/extension/manifest.json b/extension/manifest.json index 2095e8ac0..b9f74a58c 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, - "name": "FSB v0.9.92", - "version": "0.9.92", + "name": "FSB v0.9.95", + "version": "0.9.95", "minimum_chrome_version": "116", "description": "FSB - Universal AI-powered browser automation assistant with multi-model support", "homepage_url": "https://full-selfbrowsing.com", diff --git a/extension/ui/onboarding.js b/extension/ui/onboarding.js index c0e48bf83..9f205b915 100644 --- a/extension/ui/onboarding.js +++ b/extension/ui/onboarding.js @@ -40,11 +40,12 @@ { id: 'windsurf', name: 'Windsurf', logo: 'windsurf.svg', flag: '--windsurf', cmd: 'npx -y fsb-mcp-server@latest install --windsurf' }, { id: 'codex', name: 'Codex', logo: 'openai.svg', flag: '--codex', cmd: 'npx -y fsb-mcp-server@latest install --codex' }, { id: 'opencode', name: 'OpenCode', logo: 'opencode.svg', flag: '--opencode', cmd: 'npx -y fsb-mcp-server@latest install --opencode' }, + { id: 'cline', name: 'Cline', logo: 'cline.svg', flag: '--cline', cmd: 'npx -y fsb-mcp-server@latest install --cline' }, { id: 'openclaw', name: 'OpenClaw', logo: 'openclaw.svg', flag: '', cmd: 'npx -y fsb-mcp-server@latest' }, { id: 'all', name: 'All Clients', logo: 'all.svg', flag: '--all', cmd: 'npx -y fsb-mcp-server@latest install --all' } ]; - const ROLL_CLIENTS = INSTALL_CLIENTS.slice(0, 7); + const ROLL_CLIENTS = INSTALL_CLIENTS.slice(0, 8); const ALL_INSTALL_CLIENT_IDS = [ 'claude-code', 'claude-desktop', @@ -52,7 +53,8 @@ 'vscode', 'windsurf', 'codex', - 'opencode' + 'opencode', + 'cline' ]; const FSB_AGENT_PROVIDERS_STORAGE_KEY = 'fsbAgentProviders'; const STEP_LABELS = { welcome: 'Welcome', path: 'Path', mcp: 'MCP', provider: 'Provider', apikey: 'API Key', setup: 'Setup', pin: 'Pin', done: 'Done' }; diff --git a/extension/utils/mcp-client-aliases.js b/extension/utils/mcp-client-aliases.js index 0d2b5ebab..79376ed22 100644 --- a/extension/utils/mcp-client-aliases.js +++ b/extension/utils/mcp-client-aliases.js @@ -15,6 +15,9 @@ codexcli: 'codex', opencode: 'opencode', opencodecli: 'opencode', + cline: 'cline', + // Cline's SDK-based clients, such as the Cline CLI, identify as the SDK package. + '@cline/core': 'cline', openclaw: 'openclaw' }); diff --git a/extension/utils/mcp-pricing-data.json b/extension/utils/mcp-pricing-data.json index dcef0c4d5..03a0ffb9f 100644 --- a/extension/utils/mcp-pricing-data.json +++ b/extension/utils/mcp-pricing-data.json @@ -292,6 +292,12 @@ "confidence": "LOW", "source_url": "internal-fallback", "notes": "Conservative fallback; Hermes Agent lets the user wire OpenRouter / Nemotron / Mimo / etc. -- no Hermes-side single price-able default exists." + }, + "Cline": { + "model": "claude-sonnet-4-6", + "confidence": "LOW", + "source_url": "internal-fallback", + "notes": "Conservative fallback; Cline is bring-your-own-model (Cline provider, Anthropic, OpenRouter, local models, etc.) with no single Cline-side default." } } } diff --git a/extension/utils/telemetry-collector.js b/extension/utils/telemetry-collector.js index bc5559da7..0dfdc1a22 100644 --- a/extension/utils/telemetry-collector.js +++ b/extension/utils/telemetry-collector.js @@ -112,6 +112,7 @@ var MCP_CLIENT_LABELS = { 'grok': 'Grok', 'gemini': 'Gemini', 'hermes': 'Hermes', + 'cline': 'Cline', 'unknown': 'unknown' }; diff --git a/extension/ws/mcp-tool-dispatcher.js b/extension/ws/mcp-tool-dispatcher.js index 27d9d6942..c602e78a8 100644 --- a/extension/ws/mcp-tool-dispatcher.js +++ b/extension/ws/mcp-tool-dispatcher.js @@ -487,6 +487,7 @@ function canonicalLabelFromClientInfo(clientInfo) { if (name.includes('opencode')) return 'OpenCode'; if (name.includes('cursor')) return 'Cursor'; if (name.includes('windsurf')) return 'Windsurf'; + if (name.includes('cline')) return 'Cline'; return null; } diff --git a/mcp/CHANGELOG.md b/mcp/CHANGELOG.md index 86c9b657b..51f60f200 100644 --- a/mcp/CHANGELOG.md +++ b/mcp/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to `fsb-mcp-server` are documented in this file. Each entry corresponds to a published npm release; FSB extension milestones map to MCP package versions in the entry header. + + +## 0.11.5 (2026-10-18) + +Patch release making Cline an officially supported install target, released alongside FSB extension 0.9.95. + +### Fixed + +- **`install --cline` writes the file current Cline reads.** Cline's VS Code extension, JetBrains plugin, and CLI share `~/.cline/data/settings/cline_mcp_settings.json`, and Cline imports its older VS Code extension-storage file only once, on upgrade. The installer still wrote the old file, so on an upgraded Cline it reported success and FSB never appeared. It now targets the shared file, honors `CLINE_MCP_SETTINGS_PATH`, `CLINE_DATA_DIR`, and `CLINE_DIR` the way Cline does, and uses the old file only when no shared settings directory exists. +- **Long tool calls finish in Cline.** Cline applies one fixed timeout to every MCP request, 60 seconds unless configured, and does not extend it on progress notifications, so `run_task` was cut off. Cline entries now set `timeout: 1800` (seconds). + +### Changed + +- Reinstalling into Cline keeps the `timeout`, `disabled`, and `autoApprove` settings Cline stores on the `fsb` entry, so `install --all` no longer resets a Cline user's auto-approved tools. +- `setup` prints a Cline section with the settings path and entry. + +### Compatibility and update + +- Server identity, capabilities, tool schemas, bridge contracts, and dependency versions are unchanged. Only the installer and its `setup` guidance changed. Pair it with FSB extension 0.9.95, which adds Cline to onboarding and labels Cline sessions as Cline. +- Cline users who installed with an earlier version should rerun `npx -y fsb-mcp-server@latest install --cline` so FSB lands in the shared settings file. +- Merging this release into `main` automatically publishes `fsb-mcp-server@0.11.5` and creates the `mcp-v0.11.5` GitHub release. Later main pushes skip publication when the MCP version is already on npm; explicit MCP release tags remain supported. + ## 0.11.2 (2026-10-14) diff --git a/mcp/README.md b/mcp/README.md index 23644cd94..63029a5ed 100644 --- a/mcp/README.md +++ b/mcp/README.md @@ -38,11 +38,18 @@ Use this package when you want your AI client to drive the browser directly whil > **PhantomStream (engineering milestone 0.12.0):** the dashboard live-preview and remote-control relay are now powered by the published `@full-self-browsing/phantom-stream` package on the extension and showcase side. This is an internal capture/renderer/transport change — the MCP tool schemas, routes, and bridge contracts in this server are unchanged. +### What's New In v0.11.5 + +- `install --cline` writes `~/.cline/data/settings/cline_mcp_settings.json`, the settings file Cline's VS Code extension, JetBrains plugin, and CLI share. Earlier versions wrote a VS Code extension-storage file that current Cline ignores after its one-time import, so rerun the install if FSB never appeared in Cline. +- Cline entries set `timeout: 1800` (seconds) so long `run_task` calls are not cut off by Cline's fixed 60-second request timeout. +- Reinstalling keeps the `timeout`, `disabled`, and `autoApprove` settings Cline stores on the `fsb` entry. +- **Compatibility:** `fsb-mcp-server` 0.11.5 requires FSB extension 0.9.95 or newer for `mcp:task-status`. Upgrade the extension and restart the MCP host together. + ### What's New In v0.11.2 - The FSB dark logo ships with the MCP package and is advertised as an embedded PNG server icon. Update the package and restart the MCP host to make it available to clients that render MCP icons. - The MCP Registry uses the matching hosted dark logo. Rendering remains client-controlled. -- **Compatibility:** `fsb-mcp-server` 0.11.2 requires FSB extension 0.9.92 or newer for `mcp:task-status`. Upgrade the extension and restart the MCP host together. +- **0.11.2 compatibility:** `fsb-mcp-server` 0.11.2 requires FSB extension 0.9.92 or newer for `mcp:task-status`. Upgrade the extension and restart the MCP host together. ### What's New In v0.11.1 @@ -122,7 +129,7 @@ Common file or CLI targets: | `--cursor` | Cursor | | `--vscode` | VS Code | | `--windsurf` | Windsurf | -| `--cline` | Cline | +| `--cline` | Cline (VS Code, JetBrains, CLI) | | `--zed` | Zed | | `--codex` | Codex CLI / Codex IDE | | `--gemini` | Gemini CLI | @@ -152,7 +159,7 @@ command: npx args: -y fsb-mcp-server@latest ``` -For file-based targets, existing config files are parsed, updated, and serialized back in the platform's expected format: JSON, JSONC, TOML, or YAML. For Claude Code, the installer delegates to the `claude mcp add --scope user` CLI command. For instruction-only targets, it prints steps instead of guessing at a private config path. +For file-based targets, existing config files are parsed, updated, and serialized back in the platform's expected format: JSON, JSONC, TOML, or YAML. Cline entries also set `timeout: 1800`, and reinstalling keeps the `timeout`, `disabled`, and `autoApprove` settings Cline stores on the entry. For Claude Code, the installer delegates to the `claude mcp add --scope user` CLI command. For instruction-only targets, it prints steps instead of guessing at a private config path. Use `--dry-run` before `--all` when you want to see planned writes without changing files. @@ -189,6 +196,7 @@ npx -y fsb-mcp-server@latest install --cursor npx -y fsb-mcp-server@latest install --vscode npx -y fsb-mcp-server@latest install --windsurf npx -y fsb-mcp-server@latest install --codex +npx -y fsb-mcp-server@latest install --cline npx -y fsb-mcp-server@latest install --all ``` @@ -268,6 +276,20 @@ VS Code uses the `servers` root key and requires `type: "stdio"`: } ``` +Cline (`~/.cline/data/settings/cline_mcp_settings.json`) is shared by the Cline VS Code extension, JetBrains plugin, and CLI. The installer honors `CLINE_MCP_SETTINGS_PATH`, `CLINE_DATA_DIR`, and `CLINE_DIR` the way Cline does, and falls back to the VS Code extension-storage file used by older Cline builds only when no shared settings directory exists. `timeout` is in seconds: Cline applies one fixed timeout per request, 60 seconds by default, which would cut off a long `run_task`. + +```json +{ + "mcpServers": { + "fsb": { + "command": "npx", + "args": ["-y", "fsb-mcp-server@latest"], + "timeout": 1800 + } + } +} +``` + After editing config files, restart or reload the host client if tools do not appear. Claude Code is usually active immediately after `claude mcp add`; Cursor, Claude Desktop, VS Code, and Windsurf often need a restart, refresh, or trust/start action in their MCP UI. ### Local Streamable HTTP @@ -664,9 +686,9 @@ The build command copies `extension/ai/tool-definitions.js` into `mcp/ai/tool-de ### Versioning -The MCP package has its own version (`0.11.2`) because it is published independently from the extension release (`0.9.92`). Use `npm run version:set:mcp -- X.Y.Z` for MCP releases and `npm run version:set:extension -- X.Y.Z` for extension releases; `npm run version:check` verifies both domains without requiring them to match. +The MCP package has its own version (`0.11.5`) because it is published independently from the extension release (`0.9.95`). Use `npm run version:set:mcp -- X.Y.Z` for MCP releases and `npm run version:set:extension -- X.Y.Z` for extension releases; `npm run version:check` verifies both domains without requiring them to match. -Compatibility for this release: MCP 0.11.2 requires extension 0.9.92 or newer for the `mcp:task-status` route used by `complete_task`, `partial_task`, and `fail_task`. Existing `0.10.0` tool routes remain additive and unchanged. +Compatibility for this release: MCP 0.11.5 requires extension 0.9.95 or newer for the `mcp:task-status` route used by `complete_task`, `partial_task`, and `fail_task`. Existing `0.10.0` tool routes remain additive and unchanged. Contract-sensitive changes should be covered by tests before publishing: @@ -680,14 +702,14 @@ Contract-sensitive changes should be covered by tests before publishing: - trigger contract: `trigger`, `stop_trigger`, `get_trigger_status`, `list_triggers`, blocking/detached reporting, and local browser-open limits - terminal task-outcome contract: `mcp:task-status`, agent/ownership context, serialized lifecycle ordering, and `fail_task` acknowledgement semantics -### Releasing 0.11.2 +### Releasing 0.11.5 -This 0.11.2 build is release-prep ready. Merging a new MCP version into `main` automatically publishes the verified npm package to `latest` and creates its `mcp-vX.Y.Z` tag and GitHub release. Main pushes whose MCP version is already published skip publishing. Bump the MCP version with `npm run version:set:mcp -- X.Y.Z` before merging a new release. +This 0.11.5 build is release-prep ready. Merging a new MCP version into `main` automatically publishes the verified npm package to `latest` and creates its `mcp-vX.Y.Z` tag and GitHub release. Main pushes whose MCP version is already published skip publishing. Bump the MCP version with `npm run version:set:mcp -- X.Y.Z` before merging a new release. To publish: - Preferred: merge the versioned release into `main`; the npm workflow builds the Windows native-host artifacts, verifies the packed payload, publishes to npm, and creates the release automatically. -- Explicit tag alternative: for an unpublished version already on `main`, run `git tag mcp-v0.11.2 && git push origin mcp-v0.11.2`. Tag-triggered releases still require the tag to match the package version and reject an already-published version. +- Explicit tag alternative: for an unpublished version already on `main`, run `git tag mcp-v0.11.5 && git push origin mcp-v0.11.5`. Tag-triggered releases still require the tag to match the package version and reject an already-published version. - Manual MCP fallback: from the release commit, run `cd mcp && npm publish --tag latest` only after confirming `npm whoami`, `npm --prefix mcp run build`, and the MCP smoke/parity/schema gates all pass. Use the release workflow for normal releases so the native-host packaging and integrity checks run before publication. diff --git a/mcp/build/config-writer.js b/mcp/build/config-writer.js index 7f3d6f0eb..275247bc4 100644 --- a/mcp/build/config-writer.js +++ b/mcp/build/config-writer.js @@ -78,6 +78,45 @@ function mergeServerEntry(data, serverMapKey, serverName, entry, mergeStrategy) data[serverMapKey][serverName] = entry; } } +/** + * Find the current server entry, if any, under the platform's root key. + * @param data - Current config data + * @param serverMapKey - Root key + * @param serverName - Server name + * @param mergeStrategy - How entries are stored: 'object-map' or 'named-array' + * @returns The existing entry, or undefined when there is none + */ +function findExistingEntry(data, serverMapKey, serverName, mergeStrategy) { + const container = data[serverMapKey]; + if (mergeStrategy === 'named-array') { + return Array.isArray(container) + ? container.find((item) => item?.name === serverName) + : undefined; + } + if (!container || typeof container !== 'object' || Array.isArray(container)) + return undefined; + return container[serverName]; +} +/** + * Copy the platform's preserved keys from the existing entry onto the new one, + * in the order the existing entry lists them, so a reinstall keeps settings the + * user chose in the client and an otherwise unchanged entry still compares equal. + * @param entry - Server entry FSB would write + * @param existing - Entry currently in the config, if any + * @param keys - Keys the client owns on the entry + * @returns The entry to write + */ +function carryOverPreservedKeys(entry, existing, keys) { + if (!keys?.length || !existing || typeof existing !== 'object' || Array.isArray(existing)) { + return entry; + } + const merged = { ...entry }; + for (const [key, value] of Object.entries(existing)) { + if (keys.includes(key) && value !== undefined) + merged[key] = value; + } + return merged; +} /** * Check if FSB is already configured identically (idempotency check). * @param data - Current config data @@ -142,7 +181,7 @@ export async function installToConfig(configPath, platform, serverName, entry = }; } // 2. Build the server entry (use caller-provided entry or fall back to default) - const serverEntry = entry || { ...getServerEntry() }; + const baseEntry = entry || { ...getServerEntry() }; const strategy = platform.mergeStrategy; // 3. Read existing file or start fresh, backup before any mutation let data = {}; @@ -153,6 +192,8 @@ export async function installToConfig(configPath, platform, serverName, entry = const raw = await readFile(configPath, 'utf-8'); data = parseByFormat(raw, platform.format); } + // Keep client-owned settings from an existing entry (e.g. Cline's auto-approve list) + const serverEntry = carryOverPreservedKeys(baseEntry, findExistingEntry(data, platform.serverMapKey, serverName, strategy), platform.preservedEntryKeys); // 4. Check idempotency (INST-04) -- skip if already identical if (checkIdempotent(data, platform.serverMapKey, serverName, serverEntry, strategy)) { return { diff --git a/mcp/build/install.js b/mcp/build/install.js index e5e860c21..046c3d0c5 100644 --- a/mcp/build/install.js +++ b/mcp/build/install.js @@ -1,4 +1,4 @@ -import { PLATFORMS, resolvePlatformTarget, getEntryForPlatform } from './platforms.js'; +import { PLATFORMS, resolvePlatformTarget, getEntryForPlatform, CLINE_TIMEOUT_SECONDS } from './platforms.js'; import { installToConfig, removeFromConfig, serializeByFormat } from './config-writer.js'; import { FSB_MCP_VERSION, FSB_SERVER_NAME } from './version.js'; import { execSync } from 'node:child_process'; @@ -279,7 +279,9 @@ export function getInstallNextStep(platformKey, variant = null) { case 'codex': return 'Restart Codex or reload the MCP server list after editing ~/.codex/config.toml.'; case 'cline': - return 'Reload VS Code/Cline after editing the MCP settings file.'; + return variant === 'legacy-vscode' + ? 'Reload VS Code/Cline after editing the MCP settings file. Current Cline releases read ~/.cline/data/settings/cline_mcp_settings.json instead; update Cline and rerun this install if fsb does not appear.' + : 'Cline reloads ~/.cline/data/settings/cline_mcp_settings.json on its own. If fsb does not appear, restart it under MCP Servers in Cline, or start a new Cline CLI session.'; case 'zed': return 'Reload Zed after editing settings.json.'; case 'gemini': @@ -475,6 +477,26 @@ export function getSetupSections(httpEndpoint, cursorDeeplink) { ' Press refresh in Windsurf or reload the client after editing the matching config file.', ], }, + { + title: 'Cline (VS Code, JetBrains, CLI)', + lines: [ + 'Config: ~/.cline/data/settings/cline_mcp_settings.json', + ' (CLINE_MCP_SETTINGS_PATH, CLINE_DATA_DIR, or CLINE_DIR move it, as in Cline)', + 'Add:', + ' {', + ' "mcpServers": {', + ' "fsb": {', + ' "command": "npx",', + ' "args": ["-y", "fsb-mcp-server@latest"],', + ' "timeout": ' + CLINE_TIMEOUT_SECONDS, + ' }', + ' }', + ' }', + 'timeout is in seconds. Cline\'s 60-second default cuts off long run_task calls.', + 'Next step:', + ' ' + getInstallNextStep('cline'), + ], + }, // New platforms { title: 'Roo Code', diff --git a/mcp/build/platforms.js b/mcp/build/platforms.js index 99d485860..655622677 100644 --- a/mcp/build/platforms.js +++ b/mcp/build/platforms.js @@ -11,6 +11,32 @@ function vsCodeGlobalStorage(publisherId, settingsFile) { linux: join(home, '.config', 'Code', 'User', 'globalStorage', publisherId, 'settings', settingsFile), }; } +const CLINE_MCP_SETTINGS_FILE = 'cline_mcp_settings.json'; +/** + * Cline's VS Code extension, JetBrains plugin, and CLI all read one MCP + * settings file under ~/.cline. Resolve it with the same environment + * overrides Cline honors, in the same precedence order. + */ +function clineMcpSettingsPaths() { + const explicitPath = process.env.CLINE_MCP_SETTINGS_PATH?.trim(); + const dataDir = process.env.CLINE_DATA_DIR?.trim() + || join(process.env.CLINE_DIR?.trim() || join(home, '.cline'), 'data'); + const settingsPath = explicitPath || join(dataDir, 'settings', CLINE_MCP_SETTINGS_FILE); + return { darwin: settingsPath, win32: settingsPath, linux: settingsPath }; +} +/** + * Older Cline builds kept MCP settings in VS Code's extension storage. Current + * builds import that file once on upgrade and then ignore it, so it is only a + * fallback for machines that have no shared Cline settings directory. + */ +const CLINE_LEGACY_VSCODE_PATHS = vsCodeGlobalStorage('saoudrizwan.claude-dev', CLINE_MCP_SETTINGS_FILE); +/** + * Cline gives every MCP request one fixed timeout, 60 seconds unless the + * server entry sets `timeout`, and does not extend it on progress + * notifications. `run_task` can block for up to 600 seconds, so the entry + * asks for 1800: the smallest preset in Cline's timeout picker above that. + */ +export const CLINE_TIMEOUT_SECONDS = 1800; /** * Platform registry mapping all MCP-capable platforms to their * config file metadata. Each entry contains: @@ -91,9 +117,12 @@ export const PLATFORMS = { flag: 'cline', format: 'json', serverMapKey: 'mcpServers', - configPath: vsCodeGlobalStorage('saoudrizwan.claude-dev', 'cline_mcp_settings.json'), + configPath: clineMcpSettingsPaths(), installMode: 'file', mergeStrategy: 'object-map', + // Cline's MCP panel writes these onto the entry: the server toggle, + // per-tool auto-approval, and the timeout picker. + preservedEntryKeys: ['timeout', 'disabled', 'autoApprove'], }, 'zed': { displayName: 'Zed', @@ -286,6 +315,7 @@ export function getServerEntry() { * Most platforms use the standard { command, args } shape. Some need * different field names or structures: * - vscode: adds type: "stdio" + * - cline: adds a request timeout in seconds (see CLINE_TIMEOUT_SECONDS) * - goose: uses { name, type, cmd, args } for its extensions array * - opencode: uses { type: "local", command: [...] } with command as array */ @@ -294,6 +324,8 @@ export function getEntryForPlatform(platformKey) { switch (platformKey) { case 'vscode': return { type: 'stdio', ...base }; + case 'cline': + return { ...base, timeout: CLINE_TIMEOUT_SECONDS }; case 'goose': return { name: FSB_SERVER_NAME, type: 'stdio', cmd: base.command, args: base.args }; case 'opencode': @@ -334,6 +366,23 @@ function getPlatformTargetCandidates(platformKey, platform) { }, ]; } + if (platformKey === 'cline') { + return [ + { + configPath, + targetLabel: platform.displayName, + variant: null, + }, + { + configPath: CLINE_LEGACY_VSCODE_PATHS[process.platform], + targetLabel: platform.displayName + ' (legacy VS Code settings)', + variant: 'legacy-vscode', + // Upgraded installs keep this file after importing it, so it must not + // outrank a shared settings directory that exists without a file yet. + fallback: true, + }, + ]; + } return [ { configPath, @@ -342,9 +391,14 @@ function getPlatformTargetCandidates(platformKey, platform) { }, ]; } +function findDetectedCandidate(candidates) { + return candidates.find((candidate) => existsSync(candidate.configPath)) + ?? candidates.find((candidate) => existsSync(dirname(candidate.configPath))); +} /** * Resolve the best config target for a platform on the current OS. * Prefers an existing config file first, then an existing parent directory, + * consulting fallback candidates only when no other candidate is detected, * and finally falls back to the default documented path for dry-run/help output. * * @param platformKey - Platform key (e.g., 'cursor', 'windsurf') @@ -366,38 +420,16 @@ export function resolvePlatformTarget(platformKey) { detected: false, }; } - for (const candidate of candidates) { - if (existsSync(candidate.configPath)) { - return { - platformKey, - platform, - configPath: candidate.configPath, - targetLabel: candidate.targetLabel, - variant: candidate.variant, - detected: true, - }; - } - } - for (const candidate of candidates) { - if (existsSync(dirname(candidate.configPath))) { - return { - platformKey, - platform, - configPath: candidate.configPath, - targetLabel: candidate.targetLabel, - variant: candidate.variant, - detected: true, - }; - } - } - const fallback = candidates[0]; + const detected = findDetectedCandidate(candidates.filter((candidate) => !candidate.fallback)) + ?? findDetectedCandidate(candidates.filter((candidate) => candidate.fallback)); + const target = detected ?? candidates[0]; return { platformKey, platform, - configPath: fallback.configPath, - targetLabel: fallback.targetLabel, - variant: fallback.variant, - detected: false, + configPath: target.configPath, + targetLabel: target.targetLabel, + variant: target.variant, + detected: detected !== undefined, }; } /** diff --git a/mcp/build/version.d.ts b/mcp/build/version.d.ts index 6aedbb272..f6e73df31 100644 --- a/mcp/build/version.d.ts +++ b/mcp/build/version.d.ts @@ -1,5 +1,5 @@ export declare const FSB_SERVER_NAME = "fsb"; -export declare const FSB_MCP_VERSION = "0.11.2"; +export declare const FSB_MCP_VERSION = "0.11.5"; export declare const FSB_EXTENSION_BRIDGE_PORT = 7225; export declare const FSB_EXTENSION_BRIDGE_URL = "ws://localhost:7225"; export declare const DEFAULT_HTTP_HOST = "127.0.0.1"; diff --git a/mcp/build/version.js b/mcp/build/version.js index ae289f38c..f53c7d76f 100644 --- a/mcp/build/version.js +++ b/mcp/build/version.js @@ -1,6 +1,6 @@ import { NATIVE_HOST_SERVICE_PORT } from './native-host/constants.js'; export const FSB_SERVER_NAME = 'fsb'; -export const FSB_MCP_VERSION = '0.11.2'; +export const FSB_MCP_VERSION = '0.11.5'; export const FSB_EXTENSION_BRIDGE_PORT = 7225; export const FSB_EXTENSION_BRIDGE_URL = `ws://localhost:${FSB_EXTENSION_BRIDGE_PORT}`; export const DEFAULT_HTTP_HOST = '127.0.0.1'; diff --git a/mcp/data/mcp-pricing-data.json b/mcp/data/mcp-pricing-data.json index dcef0c4d5..03a0ffb9f 100644 --- a/mcp/data/mcp-pricing-data.json +++ b/mcp/data/mcp-pricing-data.json @@ -292,6 +292,12 @@ "confidence": "LOW", "source_url": "internal-fallback", "notes": "Conservative fallback; Hermes Agent lets the user wire OpenRouter / Nemotron / Mimo / etc. -- no Hermes-side single price-able default exists." + }, + "Cline": { + "model": "claude-sonnet-4-6", + "confidence": "LOW", + "source_url": "internal-fallback", + "notes": "Conservative fallback; Cline is bring-your-own-model (Cline provider, Anthropic, OpenRouter, local models, etc.) with no single Cline-side default." } } } diff --git a/mcp/native-host/runtime-integrity.json b/mcp/native-host/runtime-integrity.json index 5ce5c9b74..095cdbf7e 100644 --- a/mcp/native-host/runtime-integrity.json +++ b/mcp/native-host/runtime-integrity.json @@ -1,8 +1,8 @@ { "schema": 1, "packageName": "fsb-mcp-server", - "packageVersion": "0.11.2", - "lockSha256": "babe9349d46451732308aa53f9204093357e8f7768bcf4f1fd5c590a5a6d0667", + "packageVersion": "0.11.5", + "lockSha256": "2f78f5283a967e3c40474cacbd46701c55ebf6c46b50f0214e01554d3be1c65b", "directDependencies": [ { "name": "@modelcontextprotocol/sdk", diff --git a/mcp/package-lock.json b/mcp/package-lock.json index 3a33305c4..66ffd8144 100644 --- a/mcp/package-lock.json +++ b/mcp/package-lock.json @@ -1,12 +1,12 @@ { "name": "fsb-mcp-server", - "version": "0.11.2", + "version": "0.11.5", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fsb-mcp-server", - "version": "0.11.2", + "version": "0.11.5", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "1.29.0", diff --git a/mcp/package.json b/mcp/package.json index b4ef4b649..6e63f2995 100644 --- a/mcp/package.json +++ b/mcp/package.json @@ -1,6 +1,6 @@ { "name": "fsb-mcp-server", - "version": "0.11.2", + "version": "0.11.5", "description": "FSB Browser Automation MCP Server", "license": "MIT", "author": "Lakshman Turlapati", diff --git a/mcp/server.json b/mcp/server.json index 80c95e42a..2d7802639 100644 --- a/mcp/server.json +++ b/mcp/server.json @@ -3,7 +3,7 @@ "name": "io.github.fullselfbrowsing/fsb-mcp-server", "title": "FSB MCP Server", "description": "Control the FSB browser extension from any MCP client using a local stdio or Streamable HTTP companion runtime.", - "version": "0.11.2", + "version": "0.11.5", "icons": [ { "src": "https://www.full-selfbrowsing.com/assets/fsb_logo_dark.png", @@ -17,7 +17,7 @@ { "registryType": "npm", "identifier": "fsb-mcp-server", - "version": "0.11.2", + "version": "0.11.5", "transport": { "type": "stdio" } diff --git a/mcp/src/config-writer.ts b/mcp/src/config-writer.ts index a88d32786..477d4b1bd 100644 --- a/mcp/src/config-writer.ts +++ b/mcp/src/config-writer.ts @@ -94,6 +94,54 @@ function mergeServerEntry( } } +/** + * Find the current server entry, if any, under the platform's root key. + * @param data - Current config data + * @param serverMapKey - Root key + * @param serverName - Server name + * @param mergeStrategy - How entries are stored: 'object-map' or 'named-array' + * @returns The existing entry, or undefined when there is none + */ +function findExistingEntry( + data: Record, + serverMapKey: string, + serverName: string, + mergeStrategy: MergeStrategy, +): unknown { + const container = data[serverMapKey]; + if (mergeStrategy === 'named-array') { + return Array.isArray(container) + ? container.find((item: Record) => item?.name === serverName) + : undefined; + } + if (!container || typeof container !== 'object' || Array.isArray(container)) return undefined; + return (container as Record)[serverName]; +} + +/** + * Copy the platform's preserved keys from the existing entry onto the new one, + * in the order the existing entry lists them, so a reinstall keeps settings the + * user chose in the client and an otherwise unchanged entry still compares equal. + * @param entry - Server entry FSB would write + * @param existing - Entry currently in the config, if any + * @param keys - Keys the client owns on the entry + * @returns The entry to write + */ +function carryOverPreservedKeys( + entry: Record, + existing: unknown, + keys: readonly string[] | undefined, +): Record { + if (!keys?.length || !existing || typeof existing !== 'object' || Array.isArray(existing)) { + return entry; + } + const merged: Record = { ...entry }; + for (const [key, value] of Object.entries(existing)) { + if (keys.includes(key) && value !== undefined) merged[key] = value; + } + return merged; +} + /** * Check if FSB is already configured identically (idempotency check). * @param data - Current config data @@ -177,7 +225,7 @@ export async function installToConfig( } // 2. Build the server entry (use caller-provided entry or fall back to default) - const serverEntry: Record = entry || { ...getServerEntry() }; + const baseEntry: Record = entry || { ...getServerEntry() }; const strategy: MergeStrategy = platform.mergeStrategy; // 3. Read existing file or start fresh, backup before any mutation @@ -190,6 +238,13 @@ export async function installToConfig( data = parseByFormat(raw, platform.format); } + // Keep client-owned settings from an existing entry (e.g. Cline's auto-approve list) + const serverEntry: Record = carryOverPreservedKeys( + baseEntry, + findExistingEntry(data, platform.serverMapKey!, serverName, strategy), + platform.preservedEntryKeys, + ); + // 4. Check idempotency (INST-04) -- skip if already identical if (checkIdempotent(data, platform.serverMapKey!, serverName, serverEntry, strategy)) { return { diff --git a/mcp/src/install.ts b/mcp/src/install.ts index d406bec78..ab19c7955 100644 --- a/mcp/src/install.ts +++ b/mcp/src/install.ts @@ -1,4 +1,4 @@ -import { PLATFORMS, resolvePlatformTarget, getEntryForPlatform } from './platforms.js'; +import { PLATFORMS, resolvePlatformTarget, getEntryForPlatform, CLINE_TIMEOUT_SECONDS } from './platforms.js'; import type { PlatformConfig, PlatformTarget } from './platforms.js'; import { installToConfig, removeFromConfig, serializeByFormat } from './config-writer.js'; import type { ConfigResult } from './config-writer.js'; @@ -346,7 +346,9 @@ export function getInstallNextStep(platformKey: string, variant: string | null = case 'codex': return 'Restart Codex or reload the MCP server list after editing ~/.codex/config.toml.'; case 'cline': - return 'Reload VS Code/Cline after editing the MCP settings file.'; + return variant === 'legacy-vscode' + ? 'Reload VS Code/Cline after editing the MCP settings file. Current Cline releases read ~/.cline/data/settings/cline_mcp_settings.json instead; update Cline and rerun this install if fsb does not appear.' + : 'Cline reloads ~/.cline/data/settings/cline_mcp_settings.json on its own. If fsb does not appear, restart it under MCP Servers in Cline, or start a new Cline CLI session.'; case 'zed': return 'Reload Zed after editing settings.json.'; case 'gemini': @@ -544,6 +546,26 @@ export function getSetupSections(httpEndpoint: string, cursorDeeplink: string): ' Press refresh in Windsurf or reload the client after editing the matching config file.', ], }, + { + title: 'Cline (VS Code, JetBrains, CLI)', + lines: [ + 'Config: ~/.cline/data/settings/cline_mcp_settings.json', + ' (CLINE_MCP_SETTINGS_PATH, CLINE_DATA_DIR, or CLINE_DIR move it, as in Cline)', + 'Add:', + ' {', + ' "mcpServers": {', + ' "fsb": {', + ' "command": "npx",', + ' "args": ["-y", "fsb-mcp-server@latest"],', + ' "timeout": ' + CLINE_TIMEOUT_SECONDS, + ' }', + ' }', + ' }', + 'timeout is in seconds. Cline\'s 60-second default cuts off long run_task calls.', + 'Next step:', + ' ' + getInstallNextStep('cline'), + ], + }, // New platforms { title: 'Roo Code', diff --git a/mcp/src/platforms.ts b/mcp/src/platforms.ts index 58ba36c05..2cb07abfb 100644 --- a/mcp/src/platforms.ts +++ b/mcp/src/platforms.ts @@ -36,6 +36,8 @@ export interface PlatformConfig { installMode: InstallMode; mergeStrategy: MergeStrategy; osRestriction?: 'darwin' | 'win32' | 'linux'; + /** Settings the client stores on the server entry itself; a reinstall keeps them. */ + preservedEntryKeys?: readonly string[]; } /** Structured target resolution returned to the installer */ @@ -62,6 +64,36 @@ function vsCodeGlobalStorage(publisherId: string, settingsFile: string): Platfor }; } +const CLINE_MCP_SETTINGS_FILE = 'cline_mcp_settings.json'; + +/** + * Cline's VS Code extension, JetBrains plugin, and CLI all read one MCP + * settings file under ~/.cline. Resolve it with the same environment + * overrides Cline honors, in the same precedence order. + */ +function clineMcpSettingsPaths(): PlatformPaths { + const explicitPath = process.env.CLINE_MCP_SETTINGS_PATH?.trim(); + const dataDir = process.env.CLINE_DATA_DIR?.trim() + || join(process.env.CLINE_DIR?.trim() || join(home, '.cline'), 'data'); + const settingsPath = explicitPath || join(dataDir, 'settings', CLINE_MCP_SETTINGS_FILE); + return { darwin: settingsPath, win32: settingsPath, linux: settingsPath }; +} + +/** + * Older Cline builds kept MCP settings in VS Code's extension storage. Current + * builds import that file once on upgrade and then ignore it, so it is only a + * fallback for machines that have no shared Cline settings directory. + */ +const CLINE_LEGACY_VSCODE_PATHS: PlatformPaths = vsCodeGlobalStorage('saoudrizwan.claude-dev', CLINE_MCP_SETTINGS_FILE); + +/** + * Cline gives every MCP request one fixed timeout, 60 seconds unless the + * server entry sets `timeout`, and does not extend it on progress + * notifications. `run_task` can block for up to 600 seconds, so the entry + * asks for 1800: the smallest preset in Cline's timeout picker above that. + */ +export const CLINE_TIMEOUT_SECONDS = 1800; + /** * Platform registry mapping all MCP-capable platforms to their * config file metadata. Each entry contains: @@ -149,9 +181,12 @@ export const PLATFORMS: PlatformRegistry = { flag: 'cline', format: 'json', serverMapKey: 'mcpServers', - configPath: vsCodeGlobalStorage('saoudrizwan.claude-dev', 'cline_mcp_settings.json'), + configPath: clineMcpSettingsPaths(), installMode: 'file', mergeStrategy: 'object-map', + // Cline's MCP panel writes these onto the entry: the server toggle, + // per-tool auto-approval, and the timeout picker. + preservedEntryKeys: ['timeout', 'disabled', 'autoApprove'], }, 'zed': { @@ -363,6 +398,7 @@ export function getServerEntry(): ServerEntry { * Most platforms use the standard { command, args } shape. Some need * different field names or structures: * - vscode: adds type: "stdio" + * - cline: adds a request timeout in seconds (see CLINE_TIMEOUT_SECONDS) * - goose: uses { name, type, cmd, args } for its extensions array * - opencode: uses { type: "local", command: [...] } with command as array */ @@ -371,6 +407,8 @@ export function getEntryForPlatform(platformKey: string): Record existsSync(candidate.configPath)) + ?? candidates.find((candidate) => existsSync(dirname(candidate.configPath))); +} + /** * Resolve the best config target for a platform on the current OS. * Prefers an existing config file first, then an existing parent directory, + * consulting fallback candidates only when no other candidate is detected, * and finally falls back to the default documented path for dry-run/help output. * * @param platformKey - Platform key (e.g., 'cursor', 'windsurf') @@ -452,40 +516,16 @@ export function resolvePlatformTarget(platformKey: string): PlatformTarget { }; } - for (const candidate of candidates) { - if (existsSync(candidate.configPath)) { - return { - platformKey, - platform, - configPath: candidate.configPath, - targetLabel: candidate.targetLabel, - variant: candidate.variant, - detected: true, - }; - } - } - - for (const candidate of candidates) { - if (existsSync(dirname(candidate.configPath))) { - return { - platformKey, - platform, - configPath: candidate.configPath, - targetLabel: candidate.targetLabel, - variant: candidate.variant, - detected: true, - }; - } - } - - const fallback = candidates[0]; + const detected = findDetectedCandidate(candidates.filter((candidate) => !candidate.fallback)) + ?? findDetectedCandidate(candidates.filter((candidate) => candidate.fallback)); + const target = detected ?? candidates[0]; return { platformKey, platform, - configPath: fallback.configPath, - targetLabel: fallback.targetLabel, - variant: fallback.variant, - detected: false, + configPath: target.configPath, + targetLabel: target.targetLabel, + variant: target.variant, + detected: detected !== undefined, }; } diff --git a/mcp/src/version.ts b/mcp/src/version.ts index 94a060b57..f8f50504a 100644 --- a/mcp/src/version.ts +++ b/mcp/src/version.ts @@ -1,7 +1,7 @@ import { NATIVE_HOST_SERVICE_PORT } from './native-host/constants.js'; export const FSB_SERVER_NAME = 'fsb'; -export const FSB_MCP_VERSION = '0.11.2'; +export const FSB_MCP_VERSION = '0.11.5'; export const FSB_EXTENSION_BRIDGE_PORT = 7225; export const FSB_EXTENSION_BRIDGE_URL = `ws://localhost:${FSB_EXTENSION_BRIDGE_PORT}`; export const DEFAULT_HTTP_HOST = '127.0.0.1'; diff --git a/package-lock.json b/package-lock.json index e5c4eae9b..7bd00599e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fsb-full-self-browsing", - "version": "0.9.92", + "version": "0.9.95", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fsb-full-self-browsing", - "version": "0.9.92", + "version": "0.9.95", "license": "MIT", "os": [ "darwin", diff --git a/package.json b/package.json index 3003a5683..c575b9d44 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fsb-full-self-browsing", - "version": "0.9.92", + "version": "0.9.95", "description": "FSB - Full Self-Browsing: An intelligent, open-source Chrome extension for AI-powered browser automation using natural language instructions", "main": "background.js", "homepage": "https://github.com/fullselfbrowsing/FSB", @@ -139,7 +139,7 @@ { "description": "Version", "href": "https://github.com/fullselfbrowsing/FSB/releases", - "url": "https://img.shields.io/badge/version-0.9.92-blue.svg" + "url": "https://img.shields.io/badge/version-0.9.95-blue.svg" }, { "description": "License", diff --git a/plugins/grok-bot/README.md b/plugins/grok-bot/README.md index 4b64c3f44..a051af467 100644 --- a/plugins/grok-bot/README.md +++ b/plugins/grok-bot/README.md @@ -23,7 +23,7 @@ FSB's Chrome extension and MCP process must run **together on Grok Bot's own com 3. Once this package is listed, open Bot's **Plugins → Marketplace**, search for **Full Self Browsing**, and install it. Team policies may restrict availability. The package does not install Node, Chrome or the extension automatically. 4. Ask Bot to use FSB for a small browser read. If the extension shows a bridge pairing request, complete its local pairing flow. Confirm the connection targets the browser on Bot's computer before continuing. -The configured server is named `fsb` and launches as `npx -y fsb-mcp-server@0.11.0`. Its first launch may download the package and dependencies. Keep one intended FSB server configuration active; an existing manual FSB connection may otherwise duplicate or override the plugin connection. +The configured server is named `fsb` and launches as `npx -y fsb-mcp-server@0.11.5`. Its first launch may download the package and dependencies. Keep one intended FSB server configuration active; an existing manual FSB connection may otherwise duplicate or override the plugin connection. Manual MCP browsing requires no FSB API key or separate provider OAuth. Websites use the sessions in Bot's browser profile, so sign in there when needed. Optional FSB autopilot requires a configured model provider and can incur provider charges. @@ -48,8 +48,8 @@ Run diagnostics **on Bot's computer**: ```bash node --version npx --version -npx -y fsb-mcp-server@0.11.0 doctor -npx -y fsb-mcp-server@0.11.0 status +npx -y fsb-mcp-server@0.11.5 doctor +npx -y fsb-mcp-server@0.11.5 status ``` The doctor also checks optional model-provider configuration. A missing provider or model can produce a `config` failure even when Chrome is attached; manual MCP tools do not require that configuration. Verify a small manual page read separately. diff --git a/plugins/grok-bot/mcp.json b/plugins/grok-bot/mcp.json index 53e477114..44b622b7b 100644 --- a/plugins/grok-bot/mcp.json +++ b/plugins/grok-bot/mcp.json @@ -2,7 +2,7 @@ "mcpServers": { "fsb": { "command": "npx", - "args": ["-y", "fsb-mcp-server@0.11.0"] + "args": ["-y", "fsb-mcp-server@0.11.5"] } } } diff --git a/showcase/about.html b/showcase/about.html index 1339fbc46..813cb2a9e 100644 --- a/showcase/about.html +++ b/showcase/about.html @@ -330,7 +330,7 @@

The Best AI Tools of Marc

@@ -837,7 +837,7 @@

About this item

@@ -1101,7 +1101,7 @@

Project

diff --git a/showcase/angular/package-lock.json b/showcase/angular/package-lock.json index 6e4b184d9..564a2c2b9 100644 --- a/showcase/angular/package-lock.json +++ b/showcase/angular/package-lock.json @@ -1,12 +1,12 @@ { "name": "showcase-angular", - "version": "0.9.92", + "version": "0.9.95", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "showcase-angular", - "version": "0.9.92", + "version": "0.9.95", "dependencies": { "@angular/animations": "^20.3.19", "@angular/common": "^20.3.19", diff --git a/showcase/angular/package.json b/showcase/angular/package.json index 47c6152ec..0f3952e13 100644 --- a/showcase/angular/package.json +++ b/showcase/angular/package.json @@ -1,6 +1,6 @@ { "name": "showcase-angular", - "version": "0.9.92", + "version": "0.9.95", "private": true, "scripts": { "ng": "ng", diff --git a/showcase/angular/public/llms-full.txt b/showcase/angular/public/llms-full.txt index 63bdfc1c2..b2ead07db 100644 --- a/showcase/angular/public/llms-full.txt +++ b/showcase/angular/public/llms-full.txt @@ -5,7 +5,7 @@ The About page embeds the public 33-second FSB trailer (uploaded September 6, 20 ## 1. Project Description -FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task in plain English; FSB plans the clicks, types, and navigation to complete it. The extension runs in your browser with your own API keys, encrypted local storage, and opt-out anonymous usage telemetry for aggregate public stats only. Multi-model AI (xAI Grok, OpenAI, Anthropic Claude, Google Gemini, OpenRouter, LM Studio, custom OpenAI-compatible endpoints), 56 canonical browser tools, a 68-tool MCP surface, 142+ site-specific guides, reactive trigger watchers, real file uploads, and a 128-app native capability catalog for verified first-party API execution. Current release: FSB v0.9.92 (Chrome extension) with fsb-mcp-server 0.11.2 (npm MCP package). +FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task in plain English; FSB plans the clicks, types, and navigation to complete it. The extension runs in your browser with your own API keys, encrypted local storage, and opt-out anonymous usage telemetry for aggregate public stats only. Multi-model AI (xAI Grok, OpenAI, Anthropic Claude, Google Gemini, OpenRouter, LM Studio, custom OpenAI-compatible endpoints), 56 canonical browser tools, a 68-tool MCP surface, 142+ site-specific guides, reactive trigger watchers, real file uploads, and a 128-app native capability catalog for verified first-party API execution. Current release: FSB v0.9.95 (Chrome extension) with fsb-mcp-server 0.11.5 (npm MCP package). License: [MIT License](https://github.com/fullselfbrowsing/FSB/blob/main/LICENSE). @@ -137,7 +137,7 @@ FSB exposes its tooling through a Model Context Protocol (MCP) server. External The MCP surface is the best way to use FSB with coding agents and external autonomy layers. FSB handles precise single-attempt browser execution, visual feedback, observability, and verification. Long-running scheduling and personality/identity decisions belong in the calling agent layer, such as OpenClaw or Claude Routines. ### OpenClaw skill -FSB ships a dedicated OpenClaw + Hermes skill at `skills/fsb/` in the repo root. The skill is the canonical OpenClaw onboarding path: it runs the doctor flow against `fsb-mcp-server`, prints the OpenClaw stdio config block for the user to paste into OpenClaw's MCP config, and offers consent-gated install for any other MCP hosts detected on the same machine. The bare `--openclaw` flag in the FSB MCP installer stays manual / unsupported because OpenClaw's MCP config schema is still unstable across builds; the skill prints, the user pastes, no auto-write. Frontmatter ships with `name: fsb`, `version: 0.9.92`, `requires.bins: [node, npx]`, and `requires.env: []` so vault credentials never leave the FSB Chrome extension. The full marketing rundown (supported agents and hosts, MCP power story, skill features, one-line install command, grounded use cases, autopilot rules) lives at `https://full-selfbrowsing.com/agents`. Hermes users get the same skill: `node skills/fsb/scripts/print-hermes-yaml.mjs` prints the canonical `~/.hermes/config.yaml` `mcp_servers.fsb` block, and FSB v0.9.69 (PR #49) added `Hermes` to the v0.9.36 shared MCP client allowlist so action calls passing `client: "Hermes"` work without schema changes. +FSB ships a dedicated OpenClaw + Hermes skill at `skills/fsb/` in the repo root. The skill is the canonical OpenClaw onboarding path: it runs the doctor flow against `fsb-mcp-server`, prints the OpenClaw stdio config block for the user to paste into OpenClaw's MCP config, and offers consent-gated install for any other MCP hosts detected on the same machine. The bare `--openclaw` flag in the FSB MCP installer stays manual / unsupported because OpenClaw's MCP config schema is still unstable across builds; the skill prints, the user pastes, no auto-write. Frontmatter ships with `name: fsb`, `version: 0.9.95`, `requires.bins: [node, npx]`, and `requires.env: []` so vault credentials never leave the FSB Chrome extension. The full marketing rundown (supported agents and hosts, MCP power story, skill features, one-line install command, grounded use cases, autopilot rules) lives at `https://full-selfbrowsing.com/agents`. Hermes users get the same skill: `node skills/fsb/scripts/print-hermes-yaml.mjs` prints the canonical `~/.hermes/config.yaml` `mcp_servers.fsb` block, and FSB v0.9.69 (PR #49) added `Hermes` to the v0.9.36 shared MCP client allowlist so action calls passing `client: "Hermes"` work without schema changes. ### ClawHub install path The /agents page exposes a one-click install on ClawHub at `https://clawhub.ai/lakshmanturlapati/full-selfbrowsing`. ClawHub is the recommended distribution surface for OpenClaw users who want the FSB skill registered without manually pasting stdio config. The skill source remains browsable at `https://github.com/fullselfbrowsing/FSB/tree/main/skills/fsb` for users who prefer reading before installing. diff --git a/showcase/angular/public/llms.txt b/showcase/angular/public/llms.txt index 5d2277cd6..46a06a7bb 100644 --- a/showcase/angular/public/llms.txt +++ b/showcase/angular/public/llms.txt @@ -1,7 +1,7 @@ # FSB (Full Self-Browsing) -FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task; FSB plans the clicks, types, and navigation to complete it. Multi-model AI (xAI, OpenAI, Anthropic, Gemini, OpenRouter, LM Studio, local), 60 browser actions, a 68-tool MCP surface, 142+ site guides, local-first execution, and BYO API keys. Current release: FSB v0.9.92 (extension) with fsb-mcp-server 0.11.2 (npm). +FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task; FSB plans the clicks, types, and navigation to complete it. Multi-model AI (xAI, OpenAI, Anthropic, Gemini, OpenRouter, LM Studio, local), 60 browser actions, a 68-tool MCP surface, 142+ site guides, local-first execution, and BYO API keys. Current release: FSB v0.9.95 (extension) with fsb-mcp-server 0.11.5 (npm). License: [MIT License](https://github.com/fullselfbrowsing/FSB/blob/main/LICENSE). diff --git a/showcase/angular/scripts/llms-full.source.md b/showcase/angular/scripts/llms-full.source.md index 22f116cb6..8a357669d 100644 --- a/showcase/angular/scripts/llms-full.source.md +++ b/showcase/angular/scripts/llms-full.source.md @@ -4,7 +4,7 @@ The About page embeds the public 33-second FSB trailer (uploaded September 6, 20 ## 1. Project Description -FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task in plain English; FSB plans the clicks, types, and navigation to complete it. The extension runs in your browser with your own API keys, encrypted local storage, and opt-out anonymous usage telemetry for aggregate public stats only. Multi-model AI (xAI Grok, OpenAI, Anthropic Claude, Google Gemini, OpenRouter, LM Studio, custom OpenAI-compatible endpoints), 56 canonical browser tools, a 68-tool MCP surface, 142+ site-specific guides, reactive trigger watchers, real file uploads, and a 128-app native capability catalog for verified first-party API execution. Current release: FSB v0.9.92 (Chrome extension) with fsb-mcp-server 0.11.2 (npm MCP package). +FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task in plain English; FSB plans the clicks, types, and navigation to complete it. The extension runs in your browser with your own API keys, encrypted local storage, and opt-out anonymous usage telemetry for aggregate public stats only. Multi-model AI (xAI Grok, OpenAI, Anthropic Claude, Google Gemini, OpenRouter, LM Studio, custom OpenAI-compatible endpoints), 56 canonical browser tools, a 68-tool MCP surface, 142+ site-specific guides, reactive trigger watchers, real file uploads, and a 128-app native capability catalog for verified first-party API execution. Current release: FSB v0.9.95 (Chrome extension) with fsb-mcp-server 0.11.5 (npm MCP package). License: [MIT License](https://github.com/fullselfbrowsing/FSB/blob/main/LICENSE). @@ -136,7 +136,7 @@ FSB exposes its tooling through a Model Context Protocol (MCP) server. External The MCP surface is the best way to use FSB with coding agents and external autonomy layers. FSB handles precise single-attempt browser execution, visual feedback, observability, and verification. Long-running scheduling and personality/identity decisions belong in the calling agent layer, such as OpenClaw or Claude Routines. ### OpenClaw skill -FSB ships a dedicated OpenClaw + Hermes skill at `skills/fsb/` in the repo root. The skill is the canonical OpenClaw onboarding path: it runs the doctor flow against `fsb-mcp-server`, prints the OpenClaw stdio config block for the user to paste into OpenClaw's MCP config, and offers consent-gated install for any other MCP hosts detected on the same machine. The bare `--openclaw` flag in the FSB MCP installer stays manual / unsupported because OpenClaw's MCP config schema is still unstable across builds; the skill prints, the user pastes, no auto-write. Frontmatter ships with `name: fsb`, `version: 0.9.92`, `requires.bins: [node, npx]`, and `requires.env: []` so vault credentials never leave the FSB Chrome extension. The full marketing rundown (supported agents and hosts, MCP power story, skill features, one-line install command, grounded use cases, autopilot rules) lives at `https://full-selfbrowsing.com/agents`. Hermes users get the same skill: `node skills/fsb/scripts/print-hermes-yaml.mjs` prints the canonical `~/.hermes/config.yaml` `mcp_servers.fsb` block, and FSB v0.9.69 (PR #49) added `Hermes` to the v0.9.36 shared MCP client allowlist so action calls passing `client: "Hermes"` work without schema changes. +FSB ships a dedicated OpenClaw + Hermes skill at `skills/fsb/` in the repo root. The skill is the canonical OpenClaw onboarding path: it runs the doctor flow against `fsb-mcp-server`, prints the OpenClaw stdio config block for the user to paste into OpenClaw's MCP config, and offers consent-gated install for any other MCP hosts detected on the same machine. The bare `--openclaw` flag in the FSB MCP installer stays manual / unsupported because OpenClaw's MCP config schema is still unstable across builds; the skill prints, the user pastes, no auto-write. Frontmatter ships with `name: fsb`, `version: 0.9.95`, `requires.bins: [node, npx]`, and `requires.env: []` so vault credentials never leave the FSB Chrome extension. The full marketing rundown (supported agents and hosts, MCP power story, skill features, one-line install command, grounded use cases, autopilot rules) lives at `https://full-selfbrowsing.com/agents`. Hermes users get the same skill: `node skills/fsb/scripts/print-hermes-yaml.mjs` prints the canonical `~/.hermes/config.yaml` `mcp_servers.fsb` block, and FSB v0.9.69 (PR #49) added `Hermes` to the v0.9.36 shared MCP client allowlist so action calls passing `client: "Hermes"` work without schema changes. ### ClawHub install path The /agents page exposes a one-click install on ClawHub at `https://clawhub.ai/lakshmanturlapati/full-selfbrowsing`. ClawHub is the recommended distribution surface for OpenClaw users who want the FSB skill registered without manually pasting stdio config. The skill source remains browsable at `https://github.com/fullselfbrowsing/FSB/tree/main/skills/fsb` for users who prefer reading before installing. diff --git a/showcase/angular/scripts/llms.source.md b/showcase/angular/scripts/llms.source.md index 38016fd2c..0db9d5731 100644 --- a/showcase/angular/scripts/llms.source.md +++ b/showcase/angular/scripts/llms.source.md @@ -1,6 +1,6 @@ # FSB (Full Self-Browsing) -FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task; FSB plans the clicks, types, and navigation to complete it. Multi-model AI (xAI, OpenAI, Anthropic, Gemini, OpenRouter, LM Studio, local), 60 browser actions, a 68-tool MCP surface, 142+ site guides, local-first execution, and BYO API keys. Current release: FSB v0.9.92 (extension) with fsb-mcp-server 0.11.2 (npm). +FSB (Full Self-Browsing) is an open-source Chrome extension that automates the browser through natural language. You describe a task; FSB plans the clicks, types, and navigation to complete it. Multi-model AI (xAI, OpenAI, Anthropic, Gemini, OpenRouter, LM Studio, local), 60 browser actions, a 68-tool MCP surface, 142+ site guides, local-first execution, and BYO API keys. Current release: FSB v0.9.95 (extension) with fsb-mcp-server 0.11.5 (npm). License: [MIT License](https://github.com/fullselfbrowsing/FSB/blob/main/LICENSE). diff --git a/showcase/angular/src/app/core/seo/version.ts b/showcase/angular/src/app/core/seo/version.ts index 6f73120a2..2f2788d80 100644 --- a/showcase/angular/src/app/core/seo/version.ts +++ b/showcase/angular/src/app/core/seo/version.ts @@ -1,2 +1,2 @@ // Generated by scripts/build-crawler-files.mjs from manifest.json -- do not edit by hand. -export const APP_VERSION = '0.9.92'; +export const APP_VERSION = '0.9.95'; diff --git a/showcase/angular/src/app/pages/agents/agents-page.component.ts b/showcase/angular/src/app/pages/agents/agents-page.component.ts index d9bd8e9ec..8f9f88c08 100644 --- a/showcase/angular/src/app/pages/agents/agents-page.component.ts +++ b/showcase/angular/src/app/pages/agents/agents-page.component.ts @@ -386,6 +386,7 @@ const HOST_CHIPS: readonly HostChip[] = [ { name: 'VS Code', logo: 'vscode.svg' }, { name: 'Windsurf', logo: 'windsurf.svg' }, { name: 'OpenCode', logo: 'opencode.svg' }, + { name: 'Cline', logo: 'cline.svg' }, ]; const BASE_INSTALL_COMMAND = 'npx -y fsb-mcp-server@latest install'; @@ -406,6 +407,7 @@ const INSTALL_HOSTS: readonly InstallHost[] = [ { name: 'vscode', flag: '--vscode' }, { name: 'windsurf', flag: '--windsurf' }, { name: 'opencode', flag: '--opencode' }, + { name: 'cline', flag: '--cline' }, { name: 'all', flag: '--all' }, ]; diff --git a/showcase/angular/src/app/pages/home/home-page.component.ts b/showcase/angular/src/app/pages/home/home-page.component.ts index ac9f75ab7..84895dfbe 100644 --- a/showcase/angular/src/app/pages/home/home-page.component.ts +++ b/showcase/angular/src/app/pages/home/home-page.component.ts @@ -82,6 +82,7 @@ export class HomePageComponent implements OnInit, AfterViewInit, OnDestroy { { id: 'windsurf', name: 'Windsurf', logo: 'windsurf.svg', flag: '--windsurf' }, { id: 'codex', name: 'Codex', logo: 'openai.svg', flag: '--codex' }, { id: 'opencode', name: 'OpenCode', logo: 'opencode.svg', flag: '--opencode' }, + { id: 'cline', name: 'Cline', logo: 'cline.svg', flag: '--cline' }, ]; readonly allClients: readonly InstallClient[] = [ { id: 'claude-code', name: 'Claude Code', logo: 'claude.svg', flag: '--claude-code', cmd: `${BASE_INSTALL_COMMAND} --claude-code` }, @@ -91,6 +92,7 @@ export class HomePageComponent implements OnInit, AfterViewInit, OnDestroy { { id: 'windsurf', name: 'Windsurf', logo: 'windsurf.svg', flag: '--windsurf', cmd: `${BASE_INSTALL_COMMAND} --windsurf` }, { id: 'codex', name: 'Codex', logo: 'openai.svg', flag: '--codex', cmd: `${BASE_INSTALL_COMMAND} --codex` }, { id: 'opencode', name: 'OpenCode', logo: 'opencode.svg', flag: '--opencode', cmd: `${BASE_INSTALL_COMMAND} --opencode` }, + { id: 'cline', name: 'Cline', logo: 'cline.svg', flag: '--cline', cmd: `${BASE_INSTALL_COMMAND} --cline` }, { id: 'openclaw', name: 'OpenClaw', logo: 'openclaw.svg', flag: '', cmd: 'npx -y fsb-mcp-server@latest' }, { id: 'all', name: $localize`:@@home.install.allClients:All Clients`, logo: 'all.svg', flag: '--all', cmd: `${BASE_INSTALL_COMMAND} --all` }, ]; @@ -543,6 +545,7 @@ const ENG_CHIPS: Readonly> = { { name: 'VS Code', logo: 'vscode.svg' }, { name: 'Windsurf', logo: 'windsurf.svg' }, { name: 'OpenCode', logo: 'opencode.svg' }, + { name: 'Cline', logo: 'cline.svg' }, { name: 'OpenClaw', logo: 'openclaw.svg' }, { name: 'Hermes', logo: 'hermes.png' }, ], diff --git a/showcase/angular/src/locale/messages.de.xlf b/showcase/angular/src/locale/messages.de.xlf index 40b334da4..2c4a7238f 100644 --- a/showcase/angular/src/locale/messages.de.xlf +++ b/showcase/angular/src/locale/messages.de.xlf @@ -6403,7 +6403,7 @@ Alle Clients src/app/pages/home/home-page.component.ts - 95 + 97 @@ -6411,7 +6411,7 @@ manuell src/app/pages/home/home-page.component.ts - 98 + 100 @@ -6419,7 +6419,7 @@ Full Self-Browsing | Browserautomatisierung mit KI und MCP src/app/pages/home/home-page.component.ts - 147 + 149 @@ -6427,7 +6427,7 @@ Automatisieren Sie Chrome mit Anweisungen in natürlicher Sprache oder verbinden Sie KI-Agenten über MCP. FSB ist quelloffen und arbeitet in Ihrer echten Browsersitzung. src/app/pages/home/home-page.component.ts - 148 + 150 @@ -6435,7 +6435,7 @@ Lokal ausgerichtete Chrome-Automatisierung und MCP-Browser-Ebene, mit der KI-Agenten handeln, beobachten, verifizieren, Trigger-Watcher verwenden, echte Dateien hochladen und geschützte Funktionen von Erstanbieter-APIs im Browser des Benutzers aufrufen können src/app/pages/home/home-page.component.ts - 303 + 305 @@ -6443,7 +6443,7 @@ KI-Browserautomatisierung und MCP-Werkzeuge src/app/pages/home/home-page.component.ts - 306 + 308 @@ -6451,7 +6451,7 @@ MCP-Server für Claude Code, Codex, Cursor, Windsurf und OpenClaw src/app/pages/home/home-page.component.ts - 312 + 314 @@ -6459,7 +6459,7 @@ Echte Browserautomatisierung über eine lokale Chrome-Erweiterung src/app/pages/home/home-page.component.ts - 313 + 315 @@ -6467,7 +6467,7 @@ Trigger-Watcher für reaktive DOM-Überwachung src/app/pages/home/home-page.component.ts - 314 + 316 @@ -6475,7 +6475,7 @@ Katalog nativer Erstanbieter-API-Funktionen mit geschützten invoke_capability-Aufrufen src/app/pages/home/home-page.component.ts - 315 + 317 @@ -6483,7 +6483,7 @@ Echte Datei-Uploads mit upload_file sowie Unterstützung für simuliertes drop_file src/app/pages/home/home-page.component.ts - 316 + 318 @@ -6491,7 +6491,7 @@ Abgrenzung für Vault und automatisches Ausfüllen von Zahlungsdaten, bei der unverarbeitete Geheimnisse nie die MCP-Bridge passieren src/app/pages/home/home-page.component.ts - 317 + 319 @@ -6499,7 +6499,7 @@ DOM-basiertes Seitenverständnis, Browseraktionen, visuelles Feedback, lokales Gedächtnis und eigene Modellschlüssel src/app/pages/home/home-page.component.ts - 318 + 320 @@ -6507,7 +6507,7 @@ MCP-Browserautomatisierung, KI-Browseragent, Trigger-Watcher, Aufrufe von Erstanbieter-API-Funktionen, Browsertests mit Claude Code, Browsertests mit Codex, selbstständige Browserautomatisierung src/app/pages/home/home-page.component.ts - 320 + 322 diff --git a/showcase/angular/src/locale/messages.es.xlf b/showcase/angular/src/locale/messages.es.xlf index 449b2b999..88129eba0 100644 --- a/showcase/angular/src/locale/messages.es.xlf +++ b/showcase/angular/src/locale/messages.es.xlf @@ -6403,7 +6403,7 @@ Todos los clientes src/app/pages/home/home-page.component.ts - 95 + 97 @@ -6411,7 +6411,7 @@ manual src/app/pages/home/home-page.component.ts - 98 + 100 @@ -6419,7 +6419,7 @@ Full Self-Browsing | Automatización del navegador con IA y MCP src/app/pages/home/home-page.component.ts - 147 + 149 @@ -6427,7 +6427,7 @@ Automatiza Chrome con instrucciones en lenguaje natural o conecta agentes de IA mediante MCP. FSB es de código abierto y funciona en tu sesión real del navegador. src/app/pages/home/home-page.component.ts - 148 + 150 @@ -6435,7 +6435,7 @@ Automatización de Chrome local-first y capa de navegador MCP que permite a los agentes de IA actuar, observar, verificar, usar vigilantes de activadores, cargar archivos reales e invocar capacidades protegidas de API propias en el navegador del usuario src/app/pages/home/home-page.component.ts - 303 + 305 @@ -6443,7 +6443,7 @@ Automatización del navegador con IA y herramientas MCP src/app/pages/home/home-page.component.ts - 306 + 308 @@ -6451,7 +6451,7 @@ Servidor MCP para Claude Code, Codex, Cursor, Windsurf y OpenClaw src/app/pages/home/home-page.component.ts - 312 + 314 @@ -6459,7 +6459,7 @@ Automatización de un navegador real mediante una extensión local de Chrome src/app/pages/home/home-page.component.ts - 313 + 315 @@ -6467,7 +6467,7 @@ Vigilantes de activadores para supervisar el DOM de forma reactiva src/app/pages/home/home-page.component.ts - 314 + 316 @@ -6475,7 +6475,7 @@ Catálogo de capacidades nativas de API propias con llamadas protegidas a invoke_capability src/app/pages/home/home-page.component.ts - 315 + 317 @@ -6483,7 +6483,7 @@ Cargas de archivos reales mediante upload_file y compatibilidad con drop_file sintético src/app/pages/home/home-page.component.ts - 316 + 318 @@ -6491,7 +6491,7 @@ Límite del almacén y el autocompletado de pagos, donde los secretos sin procesar nunca atraviesan el puente MCP src/app/pages/home/home-page.component.ts - 317 + 319 @@ -6499,7 +6499,7 @@ Comprensión de páginas basada en el DOM, acciones del navegador, indicaciones visuales, memoria local y claves de modelos propias src/app/pages/home/home-page.component.ts - 318 + 320 @@ -6507,7 +6507,7 @@ automatización de navegador MCP, agente de navegador con IA, vigilantes de activadores, llamadas a capacidades de API propias, pruebas del navegador con Claude Code, pruebas del navegador con Codex, automatización de navegación autónoma src/app/pages/home/home-page.component.ts - 320 + 322 diff --git a/showcase/angular/src/locale/messages.ja.xlf b/showcase/angular/src/locale/messages.ja.xlf index 5c6aa4d26..c5055da33 100644 --- a/showcase/angular/src/locale/messages.ja.xlf +++ b/showcase/angular/src/locale/messages.ja.xlf @@ -6403,7 +6403,7 @@ すべてのクライアント src/app/pages/home/home-page.component.ts - 95 + 97 @@ -6411,7 +6411,7 @@ 手動 src/app/pages/home/home-page.component.ts - 98 + 100 @@ -6419,7 +6419,7 @@ Full Self-Browsing | AI ブラウザー自動化と MCP src/app/pages/home/home-page.component.ts - 147 + 149 @@ -6427,7 +6427,7 @@ 自然言語の指示で Chrome を自動化するか、MCP で AI エージェントを接続できます。FSB はオープンソースで、実際のブラウザーセッションで動作します。 src/app/pages/home/home-page.component.ts - 148 + 150 @@ -6435,7 +6435,7 @@ AI エージェントがユーザーのブラウザ内で操作、観察、検証、トリガー監視、実ファイルのアップロード、保護されたファーストパーティ API ケイパビリティの呼び出しを行える、ローカル優先の Chrome 自動化・MCP ブラウザ層 src/app/pages/home/home-page.component.ts - 303 + 305 @@ -6443,7 +6443,7 @@ AI ブラウザ自動化と MCP ツール src/app/pages/home/home-page.component.ts - 306 + 308 @@ -6451,7 +6451,7 @@ Claude Code、Codex、Cursor、Windsurf、OpenClaw 向け MCP サーバー src/app/pages/home/home-page.component.ts - 312 + 314 @@ -6459,7 +6459,7 @@ ローカルの Chrome 拡張機能による実ブラウザ自動化 src/app/pages/home/home-page.component.ts - 313 + 315 @@ -6467,7 +6467,7 @@ DOM の変化をリアクティブに監視するトリガーウォッチャー src/app/pages/home/home-page.component.ts - 314 + 316 @@ -6475,7 +6475,7 @@ 保護された invoke_capability 呼び出しを備えたネイティブのファーストパーティ API ケイパビリティカタログ src/app/pages/home/home-page.component.ts - 315 + 317 @@ -6483,7 +6483,7 @@ upload_file による実ファイルのアップロードと、drop_file による合成ドロップのサポート src/app/pages/home/home-page.component.ts - 316 + 318 @@ -6491,7 +6491,7 @@ 生の機密情報が MCP ブリッジを越えない Vault・支払い情報自動入力の境界 src/app/pages/home/home-page.component.ts - 317 + 319 @@ -6499,7 +6499,7 @@ DOM に基づくページ理解、ブラウザ操作、視覚フィードバック、ローカルメモリー、持ち込みのモデルキー src/app/pages/home/home-page.component.ts - 318 + 320 @@ -6507,7 +6507,7 @@ MCP ブラウザ自動化、AI ブラウザエージェント、トリガーウォッチャー、ファーストパーティ API ケイパビリティ呼び出し、Claude Code ブラウザテスト、Codex ブラウザテスト、自律ブラウジング自動化 src/app/pages/home/home-page.component.ts - 320 + 322 diff --git a/showcase/angular/src/locale/messages.ko.xlf b/showcase/angular/src/locale/messages.ko.xlf index d3c5e1819..620b6c030 100644 --- a/showcase/angular/src/locale/messages.ko.xlf +++ b/showcase/angular/src/locale/messages.ko.xlf @@ -6403,7 +6403,7 @@ 모든 클라이언트 src/app/pages/home/home-page.component.ts - 95 + 97 @@ -6411,7 +6411,7 @@ 수동 src/app/pages/home/home-page.component.ts - 98 + 100 @@ -6419,7 +6419,7 @@ Full Self-Browsing | AI 브라우저 자동화 및 MCP src/app/pages/home/home-page.component.ts - 147 + 149 @@ -6427,7 +6427,7 @@ 자연어 지시로 Chrome을 자동화하거나 MCP로 AI 에이전트를 연결하세요. FSB는 오픈 소스이며 실제 브라우저 세션에서 작동합니다. src/app/pages/home/home-page.component.ts - 148 + 150 @@ -6435,7 +6435,7 @@ AI 에이전트가 사용자의 브라우저에서 행동하고, 관찰하고, 검증하고, 트리거 감시자를 사용하고, 실제 파일을 업로드하고, 보호된 1자 API 기능을 호출할 수 있게 하는 로컬 우선 Chrome 자동화 및 MCP 브라우저 계층 src/app/pages/home/home-page.component.ts - 303 + 305 @@ -6443,7 +6443,7 @@ AI 브라우저 자동화 및 MCP 도구 src/app/pages/home/home-page.component.ts - 306 + 308 @@ -6451,7 +6451,7 @@ Claude Code, Codex, Cursor, Windsurf, OpenClaw를 위한 MCP 서버 src/app/pages/home/home-page.component.ts - 312 + 314 @@ -6459,7 +6459,7 @@ 로컬 Chrome 확장 프로그램을 통한 실제 브라우저 자동화 src/app/pages/home/home-page.component.ts - 313 + 315 @@ -6467,7 +6467,7 @@ 반응형 DOM 모니터링을 위한 트리거 감시자 src/app/pages/home/home-page.component.ts - 314 + 316 @@ -6475,7 +6475,7 @@ 보호된 invoke_capability 호출을 갖춘 네이티브 1자 API 기능 카탈로그 src/app/pages/home/home-page.component.ts - 315 + 317 @@ -6483,7 +6483,7 @@ upload_file을 통한 실제 파일 업로드와 합성 drop_file 지원 src/app/pages/home/home-page.component.ts - 316 + 318 @@ -6491,7 +6491,7 @@ 원본 비밀 정보가 MCP 브리지를 넘지 않는 볼트 및 결제 자동 입력 경계 src/app/pages/home/home-page.component.ts - 317 + 319 @@ -6499,7 +6499,7 @@ DOM 기반 페이지 이해, 브라우저 동작, 시각적 피드백, 로컬 메모리, 사용자 소유 모델 키 src/app/pages/home/home-page.component.ts - 318 + 320 @@ -6507,7 +6507,7 @@ MCP 브라우저 자동화, AI 브라우저 에이전트, 트리거 감시자, 1자 API 기능 호출, Claude Code 브라우저 테스트, Codex 브라우저 테스트, 셀프 브라우징 자동화 src/app/pages/home/home-page.component.ts - 320 + 322 diff --git a/showcase/angular/src/locale/messages.xlf b/showcase/angular/src/locale/messages.xlf index 059ff09ce..63434c755 100644 --- a/showcase/angular/src/locale/messages.xlf +++ b/showcase/angular/src/locale/messages.xlf @@ -5615,98 +5615,98 @@ All Clients src/app/pages/home/home-page.component.ts - 95 + 97 manual src/app/pages/home/home-page.component.ts - 98 + 100 Full Self-Browsing | AI Browser Automation & MCP src/app/pages/home/home-page.component.ts - 147 + 149 Automate Chrome with natural-language instructions or connect AI agents through MCP. FSB is open source and works in your real browser session. src/app/pages/home/home-page.component.ts - 148 + 150 Local-first Chrome automation and MCP browser layer that lets AI agents act, observe, verify, use trigger watchers, upload real files, and invoke guarded first-party API capabilities in the user's browser src/app/pages/home/home-page.component.ts - 303 + 305 AI browser automation and MCP tools src/app/pages/home/home-page.component.ts - 306 + 308 MCP server for Claude Code, Codex, Cursor, Windsurf, and OpenClaw src/app/pages/home/home-page.component.ts - 312 + 314 Real browser automation through a local Chrome extension src/app/pages/home/home-page.component.ts - 313 + 315 Trigger watchers for reactive DOM monitoring src/app/pages/home/home-page.component.ts - 314 + 316 Native first-party API capability catalog with guarded invoke_capability calls src/app/pages/home/home-page.component.ts - 315 + 317 Real file uploads through upload_file plus synthetic drop_file support src/app/pages/home/home-page.component.ts - 316 + 318 Vault and payment autofill boundary where raw secrets never cross the MCP bridge src/app/pages/home/home-page.component.ts - 317 + 319 DOM-based page understanding, browser actions, visual feedback, local memory, and BYO model keys src/app/pages/home/home-page.component.ts - 318 + 320 MCP browser automation, AI browser agent, trigger watchers, first-party API capability calling, Claude Code browser testing, Codex browser testing, self-browsing automation src/app/pages/home/home-page.component.ts - 320 + 322 diff --git a/showcase/angular/src/locale/messages.zh-CN.xlf b/showcase/angular/src/locale/messages.zh-CN.xlf index f6d841380..a2f9deede 100644 --- a/showcase/angular/src/locale/messages.zh-CN.xlf +++ b/showcase/angular/src/locale/messages.zh-CN.xlf @@ -6403,7 +6403,7 @@ 所有客户端 src/app/pages/home/home-page.component.ts - 95 + 97 @@ -6411,7 +6411,7 @@ 手动 src/app/pages/home/home-page.component.ts - 98 + 100 @@ -6419,7 +6419,7 @@ Full Self-Browsing | AI 浏览器自动化与 MCP src/app/pages/home/home-page.component.ts - 147 + 149 @@ -6427,7 +6427,7 @@ 通过自然语言指令自动操作 Chrome,或通过 MCP 连接 AI 智能体。FSB 是开源软件,可在你的真实浏览器会话中运行。 src/app/pages/home/home-page.component.ts - 148 + 150 @@ -6435,7 +6435,7 @@ 本地优先的 Chrome 自动化与 MCP 浏览器层,使 AI 智能体能够在用户的浏览器中执行操作、观察、验证、使用触发器监视功能、上传真实文件,并调用受控的第一方 API 能力 src/app/pages/home/home-page.component.ts - 303 + 305 @@ -6443,7 +6443,7 @@ AI 浏览器自动化与 MCP 工具 src/app/pages/home/home-page.component.ts - 306 + 308 @@ -6451,7 +6451,7 @@ 适用于 Claude Code、Codex、Cursor、Windsurf 和 OpenClaw 的 MCP 服务器 src/app/pages/home/home-page.component.ts - 312 + 314 @@ -6459,7 +6459,7 @@ 通过本地 Chrome 扩展程序实现真实浏览器自动化 src/app/pages/home/home-page.component.ts - 313 + 315 @@ -6467,7 +6467,7 @@ 用于响应式 DOM 监控的触发器监视功能 src/app/pages/home/home-page.component.ts - 314 + 316 @@ -6475,7 +6475,7 @@ 原生第一方 API 能力目录,通过受控的 invoke_capability 调用执行 src/app/pages/home/home-page.component.ts - 315 + 317 @@ -6483,7 +6483,7 @@ 通过 upload_file 上传真实文件,并支持模拟 drop_file src/app/pages/home/home-page.component.ts - 316 + 318 @@ -6491,7 +6491,7 @@ Vault 与付款自动填充的安全边界,原始密钥绝不会穿过 MCP 桥接器 src/app/pages/home/home-page.component.ts - 317 + 319 @@ -6499,7 +6499,7 @@ 基于 DOM 的页面理解、浏览器操作、视觉反馈、本地记忆和自备模型密钥 src/app/pages/home/home-page.component.ts - 318 + 320 @@ -6507,7 +6507,7 @@ MCP 浏览器自动化、AI 浏览器智能体、触发器监视、第一方 API 能力调用、Claude Code 浏览器测试、Codex 浏览器测试、自主浏览自动化 src/app/pages/home/home-page.component.ts - 320 + 322 diff --git a/showcase/angular/src/locale/messages.zh-TW.xlf b/showcase/angular/src/locale/messages.zh-TW.xlf index 7691a87f1..c56cf9e2e 100644 --- a/showcase/angular/src/locale/messages.zh-TW.xlf +++ b/showcase/angular/src/locale/messages.zh-TW.xlf @@ -6403,7 +6403,7 @@ 所有用戶端 src/app/pages/home/home-page.component.ts - 95 + 97 @@ -6411,7 +6411,7 @@ 手動 src/app/pages/home/home-page.component.ts - 98 + 100 @@ -6419,7 +6419,7 @@ Full Self-Browsing | AI 瀏覽器自動化與 MCP src/app/pages/home/home-page.component.ts - 147 + 149 @@ -6427,7 +6427,7 @@ 透過自然語言指令自動操作 Chrome,或透過 MCP 連接 AI 代理程式。FSB 是開源軟體,可在你的真實瀏覽器工作階段中運作。 src/app/pages/home/home-page.component.ts - 148 + 150 @@ -6435,7 +6435,7 @@ 本機優先的 Chrome 自動化與 MCP 瀏覽器層,讓 AI 代理能在使用者的瀏覽器中操作、觀察、驗證、使用觸發器監看、上傳真實檔案,並呼叫受保護的第一方 API 能力 src/app/pages/home/home-page.component.ts - 303 + 305 @@ -6443,7 +6443,7 @@ AI 瀏覽器自動化與 MCP 工具 src/app/pages/home/home-page.component.ts - 306 + 308 @@ -6451,7 +6451,7 @@ 適用於 Claude Code、Codex、Cursor、Windsurf 與 OpenClaw 的 MCP 伺服器 src/app/pages/home/home-page.component.ts - 312 + 314 @@ -6459,7 +6459,7 @@ 透過本機 Chrome 擴充功能執行真實瀏覽器自動化 src/app/pages/home/home-page.component.ts - 313 + 315 @@ -6467,7 +6467,7 @@ 用於即時 DOM 監控的觸發器監看器 src/app/pages/home/home-page.component.ts - 314 + 316 @@ -6475,7 +6475,7 @@ 具備受保護 invoke_capability 呼叫的原生第一方 API 能力目錄 src/app/pages/home/home-page.component.ts - 315 + 317 @@ -6483,7 +6483,7 @@ 透過 upload_file 上傳真實檔案,並支援 drop_file 合成拖放 src/app/pages/home/home-page.component.ts - 316 + 318 @@ -6491,7 +6491,7 @@ Vault 與付款自動填寫邊界,確保原始機密資料不會跨越 MCP 橋接器 src/app/pages/home/home-page.component.ts - 317 + 319 @@ -6499,7 +6499,7 @@ 以 DOM 為基礎的頁面理解、瀏覽器操作、視覺回饋、本機記憶,以及自備模型金鑰 src/app/pages/home/home-page.component.ts - 318 + 320 @@ -6507,7 +6507,7 @@ MCP 瀏覽器自動化、AI 瀏覽器代理、觸發器監看、第一方 API 能力呼叫、Claude Code 瀏覽器測試、Codex 瀏覽器測試、自主瀏覽自動化 src/app/pages/home/home-page.component.ts - 320 + 322 diff --git a/showcase/assets/providers/cline.svg b/showcase/assets/providers/cline.svg new file mode 100644 index 000000000..956a2eb3e --- /dev/null +++ b/showcase/assets/providers/cline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/showcase/server/package-lock.json b/showcase/server/package-lock.json index 8dff5630d..0a3cd72b2 100644 --- a/showcase/server/package-lock.json +++ b/showcase/server/package-lock.json @@ -1,12 +1,12 @@ { "name": "fsb-server", - "version": "0.9.92", + "version": "0.9.95", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fsb-server", - "version": "0.9.92", + "version": "0.9.95", "dependencies": { "better-sqlite3": "^11.0.0", "cors": "^2.8.5", diff --git a/showcase/server/package.json b/showcase/server/package.json index c57fbd3c4..1d8f43431 100644 --- a/showcase/server/package.json +++ b/showcase/server/package.json @@ -1,6 +1,6 @@ { "name": "fsb-server", - "version": "0.9.92", + "version": "0.9.95", "description": "FSB Background Agents backend server with result storage and dashboard", "main": "server.js", "scripts": { diff --git a/showcase/server/src/routes/telemetry.js b/showcase/server/src/routes/telemetry.js index 0b1c21e4f..94ff38c9a 100644 --- a/showcase/server/src/routes/telemetry.js +++ b/showcase/server/src/routes/telemetry.js @@ -8,7 +8,7 @@ * - 50-event batch cap (400 batch_too_large) * - versioned allowlist (400 unknown_field) * - UUIDv4 shape (400 invalid_event_id / invalid_install_uuid) - * - 13 + 'unknown' mcp_client allowlist (400 invalid_mcp_client) + * - 14 + 'unknown' mcp_client allowlist (400 invalid_mcp_client) * - event_type allowlist (400 invalid_event_type) * - timestamp tolerance (+5min / -7d window; drop offending events; 400 if any dropped) * - per-IP rate limit (30 batches/min via express-rate-limit; CVE-2026-30827 fixed) @@ -57,10 +57,11 @@ const ALLOWED_EVENT_FIELDS = new Set([ 'active_count_version', ]); -// 13 labels from mcp/src/tools/visual-session.ts (v0.9.36 allowlist) + 'unknown'. +// The 13 v0.9.36 labels from mcp/src/tools/visual-session.ts, Cline, and 'unknown'. const MCP_CLIENT_ALLOWLIST = new Set([ 'Claude', 'Codex', 'ChatGPT', 'Perplexity', 'Windsurf', 'Cursor', 'Antigravity', 'OpenCode', 'OpenClaw', 'OpenClaw 🦀', 'Grok', 'Gemini', 'Hermes', + 'Cline', 'unknown', ]); diff --git a/showcase/server/src/telemetry/housekeeper.js b/showcase/server/src/telemetry/housekeeper.js index e467d1893..ec663de56 100644 --- a/showcase/server/src/telemetry/housekeeper.js +++ b/showcase/server/src/telemetry/housekeeper.js @@ -18,9 +18,9 @@ * - v0.9.70: floor=2 (dev-phase visibility tradeoff) * - v0.9.70+ (this change): floor=1, effectively DISABLING the floor. * Rationale: MCP_CLIENT_ALLOWLIST in routes/telemetry.js is a FIXED - * PUBLIC 13-label set (Claude, Codex, ChatGPT, Perplexity, Windsurf, + * PUBLIC 14-label set (Claude, Codex, ChatGPT, Perplexity, Windsurf, * Cursor, Antigravity, OpenCode, OpenClaw, OpenClaw 🦀, Grok, Gemini, - * Hermes + 'unknown'). The label set carries no identifying information + * Hermes, Cline + 'unknown'). The label set carries no identifying information * beyond the per-label install count, and that count is already exposed * via total_users. Bucketing single-install labels into "Other" only * hides legitimate diversity at single-digit install totals (the live diff --git a/skills/fsb/SKILL.md b/skills/fsb/SKILL.md index 9f189c88d..6a9f6cdf8 100644 --- a/skills/fsb/SKILL.md +++ b/skills/fsb/SKILL.md @@ -1,7 +1,7 @@ --- name: fsb description: FSB drives the user's Chrome via the FSB extension and an MCP bridge for live web tasks. -version: 0.9.92 +version: 0.9.95 user-invocable: true requires: bins: [node, npx] diff --git a/skills/fsb/references/multi-agent-contract.md b/skills/fsb/references/multi-agent-contract.md index 5adc7174e..bc874b08f 100644 --- a/skills/fsb/references/multi-agent-contract.md +++ b/skills/fsb/references/multi-agent-contract.md @@ -1,4 +1,4 @@ -# FSB multi-agent contract (current as of v0.9.92) +# FSB multi-agent contract (current as of v0.9.95) This file documents the rules that make FSB's per-agent tab ownership work. Anyone calling FSB tools through MCP MUST follow these rules; breaking them produces typed errors that recover cleanly only when the caller knows the contract. The contract is small (six error names + the `agent_id` rule + the `back` tool), but every rule matters: bypassing one corrupts the ownership graph and the only recovery is closing the affected tab. diff --git a/store-assets/chrome-web-store/listing-copy.md b/store-assets/chrome-web-store/listing-copy.md index e8ac037ec..8a24bfac7 100644 --- a/store-assets/chrome-web-store/listing-copy.md +++ b/store-assets/chrome-web-store/listing-copy.md @@ -2,7 +2,7 @@ ## Title -FSB v0.9.92 +FSB v0.9.95 ## Summary diff --git a/tests/delegation-sidepanel-tab-follow.test.js b/tests/delegation-sidepanel-tab-follow.test.js index 5963c3a73..97158c6d1 100644 --- a/tests/delegation-sidepanel-tab-follow.test.js +++ b/tests/delegation-sidepanel-tab-follow.test.js @@ -657,6 +657,8 @@ async function adopt(options) { vm.runInContext(extractNamedFunction(dispatcherSource, 'canonicalLabelFromClientInfo'), labelContext); check(vm.runInContext("canonicalLabelFromClientInfo({ name: 'claude-code', version: '1.0' })", labelContext) === 'Claude' && vm.runInContext("canonicalLabelFromClientInfo({ name: 'Codex CLI' })", labelContext) === 'Codex' + && vm.runInContext("canonicalLabelFromClientInfo({ name: 'Cline' })", labelContext) === 'Cline' + && vm.runInContext("canonicalLabelFromClientInfo({ name: '@cline/core' })", labelContext) === 'Cline' && vm.runInContext('canonicalLabelFromClientInfo(null)', labelContext) === null, 'clientInfo names canonicalize to chip labels'); const registerBody = extractFunctionBody(dispatcherSource, 'handleAgentRegisterRoute'); diff --git a/tests/mcp-client-merged-view.test.js b/tests/mcp-client-merged-view.test.js index 48b9ed706..d2bce17fc 100644 --- a/tests/mcp-client-merged-view.test.js +++ b/tests/mcp-client-merged-view.test.js @@ -198,6 +198,9 @@ async function main() { ['codexcli', 'codex'], ['opencode', 'opencode'], ['opencodecli', 'opencode'], + ['cline', 'cline'], + [' Cline ', 'cline'], + ['@cline/core', 'cline'], ['openclaw', 'openclaw'] ]); for (const [name, expected] of aliasesExpected) { diff --git a/tests/mcp-install-platforms.test.js b/tests/mcp-install-platforms.test.js index 628d13a85..185d2cb28 100644 --- a/tests/mcp-install-platforms.test.js +++ b/tests/mcp-install-platforms.test.js @@ -28,6 +28,7 @@ const cliPath = path.join(repoRoot, 'mcp', 'build', 'index.js'); const PRODUCT_VERSION = require('../extension/manifest.json').version; const EXTENSION_ID = 'badgafnfchcihdfnjneklogedcdkmjfk'; const DEVELOPMENT_EXTENSION_ID = 'abcdefghijklmnopabcdefghijklmnop'; +const CLINE_ENV_KEYS = ['CLINE_DIR', 'CLINE_DATA_DIR', 'CLINE_MCP_SETTINGS_PATH']; function runCli(args, fixture, options = {}) { const env = { @@ -38,6 +39,9 @@ function runCli(args, fixture, options = {}) { ...(options.env ?? {}), }; if (!Object.hasOwn(options.env ?? {}, 'npm_execpath')) delete env.npm_execpath; + for (const key of CLINE_ENV_KEYS) { + if (!Object.hasOwn(options.env ?? {}, key)) delete env[key]; + } return spawnSync(process.execPath, [cliPath, ...args], { cwd: repoRoot, encoding: 'utf8', @@ -75,6 +79,23 @@ function readText(filePath) { return fs.readFileSync(filePath, 'utf8'); } +function readJson(filePath) { + return JSON.parse(readText(filePath)); +} + +function clineSettingsPath(fixture) { + return path.join(fixture.home, '.cline', 'data', 'settings', 'cline_mcp_settings.json'); +} + +function clineLegacySettingsPath(fixture) { + const storage = ['User', 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json']; + if (process.platform === 'darwin') { + return path.join(fixture.home, 'Library', 'Application Support', 'Code', ...storage); + } + if (process.platform === 'win32') return path.join(fixture.appData, 'Code', ...storage); + return path.join(fixture.home, '.config', 'Code', ...storage); +} + function assertIncludes(text, needle, msg) { assert(text.includes(needle), `${msg} (missing: ${needle})`); } @@ -169,6 +190,12 @@ function run() { '.codeium/windsurf/mcp_config.json', 'dry-run shows the Windsurf app config path', ); + assertIncludes( + output, + path.join('.cline', 'data', 'settings', 'cline_mcp_settings.json'), + 'dry-run shows the shared Cline settings path', + ); + assertIncludes(output, '"timeout": 1800', 'dry-run shows the Cline request timeout'); assertNotIncludes(output, 'Native messaging host', 'install --all excludes the native messaging host'); assertNotIncludes(output, 'native-host', 'install --all output does not widen to the native target'); }); @@ -299,6 +326,142 @@ function run() { ); }); + console.log('\n--- cline shared settings install + uninstall ---'); + withTempHome('mcp-install-cline-shared', (fixture) => { + const settingsPath = clineSettingsPath(fixture); + const existingServer = { + transport: { type: 'stdio', command: 'uvx', args: ['existing-server'] }, + autoApprove: ['search'], + }; + writeText(settingsPath, JSON.stringify({ mcpServers: { existing: existingServer } }, null, 2)); + + const result = runCli(['install', '--cline'], fixture); + const output = `${result.stdout}${result.stderr}`; + const config = readJson(settingsPath); + + assertEqual(result.status, 0, 'cline install exits cleanly'); + assertIncludes(output, 'Installed to Cline: ', 'cline install reports the shared settings target'); + assertEqual( + JSON.stringify(config.mcpServers.fsb), + JSON.stringify({ command: 'npx', args: ['-y', 'fsb-mcp-server@latest'], timeout: 1800 }), + 'cline install writes the stdio entry with a 30-minute request timeout', + ); + assertEqual( + JSON.stringify(config.mcpServers.existing), + JSON.stringify(existingServer), + 'cline install preserves other servers byte-for-byte', + ); + + const uninstallResult = runCli(['uninstall', '--cline'], fixture); + const uninstalled = readJson(settingsPath); + assertEqual(uninstallResult.status, 0, 'cline uninstall exits cleanly'); + assertIncludes( + `${uninstallResult.stdout}${uninstallResult.stderr}`, + 'Removed from Cline', + 'cline uninstall reports a successful removal', + ); + assert(!Object.hasOwn(uninstalled.mcpServers, 'fsb'), 'cline uninstall removes the fsb entry'); + assert(Object.hasOwn(uninstalled.mcpServers, 'existing'), 'cline uninstall keeps other servers'); + }); + + console.log('\n--- cline reinstall keeps client-owned settings ---'); + withTempHome('mcp-install-cline-preserve', (fixture) => { + const settingsPath = clineSettingsPath(fixture); + const userEntry = { + command: 'npx', + args: ['-y', 'fsb-mcp-server@latest'], + timeout: 3600, + autoApprove: ['navigate', 'read_page'], + disabled: false, + }; + writeText(settingsPath, JSON.stringify({ mcpServers: { fsb: userEntry } }, null, 2)); + + const unchanged = runCli(['install', '--cline'], fixture); + assertIncludes( + `${unchanged.stdout}${unchanged.stderr}`, + 'Cline already configured', + 'an entry that differs only in Cline-owned settings counts as configured', + ); + assertEqual( + JSON.stringify(readJson(settingsPath).mcpServers.fsb), + JSON.stringify(userEntry), + 'reinstall leaves the timeout, auto-approve list, and toggle untouched', + ); + + writeText(settingsPath, JSON.stringify({ + mcpServers: { + fsb: { + transport: { type: 'stdio', command: 'node', args: ['/old/fsb/build/index.js'] }, + disabled: true, + autoApprove: ['click'], + }, + }, + }, null, 2)); + const nested = runCli(['install', '--cline'], fixture); + assertIncludes( + `${nested.stdout}${nested.stderr}`, + 'Updated Cline', + 'a stale nested-transport entry is updated', + ); + assertEqual( + JSON.stringify(readJson(settingsPath).mcpServers.fsb), + JSON.stringify({ + command: 'npx', + args: ['-y', 'fsb-mcp-server@latest'], + timeout: 1800, + disabled: true, + autoApprove: ['click'], + }), + 'updating a nested entry replaces its transport and keeps Cline-owned settings', + ); + }); + + console.log('\n--- cline legacy VS Code settings fallback ---'); + withTempHome('mcp-install-cline-legacy', (fixture) => { + const legacyPath = clineLegacySettingsPath(fixture); + writeText(legacyPath, JSON.stringify({ mcpServers: {} }, null, 2)); + + const legacyResult = runCli(['install', '--cline'], fixture); + assertEqual(legacyResult.status, 0, 'legacy cline install exits cleanly'); + assertIncludes( + `${legacyResult.stdout}${legacyResult.stderr}`, + 'Installed to Cline (legacy VS Code settings)', + 'cline falls back to the legacy VS Code settings when no shared settings exist', + ); + assert(Object.hasOwn(readJson(legacyPath).mcpServers, 'fsb'), 'legacy fallback writes the fsb entry'); + + fs.mkdirSync(path.dirname(clineSettingsPath(fixture)), { recursive: true }); + const sharedPreview = runCli(['install', '--cline', '--dry-run'], fixture); + assertIncludes( + `${sharedPreview.stdout}${sharedPreview.stderr}`, + clineSettingsPath(fixture), + 'a shared settings directory outranks a leftover legacy settings file', + ); + }); + + console.log('\n--- cline settings path environment overrides ---'); + withTempHome('mcp-install-cline-env', (fixture) => { + const explicitPath = path.join(fixture.root, 'explicit', 'cline_mcp_settings.json'); + const clineDir = path.join(fixture.root, 'cline-home'); + fs.mkdirSync(path.dirname(explicitPath), { recursive: true }); + fs.mkdirSync(path.join(clineDir, 'data', 'settings'), { recursive: true }); + + const explicitResult = runCli(['install', '--cline'], fixture, { + env: { CLINE_MCP_SETTINGS_PATH: explicitPath, CLINE_DIR: clineDir }, + }); + assertEqual(explicitResult.status, 0, 'cline install honors CLINE_MCP_SETTINGS_PATH'); + assert(fs.existsSync(explicitPath), 'CLINE_MCP_SETTINGS_PATH outranks CLINE_DIR'); + + const dirPreview = runCli(['install', '--cline', '--dry-run'], fixture, { + env: { CLINE_DIR: clineDir }, + }); + assertIncludes( + `${dirPreview.stdout}${dirPreview.stderr}`, + path.join(clineDir, 'data', 'settings', 'cline_mcp_settings.json'), + 'cline install honors CLINE_DIR', + ); + }); + console.log('\n--- native target isolation ---'); withTempHome('mcp-native-target-isolation', (fixture) => { const helpResult = runCli(['--help'], fixture); diff --git a/tests/mcp-pricing.test.js b/tests/mcp-pricing.test.js index fd5dddb1e..841eab7b0 100644 --- a/tests/mcp-pricing.test.js +++ b/tests/mcp-pricing.test.js @@ -550,29 +550,32 @@ const pricingData = JSON.parse(fs.readFileSync(DATA_PATH, 'utf-8')); // (12 Tier-1 MCP-supporting AI clients appended). The 12 new entries // (Cline, Continue, Zed, VS Code, Copilot, JetBrains, Xcode, Eclipse, // Cody, Roo Code, Kiro, Goose) are owner-chip / overlay labels for - // widely used MCP clients; FSB does not ship default-model mappings - // for them (default model selection varies by per-client config and - // is not authored at this layer). The pricing parity contract is - // therefore scoped to: (a) every default-model entry must be on - // the allowlist (extra-in-pricing remains zero -- prevents drift in - // the other direction), (b) the 13 ORIGINALLY paired core labels - // must still have default-model entries (preserves the v0.9.36 - // pricing pairing for the historical client set), and (c) the - // OpenClaw crab grapheme survives byte-exact on both sides. + // widely used MCP clients; apart from Cline, which later became an + // officially supported install target with a conservative fallback, + // FSB does not ship default-model mappings for them (default model + // selection varies by per-client config and is not authored at this + // layer). The pricing parity contract is therefore scoped to: (a) + // every default-model entry must be on the allowlist (extra-in-pricing + // remains zero -- prevents drift in the other direction), (b) the 13 + // ORIGINALLY paired core labels plus Cline must still have + // default-model entries (preserves the v0.9.36 pricing pairing for the + // historical client set), and (c) the OpenClaw crab grapheme survives + // byte-exact on both sides. passAssert(labels.length >= 13, 'visual-session allowlist has >= 13 labels (parsed ' + labels.length + ' via ' + labelSource + ')'); const allowlistSet = new Set(labels); const pricingSet = new Set(Object.keys(pricingData.client_default_model)); // Lock the 13 ORIGINAL core labels (the historical v0.9.36 set - // through Hermes) to still have default-model entries. The 12 - // quick-task additions are intentionally excluded from this lock + // through Hermes) to still have default-model entries, plus Cline, + // the one quick-task addition later promoted to the public telemetry + // set. The other quick-task additions stay excluded from this lock // because default-model mapping for them is out of scope for the // allowlist expansion. const CORE_PRICING_PAIRED_LABELS = [ 'Claude', 'Codex', 'ChatGPT', 'Perplexity', 'Windsurf', 'Cursor', 'Antigravity', 'OpenCode', 'OpenClaw', 'OpenClaw \u{1F980}', - 'Grok', 'Gemini', 'Hermes', + 'Grok', 'Gemini', 'Hermes', 'Cline', ]; const missingCorePricing = CORE_PRICING_PAIRED_LABELS.filter(function (l) { return !pricingSet.has(l); }); passAssertEqual(missingCorePricing.length, 0, diff --git a/tests/mcp-setup-guidance.test.js b/tests/mcp-setup-guidance.test.js index 88ce42ce1..8eec83af3 100644 --- a/tests/mcp-setup-guidance.test.js +++ b/tests/mcp-setup-guidance.test.js @@ -51,6 +51,10 @@ const requiredSetupLines = [ label: 'Windsurf distinguishes app and plugin config surfaces and tells the user to refresh or reload', needles: ['~/.codeium/windsurf/mcp_config.json', '~/.codeium/mcp_config.json', 'reload'], }, + { + label: 'Cline documents the shared settings file and the request timeout it needs', + needles: ['~/.cline/data/settings/cline_mcp_settings.json', 'CLINE_MCP_SETTINGS_PATH', '"timeout": 1800'], + }, { label: 'OpenCode includes the manual mcp.local snippet', needles: ['OpenCode', '"mcp"', '"type": "local"', '"command": ["npx", "-y", "fsb-mcp-server@latest"]'], diff --git a/tests/onboarding-agent-provider-clicks.test.js b/tests/onboarding-agent-provider-clicks.test.js index a9ec7a0a7..879e9a168 100644 --- a/tests/onboarding-agent-provider-clicks.test.js +++ b/tests/onboarding-agent-provider-clicks.test.js @@ -18,7 +18,8 @@ const ALL_CLIENT_IDS = [ 'vscode', 'windsurf', 'codex', - 'opencode' + 'opencode', + 'cline' ]; function sha256(filePath) { @@ -138,7 +139,7 @@ function createHarness(initialStore, options) { const allIdsMatch = ONBOARDING_SOURCE.match(/const ALL_INSTALL_CLIENT_IDS = \[([\s\S]*?)\];/); assert(allIdsMatch, 'all-client expansion constant exists'); const sourceAllIds = Array.from(allIdsMatch[1].matchAll(/'([^']+)'/g), (match) => match[1]); - assert.deepStrictEqual(sourceAllIds, ALL_CLIENT_IDS, 'all expands to the exact seven flag-backed ids'); + assert.deepStrictEqual(sourceAllIds, ALL_CLIENT_IDS, 'all expands to the exact eight flag-backed ids'); assert(!sourceAllIds.includes('openclaw'), 'all expansion excludes openclaw'); const copyCommandSource = extractFunction(ONBOARDING_SOURCE, 'copyCommand'); @@ -229,7 +230,7 @@ function createHarness(initialStore, options) { await harness.api.flushCopyWrites(); const envelope = plain(harness.store.fsbAgentProviders); assert.deepStrictEqual(Object.keys(envelope.clicked).sort(), ALL_CLIENT_IDS.slice().sort(), - 'all writes exactly seven clicked entries'); + 'all writes exactly eight clicked entries'); ALL_CLIENT_IDS.forEach((id) => { assert.deepStrictEqual(envelope.clicked[id], { count: 1, diff --git a/tests/onboarding-first-run.test.js b/tests/onboarding-first-run.test.js index 6b585d4da..61802d83a 100644 --- a/tests/onboarding-first-run.test.js +++ b/tests/onboarding-first-run.test.js @@ -126,7 +126,7 @@ async function loadInitConfigHarness() { const clientIds = context.FSB_ONBOARDING_INSTALL_CLIENTS.map((client) => client.id).sort(); assert.strictEqual( JSON.stringify(clientIds), - JSON.stringify(['all', 'claude-code', 'claude-desktop', 'codex', 'cursor', 'openclaw', 'opencode', 'vscode', 'windsurf'].sort()), + JSON.stringify(['all', 'claude-code', 'claude-desktop', 'cline', 'codex', 'cursor', 'openclaw', 'opencode', 'vscode', 'windsurf'].sort()), 'MCP install client list includes all planned targets' ); } diff --git a/tests/server-telemetry-allowlist.test.js b/tests/server-telemetry-allowlist.test.js index f55daec30..d8fb7c971 100644 --- a/tests/server-telemetry-allowlist.test.js +++ b/tests/server-telemetry-allowlist.test.js @@ -129,7 +129,7 @@ function post(events) { let parsed5 = null; try { parsed5 = JSON.parse(r5.body); } catch {} check('error === invalid_event_id for bad uuid', parsed5 && parsed5.error === 'invalid_event_id', `body=${r5.body}`); - // Sanity: bad mcp_client (not in 13+unknown allowlist). + // Sanity: bad mcp_client (not in 14+unknown allowlist). const ev6 = baseEvent(); ev6.mcp_client = 'Other'; const r6 = await post([ev6]); @@ -141,11 +141,17 @@ function post(events) { const r7 = await post([ev7]); check('bad event_type -> 400 invalid_event_type', r7.statusCode === 400, `got ${r7.statusCode}, body=${r7.body}`); - // mcp_client 'unknown' is allowed (14th value in the allowlist). + // mcp_client 'unknown' is allowed (15th value in the allowlist). const ev8 = baseEvent(); ev8.mcp_client = 'unknown'; const r8 = await post([ev8]); - check('mcp_client === "unknown" -> 200 (14th allowed value)', r8.statusCode === 200, `got ${r8.statusCode}, body=${r8.body}`); + check('mcp_client === "unknown" -> 200 (15th allowed value)', r8.statusCode === 200, `got ${r8.statusCode}, body=${r8.body}`); + + // Cline joined the public label set after the v0.9.36 allowlist. + const evCline = baseEvent(); + evCline.mcp_client = 'Cline'; + const rCline = await post([evCline]); + check('mcp_client === "Cline" -> 200', rCline.statusCode === 200, `got ${rCline.statusCode}, body=${rCline.body}`); // The emoji-bearing allowlist label is accepted. const ev9 = baseEvent();