@simonyea/holysheep-cli 1.6.4 → 1.6.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/package.json +1 -1
- package/src/tools/droid.js +56 -17
- package/src/tools/openclaw.js +47 -8
package/README.md
CHANGED
|
@@ -218,6 +218,9 @@ A: OpenClaw 需要 Node.js 20+,运行 `node --version` 确认版本后重试
|
|
|
218
218
|
|
|
219
219
|
## Changelog
|
|
220
220
|
|
|
221
|
+
- **v1.6.7** — OpenClaw 配置新增 `MiniMax-M2.7-highspeed`,并补齐节点迁移脚本中的 SSH 代理账号创建逻辑
|
|
222
|
+
- **v1.6.6** — 修复 Droid CLI 的 GPT-5.4 配置残留问题,同时同步 `~/.factory/settings.json` 和 `~/.factory/config.json`,统一使用 `openai + https://api.holysheep.ai/v1`
|
|
223
|
+
- **v1.6.5** — 修复 HolySheep 对 Droid Responses API 的兼容
|
|
221
224
|
- **v1.6.4** — 修复 OpenClaw 的 npx 运行时检测,避免配置后页面仍卡在 Unauthorized / 未连接状态
|
|
222
225
|
- **v1.6.3** — OpenClaw 默认模型改为 GPT-5.4,并继续保留 Claude 模型切换能力
|
|
223
226
|
- **v1.6.2** — 修复 OpenClaw 配置误判与 npx 回退,端口冲突时自动切换空闲端口,并补充 Doctor 诊断
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonyea/holysheep-cli",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.7",
|
|
4
4
|
"description": "Claude Code/Cursor/Cline API relay for China — ¥1=$1, WeChat/Alipay payment, no credit card, no VPN. One command setup for all AI coding tools.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"openai-china",
|
package/src/tools/droid.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* 配置文件: ~/.factory/settings.json
|
|
4
4
|
*
|
|
5
5
|
* 使用 Droid 原生 customModels 配置 HolySheep 的多个模型入口:
|
|
6
|
-
* - GPT 走 OpenAI 兼容入口: https://api.holysheep.ai/
|
|
6
|
+
* - GPT 走 OpenAI 兼容入口: https://api.holysheep.ai/v1
|
|
7
7
|
* - Claude 走 Anthropic 入口: https://api.holysheep.ai
|
|
8
8
|
* - MiniMax 走 Anthropic 入口: https://api.holysheep.ai/minimax
|
|
9
9
|
*/
|
|
@@ -13,12 +13,13 @@ const os = require('os')
|
|
|
13
13
|
|
|
14
14
|
const CONFIG_DIR = path.join(os.homedir(), '.factory')
|
|
15
15
|
const SETTINGS_FILE = path.join(CONFIG_DIR, 'settings.json')
|
|
16
|
+
const LEGACY_CONFIG_FILE = path.join(CONFIG_DIR, 'config.json')
|
|
16
17
|
|
|
17
18
|
const DEFAULT_MODELS = [
|
|
18
19
|
{
|
|
19
20
|
model: 'gpt-5.4',
|
|
20
21
|
id: 'custom:gpt-5.4-0',
|
|
21
|
-
baseUrlSuffix: '
|
|
22
|
+
baseUrlSuffix: '',
|
|
22
23
|
displayName: 'GPT-5.4',
|
|
23
24
|
provider: 'openai',
|
|
24
25
|
},
|
|
@@ -66,6 +67,24 @@ function writeSettings(data) {
|
|
|
66
67
|
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(data, null, 2), 'utf8')
|
|
67
68
|
}
|
|
68
69
|
|
|
70
|
+
function readLegacyConfig() {
|
|
71
|
+
try {
|
|
72
|
+
if (fs.existsSync(LEGACY_CONFIG_FILE)) {
|
|
73
|
+
return JSON.parse(fs.readFileSync(LEGACY_CONFIG_FILE, 'utf8'))
|
|
74
|
+
}
|
|
75
|
+
} catch {}
|
|
76
|
+
return {}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function writeLegacyConfig(data) {
|
|
80
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
81
|
+
fs.writeFileSync(LEGACY_CONFIG_FILE, JSON.stringify(data, null, 2), 'utf8')
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isHolySheepModel(item) {
|
|
85
|
+
return typeof item?.baseUrl === 'string' && item.baseUrl.includes('api.holysheep.ai')
|
|
86
|
+
}
|
|
87
|
+
|
|
69
88
|
function normalizeSelectedModels(selectedModels) {
|
|
70
89
|
const selected = new Set(
|
|
71
90
|
Array.isArray(selectedModels) && selectedModels.length > 0
|
|
@@ -84,13 +103,17 @@ function normalizeSelectedModels(selectedModels) {
|
|
|
84
103
|
return models.length > 0 ? models : DEFAULT_MODELS.map((item, index) => ({ ...item, index }))
|
|
85
104
|
}
|
|
86
105
|
|
|
87
|
-
function buildCustomModels(apiKey, baseUrlAnthropic, selectedModels) {
|
|
88
|
-
const
|
|
106
|
+
function buildCustomModels(apiKey, baseUrlAnthropic, baseUrlOpenAI, selectedModels) {
|
|
107
|
+
const anthropicRootUrl = String(baseUrlAnthropic || '').replace(/\/+$/, '')
|
|
108
|
+
const openaiRootUrl = String(baseUrlOpenAI || '').replace(/\/+$/, '')
|
|
89
109
|
return normalizeSelectedModels(selectedModels).map((item) => ({
|
|
90
110
|
model: item.model,
|
|
91
111
|
id: item.id,
|
|
92
112
|
index: item.index,
|
|
93
|
-
baseUrl:
|
|
113
|
+
baseUrl:
|
|
114
|
+
item.provider === 'openai'
|
|
115
|
+
? `${openaiRootUrl}${item.baseUrlSuffix}`
|
|
116
|
+
: `${anthropicRootUrl}${item.baseUrlSuffix}`,
|
|
94
117
|
apiKey,
|
|
95
118
|
displayName: item.displayName,
|
|
96
119
|
maxOutputTokens: 64000,
|
|
@@ -108,25 +131,37 @@ module.exports = {
|
|
|
108
131
|
isConfigured() {
|
|
109
132
|
const settings = readSettings()
|
|
110
133
|
const customModels = Array.isArray(settings.customModels) ? settings.customModels : []
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
134
|
+
if (customModels.some(isHolySheepModel)) return true
|
|
135
|
+
|
|
136
|
+
const legacy = readLegacyConfig()
|
|
137
|
+
const legacyModels = Array.isArray(legacy.customModels) ? legacy.customModels : []
|
|
138
|
+
return legacyModels.some(isHolySheepModel)
|
|
114
139
|
},
|
|
115
|
-
configure(apiKey, baseUrlAnthropic,
|
|
140
|
+
configure(apiKey, baseUrlAnthropic, baseUrlOpenAI, _primaryModel, selectedModels) {
|
|
141
|
+
const nextModels = buildCustomModels(apiKey, baseUrlAnthropic, baseUrlOpenAI, selectedModels)
|
|
142
|
+
|
|
116
143
|
const settings = readSettings()
|
|
117
144
|
const preservedModels = Array.isArray(settings.customModels)
|
|
118
|
-
? settings.customModels.filter(
|
|
119
|
-
(item) => !(typeof item.baseUrl === 'string' && item.baseUrl.includes('api.holysheep.ai'))
|
|
120
|
-
)
|
|
145
|
+
? settings.customModels.filter((item) => !isHolySheepModel(item))
|
|
121
146
|
: []
|
|
122
|
-
|
|
123
147
|
settings.customModels = [
|
|
124
|
-
...
|
|
148
|
+
...nextModels,
|
|
125
149
|
...preservedModels,
|
|
126
150
|
]
|
|
127
151
|
settings.logoAnimation = 'off'
|
|
128
152
|
writeSettings(settings)
|
|
129
153
|
|
|
154
|
+
const legacy = readLegacyConfig()
|
|
155
|
+
const preservedLegacyModels = Array.isArray(legacy.customModels)
|
|
156
|
+
? legacy.customModels.filter((item) => !isHolySheepModel(item))
|
|
157
|
+
: []
|
|
158
|
+
legacy.customModels = [
|
|
159
|
+
...nextModels,
|
|
160
|
+
...preservedLegacyModels,
|
|
161
|
+
]
|
|
162
|
+
legacy.logoAnimation = 'off'
|
|
163
|
+
writeLegacyConfig(legacy)
|
|
164
|
+
|
|
130
165
|
return {
|
|
131
166
|
file: SETTINGS_FILE,
|
|
132
167
|
hot: true,
|
|
@@ -135,11 +170,15 @@ module.exports = {
|
|
|
135
170
|
reset() {
|
|
136
171
|
const settings = readSettings()
|
|
137
172
|
if (Array.isArray(settings.customModels)) {
|
|
138
|
-
settings.customModels = settings.customModels.filter(
|
|
139
|
-
(item) => !(typeof item.baseUrl === 'string' && item.baseUrl.includes('api.holysheep.ai'))
|
|
140
|
-
)
|
|
173
|
+
settings.customModels = settings.customModels.filter((item) => !isHolySheepModel(item))
|
|
141
174
|
}
|
|
142
175
|
writeSettings(settings)
|
|
176
|
+
|
|
177
|
+
const legacy = readLegacyConfig()
|
|
178
|
+
if (Array.isArray(legacy.customModels)) {
|
|
179
|
+
legacy.customModels = legacy.customModels.filter((item) => !isHolySheepModel(item))
|
|
180
|
+
}
|
|
181
|
+
writeLegacyConfig(legacy)
|
|
143
182
|
},
|
|
144
183
|
getConfigPath() { return SETTINGS_FILE },
|
|
145
184
|
hint: '已写入 ~/.factory/settings.json;重启 Droid 后可见 HolySheep 模型列表',
|
package/src/tools/openclaw.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenClaw 适配器 (v2 — 基于实测的正确配置格式)
|
|
3
3
|
*
|
|
4
|
-
* 正确方案:写入 HolySheep 的 OpenAI + Anthropic
|
|
5
|
-
* 默认模型固定为 GPT-5.4,同时保留 Claude 模型供 /model 切换。
|
|
4
|
+
* 正确方案:写入 HolySheep 的 OpenAI + Anthropic + MiniMax provider,
|
|
5
|
+
* 默认模型固定为 GPT-5.4,同时保留 Claude / MiniMax 模型供 /model 切换。
|
|
6
6
|
*/
|
|
7
7
|
const fs = require('fs')
|
|
8
8
|
const path = require('path')
|
|
@@ -17,6 +17,7 @@ const DEFAULT_GATEWAY_PORT = 18789
|
|
|
17
17
|
const MAX_PORT_SCAN = 20
|
|
18
18
|
const OPENCLAW_DEFAULT_MODEL = 'gpt-5.4'
|
|
19
19
|
const OPENCLAW_DEFAULT_CLAUDE_MODEL = 'claude-sonnet-4-6'
|
|
20
|
+
const OPENCLAW_DEFAULT_MINIMAX_MODEL = 'MiniMax-M2.7-highspeed'
|
|
20
21
|
|
|
21
22
|
function hasOpenClawBinary() {
|
|
22
23
|
return commandExists('openclaw')
|
|
@@ -189,6 +190,11 @@ function getLaunchCommand(port = getConfiguredGatewayPort()) {
|
|
|
189
190
|
return `${runtime} gateway --port ${port}`
|
|
190
191
|
}
|
|
191
192
|
|
|
193
|
+
function getDashboardCommand() {
|
|
194
|
+
const runtime = module.exports._lastRuntimeCommand || (hasOpenClawBinary() ? 'openclaw' : 'npx openclaw')
|
|
195
|
+
return `${runtime} dashboard --no-open`
|
|
196
|
+
}
|
|
197
|
+
|
|
192
198
|
function buildProviderName(baseUrl, prefix) {
|
|
193
199
|
const hostname = new URL(baseUrl).hostname.replace(/\./g, '-')
|
|
194
200
|
return `${prefix}-${hostname}`
|
|
@@ -208,7 +214,7 @@ function buildModelEntry(id) {
|
|
|
208
214
|
function buildManagedPlan(apiKey, baseUrlAnthropic, baseUrlOpenAI, selectedModels) {
|
|
209
215
|
const requestedModels = Array.isArray(selectedModels) && selectedModels.length > 0
|
|
210
216
|
? selectedModels
|
|
211
|
-
: [OPENCLAW_DEFAULT_MODEL, OPENCLAW_DEFAULT_CLAUDE_MODEL]
|
|
217
|
+
: [OPENCLAW_DEFAULT_MODEL, OPENCLAW_DEFAULT_CLAUDE_MODEL, OPENCLAW_DEFAULT_MINIMAX_MODEL]
|
|
212
218
|
|
|
213
219
|
const openaiModels = requestedModels.filter((model) => model.startsWith('gpt-'))
|
|
214
220
|
if (!openaiModels.includes(OPENCLAW_DEFAULT_MODEL)) {
|
|
@@ -220,8 +226,14 @@ function buildManagedPlan(apiKey, baseUrlAnthropic, baseUrlOpenAI, selectedModel
|
|
|
220
226
|
claudeModels.push(OPENCLAW_DEFAULT_CLAUDE_MODEL)
|
|
221
227
|
}
|
|
222
228
|
|
|
229
|
+
const minimaxModels = requestedModels.filter((model) => model.startsWith('MiniMax-'))
|
|
230
|
+
if (requestedModels.includes(OPENCLAW_DEFAULT_MINIMAX_MODEL) && !minimaxModels.includes(OPENCLAW_DEFAULT_MINIMAX_MODEL)) {
|
|
231
|
+
minimaxModels.unshift(OPENCLAW_DEFAULT_MINIMAX_MODEL)
|
|
232
|
+
}
|
|
233
|
+
|
|
223
234
|
const openaiProviderName = buildProviderName(baseUrlOpenAI, 'custom-openai')
|
|
224
235
|
const anthropicProviderName = buildProviderName(baseUrlAnthropic, 'custom-anthropic')
|
|
236
|
+
const minimaxProviderName = buildProviderName(`${baseUrlAnthropic.replace(/\/+$/, '')}/minimax`, 'custom-anthropic')
|
|
225
237
|
|
|
226
238
|
const providers = {
|
|
227
239
|
[openaiProviderName]: {
|
|
@@ -238,9 +250,19 @@ function buildManagedPlan(apiKey, baseUrlAnthropic, baseUrlOpenAI, selectedModel
|
|
|
238
250
|
},
|
|
239
251
|
}
|
|
240
252
|
|
|
253
|
+
if (minimaxModels.length > 0) {
|
|
254
|
+
providers[minimaxProviderName] = {
|
|
255
|
+
baseUrl: `${baseUrlAnthropic.replace(/\/+$/, '')}/minimax`,
|
|
256
|
+
apiKey,
|
|
257
|
+
api: 'anthropic-messages',
|
|
258
|
+
models: minimaxModels.map(buildModelEntry),
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
241
262
|
const managedModelRefs = [
|
|
242
263
|
...openaiModels.map((id) => `${openaiProviderName}/${id}`),
|
|
243
264
|
...claudeModels.map((id) => `${anthropicProviderName}/${id}`),
|
|
265
|
+
...minimaxModels.map((id) => `${minimaxProviderName}/${id}`),
|
|
244
266
|
]
|
|
245
267
|
|
|
246
268
|
return {
|
|
@@ -362,6 +384,18 @@ function _startGateway(port, preferNpx = false, preferService = true) {
|
|
|
362
384
|
return false
|
|
363
385
|
}
|
|
364
386
|
|
|
387
|
+
function getDashboardUrl(port, preferNpx = false) {
|
|
388
|
+
const result = runOpenClaw(['dashboard', '--no-open'], {
|
|
389
|
+
preferNpx,
|
|
390
|
+
timeout: preferNpx ? 60000 : 20000,
|
|
391
|
+
})
|
|
392
|
+
if (result.status === 0) {
|
|
393
|
+
const match = String(result.stdout || '').match(/Dashboard URL:\s*(\S+)/)
|
|
394
|
+
if (match) return match[1]
|
|
395
|
+
}
|
|
396
|
+
return `http://127.0.0.1:${port}/`
|
|
397
|
+
}
|
|
398
|
+
|
|
365
399
|
module.exports = {
|
|
366
400
|
name: 'OpenClaw',
|
|
367
401
|
id: 'openclaw',
|
|
@@ -452,10 +486,11 @@ module.exports = {
|
|
|
452
486
|
console.log(chalk.yellow(' ⚠️ Gateway 启动中,稍等几秒后刷新浏览器'))
|
|
453
487
|
}
|
|
454
488
|
|
|
455
|
-
const dashUrl =
|
|
456
|
-
console.log(chalk.cyan('\n →
|
|
489
|
+
const dashUrl = getDashboardUrl(gatewayPort, runtime.via === 'npx')
|
|
490
|
+
console.log(chalk.cyan('\n → 浏览器打开(推荐使用此地址):'))
|
|
457
491
|
console.log(chalk.bold.cyan(` ${dashUrl}`))
|
|
458
492
|
console.log(chalk.gray(` 默认模型: ${OPENCLAW_DEFAULT_MODEL}`))
|
|
493
|
+
console.log(chalk.gray(' 如在 Windows 上打开裸 http://127.0.0.1:PORT/ 仍报 Unauthorized,请使用上面的 dashboard 地址'))
|
|
459
494
|
|
|
460
495
|
return {
|
|
461
496
|
file: CONFIG_FILE,
|
|
@@ -477,11 +512,15 @@ module.exports = {
|
|
|
477
512
|
get hint() {
|
|
478
513
|
return `Gateway 已启动,默认模型为 ${getConfiguredPrimaryModel() || OPENCLAW_DEFAULT_MODEL}`
|
|
479
514
|
},
|
|
480
|
-
get
|
|
481
|
-
|
|
515
|
+
get launchSteps() {
|
|
516
|
+
const port = getConfiguredGatewayPort()
|
|
517
|
+
return [
|
|
518
|
+
{ cmd: getLaunchCommand(port), note: '先启动 OpenClaw Gateway' },
|
|
519
|
+
{ cmd: getDashboardCommand(), note: '再生成/打开可直接连接的 Dashboard 地址(推荐)' },
|
|
520
|
+
]
|
|
482
521
|
},
|
|
483
522
|
get launchNote() {
|
|
484
|
-
return `🌐
|
|
523
|
+
return `🌐 推荐运行 ${getDashboardCommand()};Windows 上不要只打开裸 http://127.0.0.1:${getConfiguredGatewayPort()}/`
|
|
485
524
|
},
|
|
486
525
|
installCmd: 'npm install -g openclaw@latest',
|
|
487
526
|
docsUrl: 'https://docs.openclaw.ai',
|