release: v1.0.8 — 第三方模型 404 修复、Coding Plan 套餐

This commit is contained in:
10000ge10000
2026-03-07 11:14:01 +08:00
parent e844b4754b
commit 4489fe3e02
4 changed files with 144 additions and 19 deletions

View File

@@ -4,9 +4,39 @@
格式基于 [Keep a Changelog](https://keepachangelog.com/zh-CN/1.0.0/)。
## [1.0.8] - 2026-03-07
### 修复第三方模型配置导致 Gateway 崩溃 & 新增 Coding Plan 套餐支持
#### 修复
- **EACCES 权限错误** (#8): Web PTY 以 root 运行,创建的目录 (`sessions/`, `auth-profiles.json` 等) 归 root 所有Gateway 以 `openclaw` 用户运行时无法写入,报 `EACCES: permission denied, mkdir`
- `oc-config.sh`: `auth_set_apikey``json_set`、备份目录创建后均执行 `chown openclaw:openclaw`
- `web-pty.js`: 子进程退出时自动 `chown -R openclaw:openclaw` 整个数据目录
- **第三方模型 404/405 错误** (#11, #13, #14, #15): DeepSeek、xAI Grok、Groq 等 OpenAI 兼容提供商配置后返回 404/405
- 根因: 这些提供商缺少 `register_custom_provider` 调用,未写入 `baseUrl` 导致 Gateway 请求发到错误地址
- 修复: 为 DeepSeek、xAI、Groq 快速配置补充 `register_custom_provider` 调用
- **API 类型错误导致 Gateway 崩溃**: `register_custom_provider``api` 值设为 `openai-chat-completions`,但该值在 OpenClaw v2026.3.2 中不存在
- 正确值为 `openai-completions`,错误值会导致 Gateway 启动时 JSON schema 校验失败,进入 crash loop
- 有效 api 类型: `openai-completions` | `openai-responses` | `openai-codex-responses` | `anthropic-messages` | `google-generative-ai` | `github-copilot` | `bedrock-converse-stream` | `ollama`
#### 新增
- **阿里云 Coding Plan 套餐快速配置**: 千问配置菜单新增 Coding Plan 选项 (选项 c默认推荐)
- Provider: `bailian`Base URL: `https://coding.dashscope.aliyuncs.com/v1`
- 一键注册套餐内全部模型: qwen3.5-plus、qwen3-coder-plus、qwen3-coder-next、qwen3-max、MiniMax-M2.5、glm-5、glm-4.7、kimi-k2.5
- contextWindow / maxTokens 按阿里云官方文档设定 (最大 100万上下文)
- 参考: [阿里云 Coding Plan 文档](https://help.aliyun.com/zh/model-studio/openclaw-coding-plan)
#### 变更
- **千问配置菜单重构**: 从 2 种模式扩展为 3 种
- `a)` Qwen Portal OAuth (官方向导)
- `b)` 百炼按量付费 API Key (`sk-xxx` + `dashscope.aliyuncs.com`)
- `c)` Coding Plan 套餐 (`sk-sp-xxx` + `coding.dashscope.aliyuncs.com`) ★ 默认推荐
- 明确提示两套 API Key / Base URL 不互通
- **`register_custom_provider` 增强**: 新增可选参数 `context_window` (默认 128000) 和 `max_tokens` (默认 32000)
## [1.0.7] - 2026-03-06
### 修复依赖包名错误 & 补充 GNU tar 依赖 (感谢 [@esir](https://github.com/esir) 建议)
### 修复依赖包名错误 & 补充 GNU tar 依赖 (感谢 [@esir](https://github.com/esirplayground) 建议)
#### 修复
- **依赖包名修正**: `util-linux-script` 在 OpenWrt/iStoreOS 软件源中不存在,正确的包名是 `script-utils` (提供 `/usr/bin/script` 命令)。此错误会导致通过 iStore/opkg 安装插件时依赖解析失败

View File

@@ -1 +1 @@
1.0.7
1.0.8

View File

@@ -6,7 +6,7 @@
# ── 颜色 ──
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
CYAN='\033[0;36m'; BOLD='\033[1m'; DIM='\033[2m'; NC='\033[0m'
# ── 端口检查兼容函数 (ss 或 netstat) ──
# check_port_listening <port> — 检查端口是否在监听,返回 0/1
@@ -93,6 +93,7 @@ json_set() {
local key="$1" value="$2"
if [ ! -f "$CONFIG_FILE" ]; then
mkdir -p "$(dirname "$CONFIG_FILE")"
chown -R openclaw:openclaw "$OC_STATE_DIR" 2>/dev/null || true
echo '{}' > "$CONFIG_FILE"
fi
_JS_KEY="$key" _JS_VAL="$value" "$NODE_BIN" -e "
@@ -134,6 +135,7 @@ auth_set_apikey() {
local auth_dir="${OC_STATE_DIR}/agents/main/agent"
local auth_file="${auth_dir}/auth-profiles.json"
mkdir -p "$auth_dir"
chown -R openclaw:openclaw "${OC_STATE_DIR}/agents" 2>/dev/null || true
_AP_PROVIDER="$provider" _AP_KEY="$api_key" _AP_PROFILE="$profile_id" "$NODE_BIN" -e "
const fs=require('fs'),f=process.env._AP_FILE||'${auth_file}';
let d={version:1,profiles:{},usageStats:{}};
@@ -173,11 +175,13 @@ register_and_set_model() {
}
# ── 注册自定义提供商 (需要 baseUrl 的 OpenAI 兼容提供商) ──
# 用法: register_custom_provider <provider_name> <base_url> <api_key> <model_id> [model_display_name]
# 用法: register_custom_provider <provider_name> <base_url> <api_key> <model_id> [model_display_name] [context_window] [max_tokens]
# 例: register_custom_provider dashscope https://dashscope.aliyuncs.com/compatible-mode/v1 sk-xxx qwen-max "Qwen Max"
# 例: register_custom_provider bailian https://coding.dashscope.aliyuncs.com/v1 sk-sp-xxx qwen3.5-plus "qwen3.5-plus" 1000000 65536
register_custom_provider() {
local provider_name="$1" base_url="$2" api_key="$3" model_id="$4" model_display="${5:-$4}"
_RCP_PROV="$provider_name" _RCP_URL="$base_url" _RCP_KEY="$api_key" _RCP_MID="$model_id" _RCP_MNAME="$model_display" "$NODE_BIN" -e "
local ctx_window="${6:-128000}" max_tok="${7:-32000}"
_RCP_PROV="$provider_name" _RCP_URL="$base_url" _RCP_KEY="$api_key" _RCP_MID="$model_id" _RCP_MNAME="$model_display" _RCP_CTX="$ctx_window" _RCP_MAXTOK="$max_tok" "$NODE_BIN" -e "
const fs=require('fs');
let d={};
try{d=JSON.parse(fs.readFileSync('${CONFIG_FILE}','utf8'));}catch(e){}
@@ -188,15 +192,15 @@ register_custom_provider() {
d.models.providers[prov]={
baseUrl:process.env._RCP_URL,
apiKey:process.env._RCP_KEY,
api:'openai-responses',
api:'openai-completions',
models:[{
id:process.env._RCP_MID,
name:process.env._RCP_MNAME,
reasoning:false,
input:['text','image'],
cost:{input:0,output:0,cacheRead:0,cacheWrite:0},
contextWindow:128000,
maxTokens:32000
contextWindow:parseInt(process.env._RCP_CTX)||128000,
maxTokens:parseInt(process.env._RCP_MAXTOK)||32000
}]
};
fs.writeFileSync('${CONFIG_FILE}',JSON.stringify(d,null,2));
@@ -204,6 +208,44 @@ register_custom_provider() {
chown openclaw:openclaw "$CONFIG_FILE" 2>/dev/null || true
}
# ── 注册 Coding Plan 提供商 (多模型批量注册) ──
# 用法: register_codingplan_provider <api_key>
# 按阿里云官方文档注册 bailian 提供商,包含所有 Coding Plan 套餐支持的模型
register_codingplan_provider() {
local api_key="$1"
_RCP_KEY="$api_key" "$NODE_BIN" -e "
const fs=require('fs');
let d={};
try{d=JSON.parse(fs.readFileSync('${CONFIG_FILE}','utf8'));}catch(e){}
if(!d.models)d.models={};
if(!d.models.providers)d.models.providers={};
d.models.mode='merge';
d.models.providers['bailian']={
baseUrl:'https://coding.dashscope.aliyuncs.com/v1',
apiKey:process.env._RCP_KEY,
api:'openai-completions',
models:[
{id:'qwen3.5-plus',name:'qwen3.5-plus',reasoning:false,input:['text','image'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:1000000,maxTokens:65536},
{id:'qwen3-coder-plus',name:'qwen3-coder-plus',reasoning:false,input:['text'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:1000000,maxTokens:65536},
{id:'qwen3-coder-next',name:'qwen3-coder-next',reasoning:false,input:['text'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:262144,maxTokens:65536},
{id:'qwen3-max-2026-01-23',name:'qwen3-max-2026-01-23',reasoning:false,input:['text'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:262144,maxTokens:65536},
{id:'MiniMax-M2.5',name:'MiniMax-M2.5',reasoning:false,input:['text'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:204800,maxTokens:131072},
{id:'glm-5',name:'glm-5',reasoning:false,input:['text'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:202752,maxTokens:16384},
{id:'glm-4.7',name:'glm-4.7',reasoning:false,input:['text'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:202752,maxTokens:16384},
{id:'kimi-k2.5',name:'kimi-k2.5',reasoning:false,input:['text','image'],cost:{input:0,output:0,cacheRead:0,cacheWrite:0},contextWindow:262144,maxTokens:32768}
]
};
if(!d.agents)d.agents={};
if(!d.agents.defaults)d.agents.defaults={};
if(!d.agents.defaults.models)d.agents.defaults.models={};
['qwen3.5-plus','qwen3-coder-plus','qwen3-coder-next','qwen3-max-2026-01-23','MiniMax-M2.5','glm-5','glm-4.7','kimi-k2.5'].forEach(m=>{
d.agents.defaults.models['bailian/'+m]={};
});
fs.writeFileSync('${CONFIG_FILE}',JSON.stringify(d,null,2));
" 2>/dev/null
chown openclaw:openclaw "$CONFIG_FILE" 2>/dev/null || true
}
# ── 辅助函数 ──
# 清理输入: 去除 ANSI 转义序列、不可见字符,只保留 ASCII 可打印字符
@@ -355,7 +397,7 @@ configure_model() {
echo -e " ${CYAN}5)${NC} OpenRouter (聚合多家模型)"
echo -e " ${CYAN}6)${NC} DeepSeek (DeepSeek-V3/R1)"
echo -e " ${CYAN}7)${NC} GitHub Copilot (需要 Copilot 订阅)"
echo -e " ${CYAN}8)${NC} 阿里云通义千问 Qwen (Portal/API)"
echo -e " ${CYAN}8)${NC} 阿里云通义千问 Qwen (Portal/API/Coding Plan)"
echo -e " ${CYAN}9)${NC} xAI Grok (Grok-3/3-mini)"
echo -e " ${CYAN}10)${NC} Groq (Llama 4, Llama 3.3)"
echo -e " ${CYAN}11)${NC} 硅基流动 SiliconFlow"
@@ -508,7 +550,6 @@ configure_model() {
echo ""
prompt_with_default "请输入 DeepSeek API Key" "" api_key
if [ -n "$api_key" ]; then
auth_set_apikey deepseek "$api_key"
echo ""
echo -e " ${CYAN}可用模型:${NC}"
echo -e " ${CYAN}a)${NC} deepseek-chat — DeepSeek-V3 (通用对话)"
@@ -522,6 +563,8 @@ configure_model() {
c) prompt_with_default "请输入模型名称" "deepseek-chat" model_name ;;
*) model_name="deepseek-chat" ;;
esac
auth_set_apikey deepseek "$api_key"
register_custom_provider deepseek "https://api.deepseek.com/v1" "$api_key" "$model_name" "$model_name"
register_and_set_model "deepseek/${model_name}"
echo -e " ${GREEN}✅ DeepSeek 已配置,活跃模型: deepseek/${model_name}${NC}"
fi
@@ -580,13 +623,15 @@ configure_model() {
8)
echo ""
echo -e " ${BOLD}阿里云通义千问 Qwen 配置${NC}"
echo -e " ${YELLOW}使用 OpenClaw 官方 Qwen Portal 认证插件${NC}"
echo ""
echo -e " ${CYAN}配置方式:${NC}"
echo -e " ${CYAN}a)${NC} 通过官方向导配置 (推荐)"
echo -e " ${CYAN}b)${NC} 通过 API Key 手动配置 (兼容 OpenAI 格式)"
echo -e " ${CYAN}a)${NC} 通过官方向导配置 (Qwen Portal OAuth)"
echo -e " ${CYAN}b)${NC} 百炼按量付费 API Key (sk-xxx, 按 token 计费)"
echo -e " ${CYAN}c)${NC} ${GREEN}Coding Plan 套餐${NC} (sk-sp-xxx, 按套餐抵扣额度) ${GREEN}★ 推荐${NC}"
echo ""
prompt_with_default "请选择" "b" qwen_mode
echo -e " ${DIM}提示: Coding Plan 套餐和百炼按量付费的 API Key / Base URL 不互通,请勿混用${NC}"
echo ""
prompt_with_default "请选择" "c" qwen_mode
case "$qwen_mode" in
a)
echo ""
@@ -597,11 +642,13 @@ configure_model() {
echo ""
ask_restart
;;
b|*)
b)
echo ""
echo -e " ${BOLD}百炼按量付费配置${NC}"
echo -e " ${YELLOW}获取 API Key: https://dashscope.console.aliyun.com/apiKey${NC}"
echo -e " ${DIM}Base URL: https://dashscope.aliyuncs.com/compatible-mode/v1${NC}"
echo ""
prompt_with_default "请输入阿里云 API Key (sk-...)" "" api_key
prompt_with_default "请输入百炼 API Key (sk-...)" "" api_key
if [ -n "$api_key" ]; then
echo ""
echo -e " ${CYAN}可用模型:${NC}"
@@ -623,7 +670,49 @@ configure_model() {
auth_set_apikey dashscope "$api_key"
register_custom_provider dashscope "https://dashscope.aliyuncs.com/compatible-mode/v1" "$api_key" "$model_name" "$model_name"
register_and_set_model "dashscope/${model_name}"
echo -e " ${GREEN}✅ 通义千问已配置,活跃模型: dashscope/${model_name}${NC}"
echo -e " ${GREEN}✅ 通义千问已配置 (按量付费),活跃模型: dashscope/${model_name}${NC}"
fi
;;
c|*)
echo ""
echo -e " ${BOLD}Coding Plan 套餐配置${NC}"
echo -e " ${YELLOW}订阅套餐: https://bailian.console.aliyun.com/cn-beijing/?tab=model#/efm/coding_plan${NC}"
echo -e " ${YELLOW}获取专属 API Key: 在上方页面获取 Coding Plan 专属 Key (sk-sp-...)${NC}"
echo -e " ${DIM}Base URL: https://coding.dashscope.aliyuncs.com/v1${NC}"
echo -e " ${DIM}文档: https://help.aliyun.com/zh/model-studio/openclaw-coding-plan${NC}"
echo ""
prompt_with_default "请输入 Coding Plan 专属 API Key (sk-sp-...)" "" api_key
if [ -n "$api_key" ]; then
echo ""
echo -e " ${CYAN}可用模型:${NC}"
echo -e " ${CYAN}a)${NC} qwen3.5-plus — Qwen3.5 Plus (推荐, 100万上下文)"
echo -e " ${CYAN}b)${NC} qwen3-coder-plus — Qwen3 Coder Plus (代码专用, 100万上下文)"
echo -e " ${CYAN}c)${NC} qwen3-coder-next — Qwen3 Coder Next"
echo -e " ${CYAN}d)${NC} qwen3-max-2026-01-23 — Qwen3 Max"
echo -e " ${CYAN}e)${NC} MiniMax-M2.5 — MiniMax M2.5"
echo -e " ${CYAN}f)${NC} glm-5 — 智谱 GLM-5"
echo -e " ${CYAN}g)${NC} kimi-k2.5 — Kimi K2.5"
echo -e " ${CYAN}h)${NC} 手动输入模型名"
echo ""
prompt_with_default "请选择默认模型" "a" model_choice
case "$model_choice" in
a) model_name="qwen3.5-plus" ;;
b) model_name="qwen3-coder-plus" ;;
c) model_name="qwen3-coder-next" ;;
d) model_name="qwen3-max-2026-01-23" ;;
e) model_name="MiniMax-M2.5" ;;
f) model_name="glm-5" ;;
g) model_name="kimi-k2.5" ;;
h) prompt_with_default "请输入模型名称" "qwen3.5-plus" model_name ;;
*) model_name="qwen3.5-plus" ;;
esac
echo ""
echo -e " ${CYAN}正在注册 Coding Plan 提供商 (含全部可用模型)...${NC}"
auth_set_apikey bailian "$api_key"
register_codingplan_provider "$api_key"
register_and_set_model "bailian/${model_name}"
echo -e " ${GREEN}✅ Coding Plan 已配置,活跃模型: bailian/${model_name}${NC}"
echo -e " ${DIM}提示: 套餐内全部模型已注册,可随时在 WebChat 中通过 /model 切换${NC}"
fi
;;
esac
@@ -635,7 +724,6 @@ configure_model() {
echo ""
prompt_with_default "请输入 xAI API Key" "" api_key
if [ -n "$api_key" ]; then
auth_set_apikey xai "$api_key"
echo ""
echo -e " ${CYAN}可用模型:${NC}"
echo -e " ${CYAN}a)${NC} grok-3 — Grok 3 旗舰"
@@ -649,6 +737,8 @@ configure_model() {
c) prompt_with_default "请输入模型名称" "grok-3" model_name ;;
*) model_name="grok-3" ;;
esac
auth_set_apikey xai "$api_key"
register_custom_provider xai "https://api.x.ai/v1" "$api_key" "$model_name" "$model_name"
register_and_set_model "xai/${model_name}"
echo -e " ${GREEN}✅ xAI Grok 已配置,活跃模型: xai/${model_name}${NC}"
fi
@@ -661,7 +751,6 @@ configure_model() {
echo ""
prompt_with_default "请输入 Groq API Key" "" api_key
if [ -n "$api_key" ]; then
auth_set_apikey groq "$api_key"
echo ""
echo -e " ${CYAN}可用模型:${NC}"
echo -e " ${CYAN}a)${NC} llama-4-maverick-17b-128e — Llama 4 Maverick (推荐)"
@@ -677,6 +766,8 @@ configure_model() {
d) prompt_with_default "请输入模型名称" "llama-4-maverick-17b-128e" model_name ;;
*) model_name="llama-4-maverick-17b-128e" ;;
esac
auth_set_apikey groq "$api_key"
register_custom_provider groq "https://api.groq.com/openai/v1" "$api_key" "$model_name" "$model_name"
register_and_set_model "groq/${model_name}"
echo -e " ${GREEN}✅ Groq 已配置,活跃模型: groq/${model_name}${NC}"
fi
@@ -1428,6 +1519,7 @@ reset_to_defaults() {
local backup_dir="${OC_STATE_DIR}/backups"
local backup_ts=$(date +%Y%m%d_%H%M%S)
mkdir -p "$backup_dir"
chown openclaw:openclaw "$backup_dir" 2>/dev/null || true
if [ -f "$CONFIG_FILE" ]; then
cp "$CONFIG_FILE" "${backup_dir}/openclaw_${backup_ts}.json"
echo -e " ${GREEN} 备份已保存: backups/openclaw_${backup_ts}.json${NC}"

View File

@@ -177,6 +177,9 @@ class PtySession {
this.proc.stderr.on('data', (d) => { if (this.alive) { this._spawnFailCount = 0; this.socket.write(encodeWSFrame(d, 0x01)); } });
this.proc.on('close', (code) => {
if (!this.alive) return;
// PTY 以 root 运行,子脚本可能创建了 root-owned 的目录
// 修复权限,防止以 openclaw 用户运行的 Gateway 遇到 EACCES
try { require('child_process').execFileSync('chown', ['-R', 'openclaw:openclaw', OC_DATA], { stdio: 'pipe', timeout: 5000 }); } catch(e) {}
this._spawnFailCount++;
if (this._spawnFailCount > this._MAX_SPAWN_RETRIES) {
console.log(`[oc-config] Script failed ${this._spawnFailCount} times, stopping retries`);