feat(config): Config.html Pro 版 + 渠道卡片展开填写 + Portable 模式

- Config.html 全新 Pro 版:3步式配置(选模型→填Key→启动)
- Step 3 渠道卡片可展开填写:Telegram / QQ Bot / 飞书 / 企业微信
- 渠道配置启动时自动写入 openclaw.json
- main.js 支持 Portable 模式(.app 旁有 portable/ 目录时使用)
- hasModelConfigured() 兼容新配置格式
- Node 升级到 v22.16.0
This commit is contained in:
dongsheng123132
2026-03-19 15:25:42 +08:00
parent 244bfacbbc
commit 889fc2a685
5 changed files with 1050 additions and 4277 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -14,7 +14,7 @@ NC='\033[0m'
BOLD='\033[1m' BOLD='\033[1m'
APP_DIR="$(cd "$(dirname "$0")" && pwd)" APP_DIR="$(cd "$(dirname "$0")" && pwd)"
NODE_VER="v22.14.0" NODE_VER="v22.16.0"
MIRROR="https://registry.npmmirror.com" MIRROR="https://registry.npmmirror.com"
NODE_MIRROR="https://npmmirror.com/mirrors/node" NODE_MIRROR="https://npmmirror.com/mirrors/node"

View File

@@ -44,8 +44,22 @@ function getNodeBin() {
return 'node'; return 'node';
} }
// User data // Portable mode: if a `portable/` directory exists next to the .app bundle, use it for data
const userDataPath = app.getPath('userData'); function getPortableDataPath() {
const appPath = app.getAppPath(); // inside .app/Contents/Resources/app
// Walk up to the .app's parent directory
const appBundleDir = path.resolve(appPath, '..', '..', '..', '..');
const portableDir = path.join(appBundleDir, 'portable');
if (fs.existsSync(portableDir)) {
console.log(`[${APP_NAME}] Portable mode: data in ${portableDir}`);
return portableDir;
}
return null;
}
// User data — portable or default
const portablePath = app.isPackaged ? getPortableDataPath() : null;
const userDataPath = portablePath || app.getPath('userData');
const configDir = path.join(userDataPath, '.openclaw'); const configDir = path.join(userDataPath, '.openclaw');
const configPath = path.join(configDir, 'openclaw.json'); const configPath = path.join(configDir, 'openclaw.json');
@@ -85,7 +99,13 @@ function getConfig() {
function hasModelConfigured() { function hasModelConfigured() {
const config = getConfig(); const config = getConfig();
return !!(config.agent && config.agent.model); // Check new format: agents.defaults.model.primary or env with API key or models.providers
if (config.agents?.defaults?.model?.primary) return true;
if (config.env && Object.keys(config.env).some(k => k.includes('API_KEY'))) return true;
if (config.models?.providers && Object.keys(config.models.providers).length > 0) return true;
// Legacy format
if (config.agent?.model) return true;
return false;
} }
function getToken() { function getToken() {