mirror of
https://github.com/qingchencloud/clawpanel.git
synced 2026-08-09 15:43:44 +08:00
feat: v0.8.2 — 15 fixes + 4 features + 3 improvements
Fixes: - Stop force-appending /v1 to API URLs (breaks Volcengine /v3 etc) - SSH upgrade: --unset-all + --add for 4 git insteadOf rules - Feishu: builtin detection, overlay→modal fix, select field, plugin version persistence - Docker: HTML response detection, Web mode guidance - Chat: runId dedup prevents duplicate messages - Cron: RPC params name→id - Channels: Gateway reload async (instant UI response), toggle cache invalidation - Linux: auto sudo for non-root npm installs (libc geteuid) - Control UI: dynamic hostname + auth token for remote access - npm: mirror fallback (npmmirror→npmjs.org) - QQBot: native binding friendly error message - Error diagnosis: SSH vs Git-not-installed, native binding detection Features: - About page: company info (武汉晴辰天下网络科技有限公司) - model-presets.js: shared module for models.js + assistant.js - Feishu: dual plugin support (builtin vs official @larksuiteoapi) - Assistant: provider preset quick-fill buttons Improvements: - Website: dynamic download links from latest.json + claw.qt.cool proxy - Linux deploy docs: upgrade guide, Gitee mirror, sudo notes - linux-deploy.sh: Gitee fallback + sudo npm + mirror retry
This commit is contained in:
@@ -15,20 +15,36 @@ export function diagnoseInstallError(errStr) {
|
||||
// ===== 1. Git 相关 =====
|
||||
|
||||
// git SSH 权限问题(有 git 但没配 SSH Key)
|
||||
if (s.includes('permission denied (publickey)') || s.includes('ssh://git@github')) {
|
||||
if (s.includes('permission denied (publickey)') || s.includes('ssh://git@github') || s.includes('git@github.com')) {
|
||||
return {
|
||||
title: '安装失败 — Git SSH 权限',
|
||||
hint: '依赖包用了 SSH 协议拉取代码,但你没配 GitHub SSH Key。运行以下命令改用 HTTPS:',
|
||||
command: 'git config --global url."https://github.com/".insteadOf ssh://git@github.com/',
|
||||
command: 'git config --global url."https://github.com/".insteadOf ssh://git@github.com/ && git config --global --add url."https://github.com/".insteadOf git@github.com: && git config --global --add url."https://github.com/".insteadOf git://github.com/',
|
||||
}
|
||||
}
|
||||
|
||||
// git 未安装(exit 128 + access rights)
|
||||
if (s.includes('code 128') || s.includes('exit 128') || s.includes('access rights')) {
|
||||
// git exit 128:优先判断是 SSH 失败还是 Git 未安装
|
||||
if (s.includes('code 128') || s.includes('exit 128')) {
|
||||
if (s.includes('ssh') || s.includes('git@') || s.includes('publickey') || s.includes('access rights')) {
|
||||
return {
|
||||
title: '安装失败 — Git SSH 权限',
|
||||
hint: '依赖包用了 SSH 协议拉取代码,但你没配 GitHub SSH Key。运行以下命令改用 HTTPS:',
|
||||
command: 'git config --global url."https://github.com/".insteadOf ssh://git@github.com/ && git config --global --add url."https://github.com/".insteadOf git@github.com: && git config --global --add url."https://github.com/".insteadOf git://github.com/',
|
||||
}
|
||||
}
|
||||
return {
|
||||
title: '安装失败 — 需要安装 Git',
|
||||
hint: '部分依赖需要通过 Git 下载。请先安装 Git 后重试。',
|
||||
command: '下载 Git: https://git-scm.com/downloads',
|
||||
title: '安装失败 — Git 错误',
|
||||
hint: 'Git 操作返回错误(exit 128)。可能是 Git 未安装,或 SSH 认证失败。请确认 Git 已安装,或手动执行以下命令切换到 HTTPS 模式:',
|
||||
command: 'git config --global url."https://github.com/".insteadOf ssh://git@github.com/ && git config --global --add url."https://github.com/".insteadOf git@github.com:',
|
||||
}
|
||||
}
|
||||
|
||||
// native binding 缺失(macOS/Linux 上 OpenClaw 的原生依赖问题)
|
||||
if (s.includes('cannot find native binding') || s.includes('native binding')) {
|
||||
return {
|
||||
title: '安装失败 — 原生依赖缺失',
|
||||
hint: 'OpenClaw 的原生模块未正确安装。这通常是 npm optional dependencies 的 bug。请尝试在终端手动重装:',
|
||||
command: 'npm i -g @qingchencloud/openclaw-zh@latest --registry https://registry.npmmirror.com',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
83
src/lib/model-presets.js
Normal file
83
src/lib/model-presets.js
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
* 共享模型预设配置
|
||||
* models.js 和 assistant.js 共用,只需维护一套数据
|
||||
*/
|
||||
|
||||
// API 接口类型选项
|
||||
export const API_TYPES = [
|
||||
{ value: 'openai-completions', label: 'OpenAI 兼容 (最常用)' },
|
||||
{ value: 'anthropic-messages', label: 'Anthropic 原生' },
|
||||
{ value: 'openai-responses', label: 'OpenAI Responses' },
|
||||
{ value: 'google-gemini', label: 'Google Gemini' },
|
||||
]
|
||||
|
||||
// 服务商快捷预设
|
||||
export const PROVIDER_PRESETS = [
|
||||
{ key: 'openai', label: 'OpenAI 官方', baseUrl: 'https://api.openai.com/v1', api: 'openai-completions' },
|
||||
{ key: 'anthropic', label: 'Anthropic 官方', baseUrl: 'https://api.anthropic.com', api: 'anthropic-messages' },
|
||||
{ key: 'deepseek', label: 'DeepSeek', baseUrl: 'https://api.deepseek.com/v1', api: 'openai-completions' },
|
||||
{ key: 'google', label: 'Google Gemini', baseUrl: 'https://generativelanguage.googleapis.com/v1beta', api: 'google-gemini' },
|
||||
{ key: 'ollama', label: 'Ollama (本地)', baseUrl: 'http://127.0.0.1:11434/v1', api: 'openai-completions' },
|
||||
]
|
||||
|
||||
// gpt.qt.cool 推广配置
|
||||
export const QTCOOL = {
|
||||
baseUrl: 'https://gpt.qt.cool/v1',
|
||||
defaultKey: 'sk-0JDu7hyc51ZKD4iNebpFu07EUEhXmVVc',
|
||||
site: 'https://gpt.qt.cool/',
|
||||
usageUrl: 'https://gpt.qt.cool/user?key=',
|
||||
providerKey: 'qtcool',
|
||||
api: 'openai-completions',
|
||||
models: [] // 始终从 API 动态获取最新模型列表
|
||||
}
|
||||
|
||||
// 常用模型预设(按服务商分组)
|
||||
export const MODEL_PRESETS = {
|
||||
openai: [
|
||||
{ id: 'gpt-4o', name: 'GPT-4o', contextWindow: 128000 },
|
||||
{ id: 'gpt-4o-mini', name: 'GPT-4o Mini', contextWindow: 128000 },
|
||||
{ id: 'o3-mini', name: 'o3 Mini', contextWindow: 200000, reasoning: true },
|
||||
],
|
||||
anthropic: [
|
||||
{ id: 'claude-sonnet-4-5-20250514', name: 'Claude Sonnet 4.5', contextWindow: 200000 },
|
||||
{ id: 'claude-haiku-3-5-20241022', name: 'Claude Haiku 3.5', contextWindow: 200000 },
|
||||
],
|
||||
deepseek: [
|
||||
{ id: 'deepseek-chat', name: 'DeepSeek V3', contextWindow: 64000 },
|
||||
{ id: 'deepseek-reasoner', name: 'DeepSeek R1', contextWindow: 64000, reasoning: true },
|
||||
],
|
||||
google: [
|
||||
{ id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro', contextWindow: 1000000, reasoning: true },
|
||||
{ id: 'gemini-2.5-flash', name: 'Gemini 2.5 Flash', contextWindow: 1000000 },
|
||||
],
|
||||
ollama: [
|
||||
{ id: 'qwen2.5:7b', name: 'Qwen 2.5 7B', contextWindow: 32768 },
|
||||
{ id: 'llama3.2', name: 'Llama 3.2', contextWindow: 8192 },
|
||||
{ id: 'gemma3', name: 'Gemma 3', contextWindow: 32768 },
|
||||
],
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态获取 QTCOOL 模型列表
|
||||
* @param {string} [apiKey] - 自定义密钥,不传则用默认密钥
|
||||
* @returns {Promise<Array<{id:string, name:string, contextWindow:number, reasoning?:boolean}>>}
|
||||
*/
|
||||
export async function fetchQtcoolModels(apiKey) {
|
||||
const key = apiKey || QTCOOL.defaultKey
|
||||
try {
|
||||
const resp = await fetch(QTCOOL.baseUrl + '/models', {
|
||||
headers: { 'Authorization': 'Bearer ' + key },
|
||||
signal: AbortSignal.timeout(8000)
|
||||
})
|
||||
if (resp.ok) {
|
||||
const data = await resp.json()
|
||||
if (data.data && data.data.length) {
|
||||
return data.data.map(m => ({
|
||||
id: m.id, name: m.id, contextWindow: 128000,
|
||||
reasoning: m.id.includes('codex')
|
||||
})).sort((a, b) => b.id.localeCompare(a.id))
|
||||
}
|
||||
}
|
||||
} catch { /* use fallback */ }
|
||||
return QTCOOL.models
|
||||
}
|
||||
@@ -107,6 +107,11 @@ async function webInvoke(cmd, args) {
|
||||
if (!isTauri && window.__clawpanel_show_login) window.__clawpanel_show_login()
|
||||
throw new Error('需要登录')
|
||||
}
|
||||
// 检测后端是否可用:如果返回的是 HTML(非 JSON),说明后端未运行
|
||||
const ct = (resp.headers.get('content-type') || '').toLowerCase()
|
||||
if (ct.includes('text/html') || ct.includes('text/plain')) {
|
||||
throw new Error('后端服务未运行,该功能需要 Web 部署模式')
|
||||
}
|
||||
if (!resp.ok) {
|
||||
const data = await resp.json().catch(() => ({ error: `HTTP ${resp.status}` }))
|
||||
throw new Error(data.error || `HTTP ${resp.status}`)
|
||||
@@ -196,7 +201,7 @@ export const api = {
|
||||
readPlatformConfig: (platform) => invoke('read_platform_config', { platform }),
|
||||
saveMessagingPlatform: (platform, form) => { invalidate('list_configured_platforms', 'read_platform_config'); return invoke('save_messaging_platform', { platform, form }) },
|
||||
removeMessagingPlatform: (platform) => { invalidate('list_configured_platforms', 'read_platform_config'); return invoke('remove_messaging_platform', { platform }) },
|
||||
toggleMessagingPlatform: (platform, enabled) => { invalidate('list_configured_platforms'); return invoke('toggle_messaging_platform', { platform, enabled }) },
|
||||
toggleMessagingPlatform: (platform, enabled) => { invalidate('list_configured_platforms', 'read_openclaw_config', 'read_platform_config'); return invoke('toggle_messaging_platform', { platform, enabled }) },
|
||||
verifyBotToken: (platform, form) => invoke('verify_bot_token', { platform, form }),
|
||||
listConfiguredPlatforms: () => cachedInvoke('list_configured_platforms', {}, 5000),
|
||||
getChannelPluginStatus: (pluginId) => invoke('get_channel_plugin_status', { pluginId }),
|
||||
|
||||
Reference in New Issue
Block a user