Skip to content
Open
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ xattr -rd com.apple.quarantine "/Applications/workbuddy-switch.app"
| JetBrains IDE 插件 | 支持切换 IntelliJ IDEA / PyCharm 内的 CodeBuddy 插件账号,一次切换写入所有装了插件的 IDE;IDE 运行时可自动关闭并在写入后重新打开 |
| 插件会话复制 | 切换插件账号时,可把当前插件账号的会话复制给目标账号(加法,源账号不变) |
| 自动轮换 | 后台把积分最紧迫的账号设为 CodeBuddy CLI 后续启动账号;检测到 CLI 会话运行时会跳过 |
| WebDAV 备份 / 跨设备同步 | 把完整账号库(含 token 等授权信息)与各功能配置端到端加密备份到私有 WebDAV 服务器;新设备配置同一服务器与加密密码即可恢复;「同步」拉取合并 + 整库回推(同 uid 覆盖 / 新增追加,服务端 ETag 防并发覆盖) |
| 自动更新 | 从 GitHub Releases 检查新版本,整包更新经签名校验 |
| 会话悬浮窗 | 桌面版内置 Agent Companion 悬浮栏,在桌面集中显示 Codex / WorkBuddy / CodeBuddy / Codeg 会话的运行中 / 待确认 / 已完成状态;悬停查看详情,支持跳转时点击回到原会话,托盘可临时隐藏 |
| 权限检测 | macOS 授权引导(App 管理 / 完全磁盘访问拖拽授权 + 自动检测) |
Expand Down Expand Up @@ -96,6 +97,7 @@ CodeBuddy CLI 切换时会先关闭正在运行的 CLI,当前会话会中断
6. **开关各端入口**:设置 →「支持工具」可按客户端逐个开启 / 关闭入口;关闭后该端在账号页隐藏、不再轮询状态,不影响账号库。JetBrains 端默认关闭
7. **自动轮换**:设置 → CodeBuddy CLI 自动轮换,开启后按积分紧迫程度自动设置默认账号
8. **更新**:应用会自动检查公开 GitHub Releases;发现新版本后可在左下角直接升级,也可从设置页打开 Release 页面手动下载
9. **WebDAV 备份 / 跨设备同步**:设置 →「WebDAV 备份 / 跨设备同步」,配置私有 WebDAV 服务器(地址 / 用户名 / 口令 / 远端目录)与备份加密密码;「立即备份」把整库账号与功能配置加密上传,「从云端恢复」可勾选账号并可选同时写回配置,「同步」拉取合并后回推整库(同 uid 覆盖、新增追加,服务端 ETag 防止并发覆盖)

## 界面预览

Expand Down
78 changes: 78 additions & 0 deletions crates/wb-switch-core/src/modules/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,84 @@
}
}

/// 通用 HTTP 请求,返回原始响应(状态码 + 响应头 + 响应体字节),请求体为任意字节。
///
/// 供 WebDAV 这类非 JSON 协议使用:可指定任意方法与 Content-Type,二进制上传 /
/// 下载,不强制 JSON。`timeout_secs` 为每次请求的整体超时上限(备份文件上传可能
/// 远超共享客户端的 30 秒);`None` 使用共享客户端(30 秒)。
/// 失败(网络错误)返回 `(0, HashMap::new(), 错误信息)`。
pub async fn http_request_bytes(
url: &str,
method: &str,
content_type: Option<&str>,
body: Option<Vec<u8>>,
headers: Option<&HashMap<String, String>>,
follow_redirects: bool,
timeout_secs: Option<u64>,
) -> (u16, HashMap<String, String>, Vec<u8>) {

Check warning on line 1151 in crates/wb-switch-core/src/modules/config.rs

View workflow job for this annotation

GitHub Actions / lint (tsc + fmt + clippy + server tests)

Diff in /Users/runner/work/workbuddy-switch/workbuddy-switch/crates/wb-switch-core/src/modules/config.rs
let method = match reqwest::Method::from_bytes(method.as_bytes()) {
Ok(method) => method,
Err(e) => return (0, HashMap::new(), format!("无效的 HTTP 方法:{e}").into_bytes()),
};
let client = match timeout_secs {
Some(secs) => {
let mut builder = http_client_builder().timeout(std::time::Duration::from_secs(secs));
if !follow_redirects {
builder = builder.redirect(reqwest::redirect::Policy::none());
}

Check warning on line 1161 in crates/wb-switch-core/src/modules/config.rs

View workflow job for this annotation

GitHub Actions / lint (tsc + fmt + clippy + server tests)

Diff in /Users/runner/work/workbuddy-switch/workbuddy-switch/crates/wb-switch-core/src/modules/config.rs
match builder.build() {
Ok(client) => client,
Err(e) => return (0, HashMap::new(), format!("客户端创建失败: {e}").into_bytes()),
}
}
None => {
if follow_redirects {
http_client().clone()
} else {
match http_client_builder()
.redirect(reqwest::redirect::Policy::none())
.build()
{
Ok(client) => client,
Err(e) => {
return (
0,
HashMap::new(),
format!("客户端创建失败: {e}").into_bytes(),
)
}
}
}
}
};
let mut req = client.request(method, url);
if let Some(ct) = content_type {
req = req.header("Content-Type", ct);
}
if let Some(h) = headers {
for (k, v) in h {
req = req.header(k, v);
}
}
if let Some(b) = body {
req = req.body(b);
}
match req.send().await {
Ok(resp) => {
let status = resp.status().as_u16();
let mut resp_headers = HashMap::new();
for (k, v) in resp.headers() {
if let Ok(vs) = v.to_str() {
resp_headers.insert(k.as_str().to_string(), vs.to_string());
}
}
let bytes = resp.bytes().await.map(|b| b.to_vec()).unwrap_or_default();
(status, resp_headers, bytes)
}
Err(e) => (0, HashMap::new(), e.to_string().into_bytes()),
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
1 change: 1 addition & 0 deletions crates/wb-switch-core/src/modules/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,4 @@ pub mod vscode_ext;
pub mod vscode_session;
pub mod vscode_session_link;
pub mod vscode_session_sync;
pub mod webdav_sync;
Loading
Loading