Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,25 @@

All notable changes to Agent Switch are documented here.

## 0.3.0 - 2026-08-11

### Added

- Complete English and Simplified Chinese native-app localization catalogs.
- Real, sanitized product visuals and privacy-safe repository activity graphics.
- Project metadata for software citation and answer-engine discovery.

### Changed

- Homebrew is now the canonical stranger-first install and update path.
- README onboarding, trust boundaries, recovery guidance, and bilingual parity were refreshed.
- Test credentials now use unmistakably synthetic, non-provider-shaped fixture values.

### Security

- Repository metrics keep owner-only Traffic collection inside GitHub Actions and publish only a generated aggregate image.
- Release guidance continues to disclose ad-hoc signing and the absence of Apple notarization.

## 0.2.0 - 2026-07-23

### Added
Expand Down
4 changes: 2 additions & 2 deletions CITATION.cff
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ authors:
repository-code: "https://github.com/JNHFlow21/agent-switch"
url: "https://github.com/JNHFlow21/agent-switch"
license: MIT
version: 0.2.0
date-released: 2026-07-26
version: 0.3.0
date-released: 2026-08-11
keywords:
- MCP
- macOS
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ credential store, and deterministic per-agent projections.
> Agent Switch is **alpha software**. The public app is ad-hoc signed and is not
> Apple-notarized. The project-owned Homebrew Cask therefore removes Gatekeeper
> quarantine after installation. Review the [public source](https://github.com/JNHFlow21/agent-switch)
> and [release checksums](https://github.com/JNHFlow21/agent-switch/releases/tag/v0.2.0)
> and [release checksums](https://github.com/JNHFlow21/agent-switch/releases/tag/v0.3.0)
> before installing.

The native app follows the macOS language preference and ships complete
Expand Down Expand Up @@ -77,7 +77,7 @@ Homebrew:
Expected first result:

```text
agent-switch --version -> agent-switch 0.2.0
agent-switch --version -> agent-switch 0.3.0
Native app -> /Applications/Agent Switch.app
```

Expand Down
4 changes: 2 additions & 2 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Agent Switch 用一个本地 MCP 注册表、一个私密凭据存储和可重
> Agent Switch 目前仍是 **Alpha 软件**。公开 App 使用 ad-hoc 签名,尚未经过
> Apple 公证,因此项目自己的 Homebrew Cask 会在安装完成后移除 Gatekeeper
> quarantine。安装前请先检查[公开源码](https://github.com/JNHFlow21/agent-switch)
> 和[发行校验和](https://github.com/JNHFlow21/agent-switch/releases/tag/v0.2.0)。
> 和[发行校验和](https://github.com/JNHFlow21/agent-switch/releases/tag/v0.3.0)。

原生应用会跟随 macOS 的语言偏好,并完整提供**英文**与**简体中文**界面。

Expand Down Expand Up @@ -74,7 +74,7 @@ Homebrew 会:
首次安装成功后应当看到:

```text
agent-switch --version -> agent-switch 0.2.0
agent-switch --version -> agent-switch 0.3.0
原生应用 -> /Applications/Agent Switch.app
```

Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.2.0
0.3.0
4 changes: 2 additions & 2 deletions macos-app/AgentSwitch/AgentSwitch.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 0.2.0;
MARKETING_VERSION = 0.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jnhflow.AgentSwitch;
PRODUCT_NAME = "Agent Switch";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -388,7 +388,7 @@
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 0.2.0;
MARKETING_VERSION = 0.3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jnhflow.AgentSwitch;
PRODUCT_NAME = "Agent Switch";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
2 changes: 1 addition & 1 deletion macos-app/AgentSwitch/AgentSwitch/Resources/L10n.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import Foundation

enum AppVersion {
static let current = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.2.0"
static let current = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? "0.3.0"
}

enum L10n {
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "agent-switch"
version = "0.2.0"
version = "0.3.0"
description = "Local control plane for AI agents, MCP servers, Skills, CLI tools, and secrets."
readme = "README.md"
license = {file = "LICENSE"}
Expand Down
2 changes: 1 addition & 1 deletion src/agent_switch/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Agent Switch runtime package."""

__version__ = "0.2.0"
__version__ = "0.3.0"
2 changes: 1 addition & 1 deletion src/agent_switch/mcp/bird_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def build_server() -> StdioMCPServer:
string_arg = {"type": "string"}
return StdioMCPServer(
name="agent-birdread",
version="0.2.0",
version="0.3.0",
tools=[
Tool(
name="read_x_post",
Expand Down
2 changes: 1 addition & 1 deletion src/agent_switch/mcp/xcli_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def build_server() -> StdioMCPServer:
string_arg = {"type": "string"}
return StdioMCPServer(
name="agent-xurl-fallback",
version="0.2.0",
version="0.3.0",
tools=[
Tool(
name="x_api_get_post",
Expand Down
14 changes: 7 additions & 7 deletions tests/integration/test_reconcile_fixture_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,13 @@ def test_reconcile_converges_and_second_run_is_idempotent(self) -> None:
paths.secrets_file.write_text(
"\n".join(
[
"TAVILY_API_KEY=sk-secret000000000000",
"XCRAWL_API_KEY=sk-secret000000000001",
"BIRDREAD_API_KEY=sk-secret000000000002",
"X_API_KEY=sk-secret000000000003",
"X_API_SECRET=sk-secret000000000004",
"X_ACCESS_TOKEN=sk-secret000000000005",
"X_ACCESS_TOKEN_SECRET=sk-secret000000000006",
"TAVILY_API_KEY=fixture-tavily-secret",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Check the replacement fixture values for leakage

After these fixtures stop using the sk-secret prefix, the assertion at the end of this test still checks only self.assertNotIn("sk-secret", combined). Consequently, leaking any of the new credential values into a reconciled client configuration or the doctor report would no longer fail this integration test. Check every replacement value (or retain a shared sentinel) so the test continues guarding against secret disclosure.

Useful? React with 👍 / 👎.

"XCRAWL_API_KEY=fixture-xcrawl-secret",
"BIRDREAD_API_KEY=fixture-birdread-secret",
"X_API_KEY=fixture-x-api-secret",
"X_API_SECRET=fixture-x-api-signing-secret",
"X_ACCESS_TOKEN=fixture-x-access-token",
"X_ACCESS_TOKEN_SECRET=fixture-x-access-token-secret",
]
)
)
Expand Down
5 changes: 3 additions & 2 deletions tests/test_config_and_secrets.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ def test_rejects_non_agent_id_before_write(self) -> None:
def test_secret_report_exposes_names_only(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
secret_file = Path(tmp) / "secrets.env"
secret_file.write_text("TAVILY_API_KEY=sk-secret000000000000\nUNUSED_API_KEY=fixture-unused\n")
secret_value = "fixture-tavily-secret-value"
secret_file.write_text(f"TAVILY_API_KEY={secret_value}\nUNUSED_API_KEY=fixture-unused\n")
config = replace(
default_config(secret_file),
tools=(ToolSpec(id="agent-tavily", name="Tavily", command="tool", required_secrets=("TAVILY_API_KEY", "XCRAWL_API_KEY")),),
Expand All @@ -81,7 +82,7 @@ def test_secret_report_exposes_names_only(self) -> None:
self.assertIn("TAVILY_API_KEY", report.stored_names)
self.assertIn("UNUSED_API_KEY", report.stored_names)
self.assertNotIn("UNUSED_API_KEY", report.present_names)
self.assertNotIn("sk-secret", json.dumps(report.to_dict()))
self.assertNotIn(secret_value, json.dumps(report.to_dict()))
self.assertIn("XCRAWL_API_KEY", report.missing)

def test_non_secret_environment_round_trips_but_public_view_lists_names_only(self) -> None:
Expand Down