Repository navigation
Conversation
Android 移植(完整功能,账号切换按平台隐藏): - 多目标构建 net9.0-windows(单文件 exe)+ net9.0-android(APK) - Platform/ 跨平台抽象:Keystore(RSA+AES-GCM) 替代 DPAPI、Intent 打开浏览器、 Android 数据目录(App 私有 files/TraeTools)、托盘/开机自启/进程控制平台分流 - 登录改造:Android 隐藏内嵌 WebView2 入口,保留浏览器+手动粘贴 Token 方案 - 账号切换仅 Windows 桌面端可用(Android 无本地客户端可操作),导航项按平台隐藏 - SQLite 增加 android 原生库(SQLitePCLRaw.lib.e_sqlite3.android) TRAE 双端合并适配(2026-10-09 官方合并 TraeWork/TraeCode 为 TRAE): - 设备号解析目录覆盖 TRAE/TRAE CN/TRAE SOLO CN/Trae CN - 客户端进程名多名字匹配(TRAE,TRAE SOLO CN,Trae),exe/目录探测覆盖新旧 非强制更新: - Android 检查更新仅提示并跳转 Release 页,不自动下载/安装;Windows 维持询问式 仓库指向:更新检查、云端签到 fork 源、PAT 提示等改为 akexin/TraeTools-Android CI/Release:新增 android job(workload install → publish apk → 挂 Release)
…ick 处理器 1. NETSDK1100:多目标项目在 ubuntu 上执行 dotnet build/publish -f net10.0-android 时, restore 阶段仍会还原 net10.0-windows,需显式允许下载 Windows 目标包: - ci.yml / release.yml 的 ubuntu job 命令加 -p:EnableWindowsTargeting=true - csproj 同步加 <EnableWindowsTargeting>true</EnableWindowsTargeting>(非 Windows 机器通用兜底) 2. LoginWindow.axaml 的自动登录按钮绑定了 Click="OnAutoLoginClick",而该方法此前被 #if !ANDROID 整体排除,Avalonia XAML 编译器在编译期解析事件处理器会直接报错。 改为双平台共存:Android 端仅回写提示文案(按钮已隐藏),Windows 端逻辑不变。
…droid 编译时 Windows 分支连方法结束括号一起被排除
1. CS8954 ClientController.cs:文件顶部 file-scoped namespace 两平台共用, #if ANDROID 分支又重复声明了一遍 → 删除重复声明 2. CS0104 App.axaml.cs:.NET Android SDK 隐式全局 using 含 Android.App, 裸 Application 与 Avalonia.Application 歧义 → 基类写全称 Avalonia.Application 3. CS0311 MainActivity.cs:上一项的连带错误(App 未构成 Avalonia.Application), 随 #2 一并解决;同时补 using Avalonia;(WithInterFont 扩展方法在该命名空间) 4. UiHost.cs 同 #2:Application.Current → Avalonia.Application.Current 已全景排查 Android.App/Android.Widget 全局 using 的全部常见冲突类型名, Android 编译单元内无其他裸用冲突类型。
1. Secrets.Android.cs:Cipher.EncryptMode/DecryptMode 常量在 .NET Android 中已 标记为 obsolete(报错级 CS0619),改用 Javax.Crypto.CipherMode 枚举成员 (EncryptMode/DecryptMode,Init(CipherMode, IKey) 重载已确认存在) 2. Program.cs:BuildAvaloniaApp() 整段移入 #if !ANDROID——UsePlatformDetect 扩展 由 Avalonia.Desktop 提供,Android 目标不引用该包;Android 端由 AvaloniaAndroidApplication 基类自行构建 AppBuilder
…y.Cryptography.CipherMode 二义性 CS0104)
- NU1102:多目标项目下命令行 -r win-x64 是全局属性,会渗入 net10.0-android 的 还原阶段,去找不存在的 Microsoft.NETCore.App.Runtime.Mono.win-x64 而失败 - 改为在 csproj 的 net10.0-windows 条件组内声明 <RuntimeIdentifier>win-x64</RuntimeIdentifier>, release.yml 的 publish 命令去掉 -r win-x64 - README 编译指引同步(多目标须显式 -f;非 Windows 构建 Android 需 EnableWindowsTargeting)
问题:release.yml 用 find | head -1 取 APK,取到的是未签名的中间产物 xxx.apk(安装会报 INSTALL_PARSE_FAILED_NO_CERTIFICATES);且 SDK 默认用 每次运行临时生成的 ~/.android/debug.keystore 签名,跨版本签名不一致, 无法覆盖安装。 - 新增 signing/traetools.keystore(PKCS12,alias/口令 traetools,3DES-PBE 最大化 JDK 兼容),随仓库公开,仅用于保证签名一致性 - publish 增加 AndroidKeyStore=true + 四个签名参数 → 产出 *-Signed.apk - Collect 步骤改用 *-Signed.apk 并在找不到时报错;打印全部候选与 apksigner verify 证书指纹
闪退根因:Avalonia 单视图生命周期不支持再创建 Window(官方文档明确),启动路径 MainWindow.OnOpenedEulaCheck 调用 EulaWindow.ShowDialog,异常经 async void 未捕获直接带崩进程。 - 新增 Views/OverlayDialog:Android 把对话框内容借用 MainWindow 顶部覆盖层呈现 (TCS 返回结果),桌面端仍走原生 ShowDialog;6 个对话框关闭入口统一走 OverlayDialog.CloseDialog;14 处 ShowDialog 调用全部改走 OverlayDialog.ShowAsync - 新增 Platform/CrashLog:全局未捕获异常 + 启动轨迹日志;Android 写外部应用目录 Android/data/cn.akexin.traetools/files/crash/(文件管理器免 root 可读)+ logcat - EULA 对话框失败兜底:记录日志并视为同意,保证应用可用 - 定时签到 Tick 加异常防护(async void 不再可能带崩 UI) - 版本 1.4.1(versionCode 11,可覆盖安装 1.4.0)
手机(Android 16 / 澎湃 OS)打开即闪退,日志无法回收,故本版以"让崩溃可见 + 排除 AOT 风险"为目标: - 关闭 AOT 与链接器(RunAOTCompilation/AOTAssemblies=false、AndroidLinkMode=None): Release 默认 LLVM AOT 下反射路径(ViewLocator、SQLitePCLRaw provider 初始化等) 可能抛 MissingRuntimeArtifactException,表现为启动即崩;本应用为网络 IO 型,稳定性优先。 - 新增原生 CrashReportActivity:进程最早点(Application 构造)注册全局异常捕获, AndroidEnvironment 事件里反射置 Handled=true 阻止进程被杀,改为弹出可读的原生错误页 (堆栈可复制、可一键重启),不再"一闪而过"。 - 新增 StartupErrorView(纯代码构建,不依赖 XAML):Avalonia 可显示时用它呈现异常 + 启动日志。 - 崩溃标记 crash_flag:上次崩溃则本次直接进错误页,避免反复闪退,可清除后重试正常界面。 - CrashLog 增强:崩溃标记读写、日志目录暴露、ReadRecent 供界面展示。 - 启动链路全面兜底:App.Initialize / OnFrameworkInitializationCompleted / MainWindow.OnOpened 全程 try-catch;ViewLocator 反射失败降级为占位文本;OverlayDialog 异常返回默认值。
真实堆栈(v1.4.2 崩溃日志)确认根因:
System.NotSupportedException: Specified method is not supported.
at Avalonia.Android.WindowingPlatformStub.CreateWindow()
at Avalonia.Controls.Platform.PlatformManager.CreateWindow()
at Avalonia.Controls.Window..ctor()
at TraeTools.Views.MainWindow..ctor()
Android 是 Avalonia 单视图平台,构造任何 Window 都会抛异常。
此前把 MainWindow 当 MainView、并用 Window 做对话框,必然启动即崩。
改造:
- 新增 Views/MainView.axaml(.cs)(UserControl):承载原 MainWindow 的全部界面与
双平台共享逻辑(覆盖层宿主、自动签到定时器、首次启动 EULA)。
- MainWindow(Window)仅保留桌面专属行为(尺寸/图标/托盘/关闭最小化),内部嵌 MainView;
Android 端 App 直接用 MainView 作为 ISingleViewApplicationLifetime.MainView。
- 6 个对话框(Eula/Prompt/Input/PatInput/DeviceAuth/Login)由 Window 改为 UserControl,
新增 DialogTitle 属性供桌面端窗口标题使用。
- 新增 Views/DialogHostWindow:桌面端把对话框 UserControl 包进真实 Window 后 ShowDialog;
OverlayDialog 据此重写(Android 覆盖层 / 桌面 DialogHostWindow 双路径)。
- 修正 Android 上 owner 恒为 null 导致对话框被跳过:移除 ViewModels 中
`owner != null &&` 短路与 `owner == null return` 守卫,桌面端 owner 缺省时兜底主窗口。
- EULA 触发改到 Loaded(构造期 desktop.MainWindow 尚未赋值,取 owner 会失败)。
- 版本 1.4.3(versionCode 13)。
手机(360dp 级别屏宽)上桌面布局严重溢出:190px 侧栏占去一半屏宽, 仪表盘双列卡片与页头按钮被裁掉。本版按宿主实际宽度自动切换布局: - MainView 新增紧凑模式(IsCompact,宽度<640 逻辑像素触发,由 OnPropertyChanged(Bounds) 驱动,桌面窄窗口同样生效): * 侧栏隐藏,改为顶部品牌栏 + 底部标签栏(仪表盘/用量/签到/云端/设置) * 内容区边距 28,32 → 14,12 * 仪表盘:页头按钮堆叠到标题下方;趋势图与账号概览改上下排列 * 签到/设置/用量:双列卡片上下堆叠;用量 4 张汇总卡 2x2 排布 * 所有切换用 ^.compact 后代样式实现,宽屏/桌面布局零影响 - 修复 OverlayRoot 覆盖层未跨列的 bug:此前对话框宿主只占 Grid 第一列单元格,EULA 等弹窗整体偏在侧栏上;现显式 RowSpan=3/ColumnSpan=2 - 版本 1.4.4(versionCode 14)
…被样式切换;AVLN3000 修复
* feat: TraeTools 移动端 UI 优化方案 Co-authored-by: traeagent <traeagent@users.noreply.github.com> * fix(xaml): 修复 CI 编译错误——Button 无 BoxShadow 属性;Border 单子元素限制用 Panel 包裹 * chore: bump version to 1.4.5 --------- Co-authored-by: akexin <akexin@users.noreply.github.com> Co-authored-by: traeagent <traeagent@users.noreply.github.com>
* feat(ui): 扁平弱卡片重构——去渐变/投影、圆角降级 12/8px、设置页垂直流修复遮挡 - 主题层:卡片 12px 圆角去投影;新增 HeroTint 淡蓝色块令牌;按钮/输入 8px 圆角;code-box 改浅灰文本区;移除渐变 CTA(统一 btn-primary) - 仪表盘:Hero 换淡蓝扁平色块 + 深色文字 - 签到页:渐变块换淡蓝底 + 细线进度环;日历状态反转(已签淡底/今日实心);图例居中;空记录改轻量文案 - 用量页:重构为「主指标卡(Token 大数字)+ 三个带图标副指标」;会话/模型空态改轻量引导文案 - 设置页:左右双列改垂直列表流;深色模式并入列表项;签到时间/间隔改垂直表单;设备号与操作按钮分行修复重叠;9074 说明改折叠 Expander - 云端页:部署流程改纵向步骤列表;部署状态改键值对行;警告条细化;CTA 统一扁平主色 - 导航:选中 Tab 图标轻微放大动效;覆盖层圆角 12 * chore: bump version to 1.4.6 --------- Co-authored-by: akexin <akexin@users.noreply.github.com>
* feat(ui): 异步操作动效——签到/刷新/拉取/部署加载反馈与防重复提交 - 新增 loading-bar 细加载条样式(页头标题下方,3px 主色进度条) - 签到页:新增 IsCheckingIn;按钮文案「签到中…」、CanCheckin 禁用态、加载条;DoCheckin 加防重入 - 仪表盘:新增 IsRefreshing;「刷新状态」按钮文案与禁用联动、加载条;RefreshStatus 加防重入 - 用量页:新增 PullButtonText(「拉取中…」)+ 加载条,复用已有 IsLoading - 云端页:复用已有 IsBusy,加载条 + 按钮禁用(与命令自身防重入一致) - Tab 栏:新增按压缩放 0.92 触感反馈 * chore: bump version to 1.4.8 --------- Co-authored-by: akexin <akexin@users.noreply.github.com>
产品减法(Less is More):六页导航收敛为「单页仪表盘 + 二级设置」。 新增 - ViewModels/HomeViewModel.cs:聚合层,复用既有的 CheckinViewModel / CloudViewModel 引擎 (刻意共享同一实例,保证定时自动签到与页面按钮作用于同一状态) - Views/HomeView.axaml:单页三模块 —— 本地签到卡片 / 云端自动签到卡片 / 最近 3 条记录 - .github/workflows/bump-version.yml:version.json 与 Android versionCode 自增,推送后显式派发 CI 界面重构 - MainView 移除侧栏与底部 4-Tab 导航,改为顶栏:小号加粗标题 + 云端状态徽章(可点击展开详情)+ 设置入口 - 设计规范落地:卡片 16px 圆角 + 0 4 12 rgba(0,0,0,.03) 微投影(去边框)、主色胶囊 CTA、 状态色徽章与圆点(绿=运行中 / 黄=待部署 / 红=未部署)、运行中呼吸灯动效 - 修正紧凑模式边距:原实现把 Padding 写在元素上,局部值压过样式导致紧凑覆盖失效,改为全部由样式给出 移除(能力下线,非仅隐藏入口) - 用量统计:UsageViewModel / UsageView / TraeUsageClient / UsageStore - 账号切换:SwitchViewModel / SwitchView / SwitcherService / VaultService / AuthProbe / ClientController / CarrierProfiler(Windows-only 能力) - 仪表盘聚合页:DashboardViewModel / DashboardView(职责被单页吸收) - 其余页面视图(CheckinView / CloudView)随单页化删除,引擎保留并被 HomeView 复用 保留 - 本地/自动签到、云端授权与部署、飞书推送、账号管理(登录唯一入口,仍在设置页)、 更新检查、EULA、崩溃兜底与 Overlay 对话框宿主
## 1. 移动端版本号错乱(检查更新说 2.0.0,App 内却显示 1.0.0) 根因有两处,互相独立: - Services/AppVersion.cs 用 Assembly.GetEntryAssembly() 作为版本读取入口。 该 API 在 **.NET for Android 上返回 null**,导致「内嵌 version.json 资源」与 「InformationalVersion 回退」两条路径同时落空,最终命中硬编码兜底常量 "1.0.0"。 UpdaterService.CurrentVersion 同源,于是本地版本被算成 1.0.0、远端 2.0.0, 升级判定一直错位。 修复:改为按 CandidateAssemblies() 依次尝试(GetEntryAssembly 可能为 null 或抛异常), 并以 typeof(AppVersion).Assembly 兜底;同时截掉 InformationalVersion 的 "+<commit>" 后缀。 - TraeTools.csproj 的版本同步写在 Target(BeforeTargets=GenerateAssemblyInfo)里。 Target 内的赋值只对求值之后运行的 Target 可见,而 .NET for Android 生成 AndroidManifest 的阶段更早,实测发布出的 v2.0.0 APK 清单里 android:versionName 仍是 csproj 静态值 1.4.4(versionCode=15 是对的)。 修复:版本改在「求值期」的 PropertyGroup 中从 version.json 读取,并让 ApplicationDisplayVersion 直接引用该变量,csproj 不再有硬编码版本。 ## 2. 签名校验:从「只打印」改为硬门禁 先厘清事实:v2.0.0 APK 实测签名是完整的(v1 JAR + v2 + v3 三方案齐全, 证书 CN=TraeTools/O=akexin,SHA-256 313fa330…),并非签名无效。 「签名验证不通过」的真实来源是**签名不一致**——signing/traetools.keystore 是 2026-10-13 的 0f0620a 才引入的,此前各版本用的是每次 CI 重新生成的临时 debug keystore,故 ≤v1.4.7 的用户无法覆盖安装。 - 新增 .github/workflows/rotate-keystore.yml:用构建机 keytool 生成密钥 (本机无 JRE,第三方工具生成的 PKCS#12 在 Java 侧 alias 映射不确定), 自动写入 signing/EXPECTED_CERT_SHA256.txt 并提交回分支。 - release.yml 的签名步骤由 `apksigner verify … | head -8 || true`(既不校验退出码 也不比对指纹)改为三项硬校验:退出码、证书 SHA-256 与基准文件一致、 结构性校验 v1+v2+v3 齐全。方案齐全性刻意直接解析 APK 二进制,不 grep apksigner 文本输出——实测该 CI 的 build-tools 输出格式是 "V3.0 Signer:" 而非 "Signer #1 certificate …",文本匹配不可靠。 ## 3. 「关于」页链接 「打开原仓库」与「打开我的 Fork 仓库」两行此前指向同一地址(本项目仓库), 是 fork 时代遗留的重复项且用词误导。合并为单行「项目仓库」, OpenForkRepoCommand / ForkRepoUrl 一并移除。 ## 4. 版本号 2.0.1 version.json 2.0.0 → 2.0.1,Android versionCode 15 → 16(必须单调递增)。
由 .github/workflows/rotate-keystore.yml 手动触发。轮换后所有已安装用户必须卸载旧版才能安装新版。同时更新 signing/EXPECTED_CERT_SHA256.txt(release.yml 的签名校验门禁基准)。
第一次 v2.0.1 发布被新增的签名门禁拦下,暴露两个真问题(都值得修): 1. v1(JAR) 签名丢失 `dotnet publish -p:AndroidKeyStore=true` 产出的包,v2.0.0 含 v1 签名, 而换密钥后的 v2.0.1 为 `Verified using v1 scheme (JAR signing): false`。 即 v1 是否产出取决于 .NET for Android 的内部启发式(与 apksigner 的 minSdk 默认值交互),不是确定行为。 改为:publish 用 AndroidKeyStore=false 只产未签名包,再由工作流显式 `zipalign -f -p -P 16 4` + `apksigner sign --v1/--v2/--v3 true` 签名。 行为从此确定,且不再依赖打包器的隐式决策。 (-P 16 保证 .so 16KB 页对齐,Android 15+ 有要求;不支持 -P 的旧 build-tools 回退 -p 4。) 2. 指纹比对误报 rotate-keystore 里 keytool 输出大写、release 里 apksigner 输出小写, 直接字符串比较判定"不一致"。改为两侧统一 `tr -cd 0-9A-Fa-f | tr a-f A-F` 归一化后再比。 此次为上一提交 6f4cf19 的发布链路修复,v2.0.1 的 tag 与不完整 Release 已删除,修复后会重新打 tag 触发完整发布。
第二次发布仍被门禁拦下,两处问题: 1. 方案结构校验的读取窗口太小(本次真正的失败点) 原脚本固定读「文件尾部 64KB」,但本包中央目录本身约 73KB, 而 APK Signing Block 位于中央目录**之前**,于是 rfind 落空, 误报 "APK Signing Block: 未找到"、v2/v3 判定为缺失。 (同一次日志里 v1 判定为「存在」,说明上一步的显式三方案签名其实是成功的。) 改为先读尾部 EOCD 取中央目录偏移,再自该偏移向前多读 64KB。 已用 v2.0.0 的实测数据在本地回归:旧逻辑取不到,新逻辑正确解析出 v2 / v3 / source stamp。 2. 自行 zipalign 会把 16KB 页对齐降级为 4KB 上一版改为「未签名包 + zipalign + 签名」,但实测该 build-tools 的 zipalign 不支持 -P 16(16KB 页对齐,Android 15+ 对含原生库的包有要求), 只能回退 -p 4,等于把 .NET 打包器原有的对齐策略降级。 改为:仍由 .NET 打包器签名(它自带对齐流程),然后对其产物**重签** 以强制 v1+v2+v3——重签不触碰 zip 结构,对齐策略保持不变。 两轮失败都是新增门禁抓出来的真实缺陷,不是误报。
本轮把「获取登录态」和「结果送达」两端都补全,并解决了长期存在的深色模式失效问题。
本地已用 .NET 10.0.402 SDK 对 net10.0-windows 目标做过 Debug/Release 双配置编译校验
(0 错误 0 警告)。
### 登录方式(原仅内嵌浏览器 / 手动粘贴)
- Services/Login/CredentialImporter.cs:清洗并识别粘贴内容,剥掉
`X-Cloudide-Session=`、`Authorization: Cloud-IDE-JWT`、Cookie/Set-Cookie 头名、
成对引号与折断换行等外壳;JWT 与会话按结构判定;会话自动换取 Token 并保留用于静默续期
- Services/Login/LocalClientImporter.cs:只读扫描本机 TRAE 客户端数据目录
(storage.json / Local Storage/leveldb)提取设备号、Token 与会话,多来源多策略,
失败即给出可执行的下一步建议
- Services/Login/AccountBackup.cs:账号导出/导入。口令派生密钥
(PBKDF2-SHA256 20 万次)+ AES-256-GCM 认证加密,不依赖 DPAPI/Keystore 的机器绑定,
因而可跨设备迁移;导入按账号去重合并,不删除本地已有账号
- Views/LoginWindow:重写为四方式入口 + 内容类型下拉(自动识别/Token/会话)
### 账号切换(2.0 曾整体下线)
- 顶栏账号胶囊(首字头像 + 当前账号名)展开账号列表,点选即切换;
只有一个账号时点击直接进账号管理页
- 设置页账号列表恢复「点选即切换为当前账号」
- 切换后签到引擎、首页状态与设置页账号状态整体重载
### 推送通知(原仅飞书)
- Services/Notify/NotifyDispatcher.cs:统一扇出,渠道彼此隔离,单个失败不阻断其它与签到主流程
- Services/Notify/EmailNotifier.cs:自实现 SMTP。内置 SmtpClient 不支持 465 隐式 TLS,
而国内邮箱普遍使用 465,故自行实现 ssl / starttls / none 三种模式,
并支持 AUTH LOGIN 与 AUTH PLAIN 回退、RFC 2047 中文主题
- Services/Notify/RobotNotifiers.cs:通用 Webhook({title}/{content} 模板,
兼容 Server酱/PushPlus/Bark)、钉钉(含加签 HMAC-SHA256)、企业微信、Telegram
- Services/Notify/SystemNotifier.cs:Windows 主窗口通知 / Android 通知栏
- 设置页新增「推送通知」卡片:总开关 + 六渠道独立开关与配置 + 一键测试全部
### 日志
- Services/AppLog.cs:内存环形缓冲(800 条)+ 按月分文件落盘,
格式 `[时间] [级别] [账号] 消息`,兼容旧格式与崩溃日志格式
- Views/LogView + ViewModels/LogViewModel:最近(本进程实时)/ 全部(历史归档文件)
双视角,可复制、删除单个文件、清空全部
- 「自动滚动」可关闭并记忆偏好:关闭后新日志仍入列表,但视图停在当前位置,
翻看早先记录时不会被不断涌入的新日志推着走
### 深色模式
- 主题改为三态(跟随系统 / 浅色 / 深色),App.ApplyThemeMode 统一切换
- 修复 AppConfig.CloneForStorage 的逐字段白名单拷贝导致 DarkMode 永不落盘
(用户此前「切了深色、重启又变回浅色」的根因),改为 JSON 深拷贝
- LoginWindow / EulaWindow / PromptWindow / InputWindow / PatInputWindow /
DeviceAuthWindow 六处对话框的硬编码浅色全部替换为主题令牌
### 修复的真实缺陷(均由本地编译/静态核对发现)
- AppConfig.CloneForStorage 误用 this.Accounts 而非 copy.Accounts,
会就地加密运行中账号的 Token/Session/DeviceId —— 保存一次设置后,
内存里的 Token 变成 DPAPI 密文,后续所有接口调用发出密文、签到全部失败
- SystemNotifier 桌面端调用 TrayIcon.ShowNotification,但 Avalonia 12 的 TrayIcon
已无此方法(0.10 后移除);改走 WindowNotificationManager,经 App.DesktopNotifier 桥接
- EmailNotifier.SendAsync 返回 Task<MailResult>,而 NotifyDispatcher.WrapAsync
要求 Task<NotifyResult>,类型不兼容;统一为 NotifyResult
- 剪贴板调用缺少 `using Avalonia.Input.Platform`(扩展方法所在命名空间);
且 Avalonia 12 已把读取入口由 GetTextAsync 改名为 TryGetTextAsync
- LogView/SettingsView 的 XML 注释中出现 `--`,属非法 XML,Avalonia 编译期直接报 AVLN1001
- FeishuWebhook 由明文改为加密后,老配置解密失败会得到 null 而静默清空推送地址;
改为解密失败时保留原值
- version.json: 2.0.1 -> 2.1.0(版本号单一真源) - TraeTools.csproj: Android versionCode 16 -> 17 (versionCode 必须严格单调递增,否则 APK 无法覆盖安装)
新增能力 - 账号简介:每个账号可写自由文本备注,设置页账号卡片与顶栏切换器均显示,停手 0.8s 自动落盘 - 积分数据面板:剩余积分 / 已消耗 / 累计获得 / 总额度 / 今日获得 / 连续签到 / 签到天数 / 最近签到 + 会员·学生·今日已签·Token 有效期状态串;本地统计离线可看,额度三块点「刷新数据」 从云端拉取(TraeApiClient 新增 GetEntitlementAsync,返回总配额与已消耗) - 自定义 16 位设备号:手填 / 随机生成 / 保存,实时校验位数、字符与「不得与其它账号重复」 - 账号保活:每个账号独立开关 + 间隔(5–1440 分钟,默认 60),到点用会话 Cookie 静默换新 Token 并刷新积分与资料,只探测不 claim,不影响今日签到判定;支持「立即保活」。 实现见 Services/KeepAlive/KeepAliveService.cs,挂在 MainView 的 30 秒定时器上与自动签到共用节拍 - 安全检查:Services/Security/SecurityAudit.cs,8 项本地只读审计(配置加密状态、加解密往返自检、 明文敏感字段残留、设备号合规与重复、Token/会话有效期、保活覆盖、数据目录私有性、日志凭证泄漏), 结论按风险→警告→通过排序,只输出字段名不显示凭证内容;审计在后台线程执行,不阻塞界面 修复 - Android 上所有加密字段都无法解密(重要):Secrets.Unprotect 把「跨平台密文互不通用」的判断 写在了共享层,而 Android 自己的密文就是 "and-v1:" 前缀,于是被自己的规则拦下——表现为 Token 每次启动即丢、设备号退化成密文被当作设备号发出,直接触发风控 9074「参与用户太多」。 现把该判断下移到 Windows 实现内部,并加自愈:运行时发现设备号不是 16 位数字自动换发新号 (AccountHelpers.IsValidDeviceId) - 「授权 GitHub」不再自动打开浏览器:BrowserLauncher.Open 改为返回 bool 且不再静默吞异常, Android 按 Activity → Application 上下文 → 系统选择器三级回退,失败写入日志(category=browser); CloudViewModel.OpenAuthPage 调整为先开浏览器再复制授权码;清单补 <queries> http/https - 点账号旁下三角被强行带去设置页:ToggleAccountSwitcher 改为始终展开账号列表, 列表底部固定提供「添加账号 / 管理账号」入口(ManageAccountsCommand) 其他 - TraeAccount 新增 Bio / KeepAliveEnabled / KeepAliveIntervalMinutes / LastKeepAliveAt / LastKeepAliveMessage / QuotaLimit / QuotaUsed / StatsUpdatedAt - CheckinDatabase 新增 GetAccountStats / GetAccountSignedDates / GetAccountStreakDays (归属判定同时接受 account_id 与 account_name,兼容旧文本迁移数据) - TokenUtils 新增 TryGetExpiry 解析 JWT exp - 静态检查工具纳入仓库 tools/(check_bindings / lint_xaml_comments / lint_cs) - version.json → 2.2.0,ApplicationVersion → 18 本地验证:net10.0-windows Debug 与 Release 均 0 错误 0 警告; 224 个绑定表达式全部可解析;0 处非法 XAML 注释;67 个 .cs 文件结构检查通过。
本合并提交的树等于 fork 的 2.2.0 源码本身(origin/main = 464f9b7),理由: 上游在分叉点之后做的架构工作与 2.2.0 的对应实现重复,且 2.2.0 侧已经删除了 它们所依赖的类型,无法在同一棵树里共存: · TraeTools.LoginHost 独立登录进程(aac7764) 2.2.0 用进程内 WebView2 宿主自动取凭证,不依赖外部宿主进程。 · Services/Json 源生成 JSON(3e34c46) 引用了 2.2.0 已删除的 TraeSwitch.Services.VaultMeta 与 GitHubSecretPayload。 · Android/ 独立工程 + .github/workflows/android.yml(503a4d2) 2.2.0 用单工程多目标(net10.0-windows;net10.0-android)实现 Android 端。 因此「以 2.2.0 为准」即意味着上述改动被 2.2.0 的实现取代。 记录 upstream/main 为第二个父提交,使本分支与上游 main 之间不再存在合并冲突。
|
@star620 补充一下这个 PR 里最需要你拍板的地方,免得看起来像无理由整体覆盖。 为什么是「整条线取代」而不是「只加功能」我本来想只提增量、保留你现在的架构,但编译上过不去,不是偏好问题: 你 [JsonSerializable(typeof(GitHubSecretPayload))] // CompactJsonContext
[JsonSerializable(typeof(VaultMeta))] // IndentedJsonContext而 2.2.0 里 我实测过直接 这个 PR 怎么做到零冲突的用策略级合并把上游 git checkout -B sync-2.2.0 <2.2.0>
git merge -s ours upstream/main
哪一项你更想留你那边的,说一声我就改
另外,如果你更想要一个完全不动你架构、只加功能的版本,我也可以重开一个:只搬 2.2.0 的自研功能(多账号切换、四种登录方式、六渠道推送含自定义 SMTP、日志页、深色模式、自定义设备号),其余全部保留你的实现。 你倾向哪个方向,我按那个来 —— 这个 PR 先挂着你慢慢看也行。 |
|
@akexin 谢谢这份大礼包,也谢谢你把取舍点讲得这么清楚 —— 我把 Android 这条线单独放到 想请你按你自己提的那个方案,重开一个「只加功能、不动我架构」的 PR。要搬的功能:
请务必保留上游这些实现(不要被取代):
两点额外说明:
另外,2.2.0 的账号/凭证模型既然是新的, |
这个 PR 把 2.2.0 那条线(fork 的
main)整体推到上游。它包含全部自研功能,并且自带一套 Android 实现(单工程多目标),因此与 #52 刚合并进来的Android/目录是两套并行方案 —— 请见下方「需要移除的东西」。本分支已经把上游
main合并了进来(upstream/main是本分支的第二个父提交),所以不存在任何合并冲突,可以直接合并。142 files changed, +10005 −7459Android/目录2.2.0 的 Android 端是单工程多目标实现的,不需要第二个工程:
Windows 专属能力(WinForms / WebView2 / DPAPI / 托盘)与 Android 平台实现(
Platform/目录)全部按TargetFramework条件编译,一份代码两个产物。Android/那套「独立工程 + 链接共享源码」与它重复,请一并删掉:Android/**(33 个文件)、.github/workflows/android.ymlTraeTools.csproj里的 3 行Remove="Android\**"排除项删掉之后 Android 端不会失去任何能力,CI 里
build-android作业会继续编译net10.0-android并产出 APK。2.2.0 带来什么
net10.0-windows(win-x64 单文件 exe)+net10.0-android(apk),versionName与version.json同源.NET 打包后重签;签名密钥轮换工作流与上游现有实现的取舍
「以 2.2.0 为准」意味着上游在分叉点之后的部分架构工作会被 2.2.0 的对应实现取代。逐项说明如下,便于评估:
aac7764拆出TraeTools.LoginHost独立进程(去 WinForms/WebView2,为 NativeAOT 让路)3e34c46JSON 走源生成(Services/Json)System.Text.Json;且AppJson引用了 2.2.0 已删除的TraeSwitch.Services.VaultMeta、GitHubSecretPayload,无法编译Services/Json/**移除cb573f2AOT 目录版 zip 整包分发 + 更新器整套文件替换UpdaterService走单文件 exe 覆盖 + zip 校验6b654a993cef5f6d2e8b2switch 系列修复Services/Switch/*旧实现、ViewModels/SwitchViewModel.cs、Views/SwitchView.axaml等)4da8bf7旧客户端目录不再优先探测503a4d2Android/独立工程其中 10 个
Android/**文件被 git 识别为重命名而非删除(例如Android/Platform/AndroidVault.cs→Platform/Secrets.Android.cs、Android/Platform/MainActivity.cs→Platform/MainActivity.cs、Android/Resources/**→Resources/**),所以 diff 里看起来是「挪了位置」。迁移影响
Views/{Checkin,Cloud,Dashboard,Switch,Usage}View.*、ViewModels/{Dashboard,Switch,Usage}ViewModel.cs、Services/Switch/{AuthProbe,CarrierProfiler,ClientController,SwitcherService,VaultService}.cs、Services/Usage/*)在 2.2.0 的单页架构里已合并进新页面。version.json会从1.3.3变为2.2.0。ApplicationId为cn.akexin.traetools、ApplicationVersion(versionCode)为 18。验证
main之间零冲突(upstream/main已是本分支的父提交,合并可快进)。main(464f9b7)逐字节一致(git diff为空),因此 2.2.0 既有的构建与发布流程不受影响。build-windows(windows-latest)与build-android(ubuntu-latest,dotnet workload install android)两个作业均通过,分别产出单文件 exe 与 APK。trae-tools-build-windows88.41 MB)如果你更希望保留上游的某一部分(比如
TraeTools.LoginHost的进程隔离,或 JSON 源生成),告诉我,我可以改成分支上保留、而不是取代。