chore(release): publish 1.3.12

- add OpenCode CLI provider support for voice agent workflows

- improve updater fallback/error visibility and compatibility

- bump app, Tauri, and Cargo versions to 1.3.12
This commit is contained in:
sim committed 2026-06-24 12:42:25 +08:00
1 parent e1c1be2942
commit a4df800d66
8 files changed
+83 -90

No files matched your search

+2 -2
View File
@@ -1,12 +1,12 @@
{
"name": "openless-app",
"version": "1.3.11",
"version": "1.3.12",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "openless-app",
"version": "1.3.11",
"version": "1.3.12",
"dependencies": {
"@formkit/auto-animate": "^0.9.0",
"@tauri-apps/api": "^2.1.1",
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "openless-app",
"private": true,
"version": "1.3.11",
"version": "1.3.12",
"type": "module",
"scripts": {
"dev": "vite",
+1 -1
View File
@@ -3883,7 +3883,7 @@ dependencies = [
[[package]]
name = "openless"
version = "1.3.11"
version = "1.3.12"
dependencies = [
"anyhow",
"arboard",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "openless"
version = "1.3.11"
version = "1.3.12"
description = "OpenLess — local voice input that types where your cursor is"
authors = ["OpenLess"]
edition = "2021"
@@ -16,7 +16,7 @@
//! [`CodingAgentEvent::Completed`],`cost_usd = None`(OpenCode CLI 不在 JSON 里给单次成本)。
use std::process::Stdio;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use std::time::Duration;
@@ -415,7 +415,7 @@ fn finalize_polished_text(
polished: String,
translation_active: bool,
_raw_uses_llm: bool,
mode: PolishMode,
_mode: PolishMode,
polish_error: &Option<String>,
chinese_script_preference: crate::types::ChineseScriptPreference,
correction_rules: &[crate::types::CorrectionRule],
+1 -1
View File
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "OpenLess",
"version": "1.3.11",
"version": "1.3.12",
"identifier": "com.openless.app",
"build": {
"beforeDevCommand": "npm run dev",
+75 -82
View File
@@ -1,10 +1,9 @@
// 自动更新共用模块 — Settings 的"关于"section 和 AutoUpdateGate 共用同一套
// 状态机 + 对话框 UI。各自调用 useAutoUpdate(),dialog 渲染条件相同。
// 自动更新共用模块 — Settings 的"关于"section 和 footer 按钮共用同一套
// 状态机 + 对话框 UI。两边各自调用 useAutoUpdate(),dialog 渲染条件相同。
//
// 渠道感知:check 走 appCheckUpdateWithChannel()(Rust 按渠道拼 manifest URL)。
// 桌面:download/install 复用 plugin-updater 的 Update 类。
// Android:download/install 走 appDownloadAndInstallAndroidUpdate(minisign + 系统安装器)。
// Android 后台 Gate:发现更新后 autoInstallAndroid 跳过确认,直接下载并打开系统安装器。
import { useEffect, useRef, useState } from 'react';
import type { DownloadEvent } from '@tauri-apps/plugin-updater';
@@ -71,8 +70,6 @@ type AndroidUpdatePayload = {
export function useAutoUpdate(): UseAutoUpdate {
const updateRef = useRef<Update | null>(null);
const androidUpdateRef = useRef<AndroidUpdatePayload | null>(null);
/** True only while this hook instance initiated an Android download/install. */
const androidDownloadActiveRef = useRef(false);
const [status, setStatus] = useState<UpdateStatus>('idle');
const [version, setVersion] = useState('');
const [downloaded, setDownloaded] = useState(0);
@@ -110,7 +107,6 @@ export function useAutoUpdate(): UseAutoUpdate {
contentLength: number | null;
phase: string;
}>('android-update:progress', (event) => {
if (!androidDownloadActiveRef.current) return;
setDownloaded(event.payload.downloaded);
setContentLength(event.payload.contentLength);
if (event.payload.phase === 'installing') {
@@ -141,30 +137,87 @@ export function useAutoUpdate(): UseAutoUpdate {
androidUpdateRef.current = { url, signature, version: metadata.version };
};
const installAndroidUpdate = async () => {
const payload = androidUpdateRef.current;
if (!payload) return;
const checkForUpdates = async (channel?: UpdateChannel, options?: CheckUpdateOptions) => {
setStatus('checking');
setVersion('');
setErrorMessage(null);
resetProgress();
setStatus('downloading');
androidDownloadActiveRef.current = true;
await closeUpdate();
try {
await appDownloadAndInstallAndroidUpdate(payload);
androidUpdateRef.current = null;
setStatus('downloaded');
if (!isTauri) {
setStatus('none');
return;
}
const metadata = await appCheckUpdateWithChannel(
UPDATE_CHECK_TIMEOUT_MS,
channel ?? null,
);
if (!metadata) {
setStatus('none');
return;
}
if (isAndroid()) {
storeAndroidMetadata(metadata);
setVersion(metadata.version);
if (options?.autoInstallAndroid) {
try {
// 复用 storeAndroidMetadata 的 rawJson 解析(提取 url/signature/version)
const raw = metadata.rawJson ?? {};
const url = typeof raw.url === 'string' ? raw.url : '';
const signature = typeof raw.signature === 'string' ? raw.signature : '';
if (!url || !signature) {
console.warn('[auto-update] android manifest missing url/signature, falling back to manual update');
setStatus('available');
return;
}
await appDownloadAndInstallAndroidUpdate({ url, signature, version: metadata.version });
setStatus('downloaded');
} catch (error) {
console.warn('[auto-update] android auto-install failed', error);
setStatus('available');
}
return;
}
setStatus('available');
return;
}
const next = new Update({
rid: metadata.rid,
currentVersion: metadata.currentVersion,
version: metadata.version,
date: metadata.date ?? undefined,
body: metadata.body ?? undefined,
rawJson: metadata.rawJson,
});
updateRef.current = next;
setVersion(next.version);
setStatus('available');
} catch (error) {
console.error('[updater] failed to install android update', error);
console.error('[updater] failed to check update', error);
const msg = error instanceof Error ? error.message : String(error);
void logClientError(`[updater] android install failed (v${payload.version}): ${msg}`);
void logClientError(`[updater] check failed: ${msg}`);
setErrorMessage(msg);
setStatus('installError');
} finally {
androidDownloadActiveRef.current = false;
setStatus('error');
}
};
const installUpdate = async () => {
if (isAndroid()) {
await installAndroidUpdate();
const payload = androidUpdateRef.current;
if (!payload) return;
resetProgress();
setStatus('downloading');
try {
await appDownloadAndInstallAndroidUpdate(payload);
androidUpdateRef.current = null;
setStatus('downloaded');
} catch (error) {
console.error('[updater] failed to install android update', error);
const msg = error instanceof Error ? error.message : String(error);
void logClientError(`[updater] android install failed (v${payload.version}): ${msg}`);
setErrorMessage(msg);
setStatus('installError');
}
return;
}
@@ -197,59 +250,6 @@ export function useAutoUpdate(): UseAutoUpdate {
}
};
const checkForUpdates = async (channel?: UpdateChannel, options?: CheckUpdateOptions) => {
setStatus('checking');
setVersion('');
setErrorMessage(null);
resetProgress();
await closeUpdate();
try {
if (!isTauri) {
setStatus('none');
return;
}
const metadata = await appCheckUpdateWithChannel(
UPDATE_CHECK_TIMEOUT_MS,
channel ?? null,
);
if (!metadata) {
setStatus('none');
return;
}
if (isAndroid()) {
storeAndroidMetadata(metadata);
setVersion(metadata.version);
if (options?.autoInstallAndroid) {
try {
await installAndroidUpdate();
} catch (error) {
console.warn('[auto-update] android auto-install failed', error);
}
return;
}
setStatus('available');
return;
}
const next = new Update({
rid: metadata.rid,
currentVersion: metadata.currentVersion,
version: metadata.version,
date: metadata.date ?? undefined,
body: metadata.body ?? undefined,
rawJson: metadata.rawJson,
});
updateRef.current = next;
setVersion(next.version);
setStatus('available');
} catch (error) {
console.error('[updater] failed to check update', error);
const msg = error instanceof Error ? error.message : String(error);
void logClientError(`[updater] check failed: ${msg}`);
setErrorMessage(msg);
setStatus('error');
}
};
const dismissDialog = async () => {
if (busy) return;
await closeUpdate();
@@ -301,17 +301,10 @@ export function UpdateDialog({
const installing = status === 'installing';
const installError = status === 'installError';
const androidInstalled = isAndroid() && status === 'downloaded';
const installLabel = isAndroid()
? t('settings.about.updateDialog.androidInstall')
: t('settings.about.updateDialog.install');
return (
<div style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.18)', display: 'grid', placeItems: 'center', zIndex: 40 }}>
<div style={{ width: 360, borderRadius: 16, background: 'var(--ol-surface)', border: '0.5px solid var(--ol-line-strong)', boxShadow: '0 18px 42px rgba(0,0,0,0.18)', padding: 18 }}>
<div style={{ fontSize: 15, fontWeight: 650, marginBottom: 8 }}>
{androidInstalled
? t('settings.about.updateDialog.androidInstalled.title')
: t(`settings.about.updateDialog.${status}.title`)}
</div>
<div style={{ fontSize: 15, fontWeight: 650, marginBottom: 8 }}>{t(`settings.about.updateDialog.${status}.title`)}</div>
<div style={{ fontSize: 12, color: 'var(--ol-ink-3)', lineHeight: 1.6, marginBottom: 14, whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
{androidInstalled
? t('settings.about.updateDialog.androidInstalled.desc', { version, defaultValue: '系统安装器已打开,请按提示完成安装。安装后重新打开 OpenLess 即可使用 {{version}}。' })
@@ -335,7 +328,7 @@ export function UpdateDialog({
)}
<div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8 }}>
{status === 'available' && <Btn size="sm" onClick={onClose}>{t('common.cancel')}</Btn>}
{status === 'available' && <Btn variant="blue" size="sm" onClick={onInstall}>{installLabel}</Btn>}
{status === 'available' && <Btn variant="blue" size="sm" onClick={onInstall}>{t('settings.about.updateDialog.install')}</Btn>}
{(downloading || installing) && <Btn size="sm" disabled>{installing ? t('settings.about.updateDialog.installingLabel') : t('settings.about.updateDialog.downloadingLabel')}</Btn>}
{status === 'downloaded' && <Btn size="sm" onClick={onClose}>{t('settings.about.updateDialog.later')}</Btn>}
{status === 'downloaded' && !androidInstalled && <Btn variant="blue" size="sm" onClick={restartApp}>{t('settings.about.updateDialog.restartNow')}</Btn>}