- 新增 update 模块:从 dl.shellrs.com 取已签名的更新清单(minisign,签名绑定通道和版本), 按大小和 SHA-256 校验安装包;下载可续传、可换镜像,走系统代理 - 按安装方式安装:macOS 整包交换并核对开发者签名,Windows 静默运行安装程序,Linux 覆盖 AppImage; 开发构建和无法自更新的安装只提示前往下载页 - 设置新增「关于」:版本信息、检查更新、下载进度和「自动检查并下载更新」开关; 新版本就绪后标题栏右上角提示,更新对话框写明重启会中断什么;退出时自动安装,新版本启动后提示已更新 - 数据库拒绝打开更新版本建的库,迁移已有的库之前先备份成 shellrs.db.v<旧版本>.bak - 新增 release workflow 和 packaging 脚本:universal macOS 包签名公证、Inno Setup 安装程序、 AppImage、上传 R2、生成并签名更新清单;新增 CHANGELOG.md - 应用图标放在 assets/logo;开发模式下 macOS Dock 显示 logo,Windows exe 嵌入图标 - 修正 AppImage 里 shellrs 命令链接到临时挂载点;启动时刷新已安装但过期的 Agent skill - 新增清单、验签、下载、各平台安装器和界面的单元测试与 UI 测试
105 lines
3.1 KiB
Rust
105 lines
3.1 KiB
Rust
use gpui_kit::*;
|
|
|
|
use super::install::{
|
|
AgentKind, BinaryStatus, IntegrationPaths, SkillStatus, binary_status, skill_status,
|
|
update_outdated_binary, update_outdated_skills,
|
|
};
|
|
|
|
/// What the settings page shows about the `shellrs` command and the agent
|
|
/// skills: where they go and whether they are there.
|
|
///
|
|
/// Without paths nothing can be installed. That is the state UI tests start
|
|
/// in, so no test writes to the real home directory by accident; they inject
|
|
/// a temporary one.
|
|
pub struct CliIntegration {
|
|
paths: Option<IntegrationPaths>,
|
|
/// `None` until the first check comes back.
|
|
status: Option<IntegrationStatus>,
|
|
_check: Option<Task<()>>,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct IntegrationStatus {
|
|
pub binary: BinaryStatus,
|
|
skills: Vec<(AgentKind, SkillStatus)>,
|
|
}
|
|
|
|
impl IntegrationStatus {
|
|
pub fn skill(&self, agent: AgentKind) -> SkillStatus {
|
|
self.skills
|
|
.iter()
|
|
.find(|(kind, _)| *kind == agent)
|
|
.map_or(SkillStatus::Missing, |(_, status)| *status)
|
|
}
|
|
|
|
fn read(paths: &IntegrationPaths) -> Self {
|
|
Self {
|
|
binary: binary_status(paths),
|
|
skills: AgentKind::ALL
|
|
.iter()
|
|
.map(|agent| (*agent, skill_status(paths, *agent)))
|
|
.collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl CliIntegration {
|
|
pub fn new(paths: Option<IntegrationPaths>) -> Self {
|
|
Self {
|
|
paths,
|
|
status: None,
|
|
_check: None,
|
|
}
|
|
}
|
|
|
|
pub fn paths(&self) -> Option<&IntegrationPaths> {
|
|
self.paths.as_ref()
|
|
}
|
|
|
|
pub fn status(&self) -> Option<&IntegrationStatus> {
|
|
self.status.as_ref()
|
|
}
|
|
|
|
pub fn set_paths(&mut self, paths: Option<IntegrationPaths>, cx: &mut Context<Self>) {
|
|
self.paths = paths;
|
|
self.status = None;
|
|
self.refresh(cx);
|
|
}
|
|
|
|
/// Look at the file system again, off the main thread.
|
|
pub fn refresh(&mut self, cx: &mut Context<Self>) {
|
|
self.check(false, cx);
|
|
}
|
|
|
|
/// Bring a copy of the command left by an older build up to this one,
|
|
/// and the installed agent skills with it, then look again. A copy that
|
|
/// old answers every request with `version_mismatch`. Quietly: if it
|
|
/// fails, the page offers 更新.
|
|
pub fn update_outdated(&mut self, cx: &mut Context<Self>) {
|
|
self.check(true, cx);
|
|
}
|
|
|
|
fn check(&mut self, update: bool, cx: &mut Context<Self>) {
|
|
let Some(paths) = self.paths.clone() else {
|
|
self._check = None;
|
|
cx.notify();
|
|
return;
|
|
};
|
|
self._check = Some(cx.spawn(async move |this, cx| {
|
|
let status = cx
|
|
.background_spawn(async move {
|
|
if update {
|
|
let _ = update_outdated_binary(&paths);
|
|
let _ = update_outdated_skills(&paths);
|
|
}
|
|
IntegrationStatus::read(&paths)
|
|
})
|
|
.await;
|
|
let _ = this.update(cx, |this, cx| {
|
|
this.status = Some(status);
|
|
cx.notify();
|
|
});
|
|
}));
|
|
}
|
|
}
|