@simonyea/holysheep-cli 1.6.4 → 1.6.6
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 +2 -0
- package/package.json +1 -1
- package/src/tools/droid.js +56 -17
- package/src/tools/openclaw.js +27 -5
package/README.md
CHANGED
|
@@ -218,6 +218,8 @@ A: OpenClaw 需要 Node.js 20+,运行 `node --version` 确认版本后重试
|
|
|
218
218
|
|
|
219
219
|
## Changelog
|
|
220
220
|
|
|
221
|
+
- **v1.6.6** — 修复 Droid CLI 的 GPT-5.4 配置残留问题,同时同步 `~/.factory/settings.json` 和 `~/.factory/config.json`,统一使用 `openai + https://api.holysheep.ai/v1`
|
|
222
|
+
- **v1.6.5** — 修复 HolySheep 对 Droid Responses API 的兼容
|
|
221
223
|
- **v1.6.4** — 修复 OpenClaw 的 npx 运行时检测,避免配置后页面仍卡在 Unauthorized / 未连接状态
|
|
222
224
|
- **v1.6.3** — OpenClaw 默认模型改为 GPT-5.4,并继续保留 Claude 模型切换能力
|
|
223
225
|
- **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.6",
|
|
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
|
@@ -189,6 +189,11 @@ function getLaunchCommand(port = getConfiguredGatewayPort()) {
|
|
|
189
189
|
return `${runtime} gateway --port ${port}`
|
|
190
190
|
}
|
|
191
191
|
|
|
192
|
+
function getDashboardCommand() {
|
|
193
|
+
const runtime = module.exports._lastRuntimeCommand || (hasOpenClawBinary() ? 'openclaw' : 'npx openclaw')
|
|
194
|
+
return `${runtime} dashboard --no-open`
|
|
195
|
+
}
|
|
196
|
+
|
|
192
197
|
function buildProviderName(baseUrl, prefix) {
|
|
193
198
|
const hostname = new URL(baseUrl).hostname.replace(/\./g, '-')
|
|
194
199
|
return `${prefix}-${hostname}`
|
|
@@ -362,6 +367,18 @@ function _startGateway(port, preferNpx = false, preferService = true) {
|
|
|
362
367
|
return false
|
|
363
368
|
}
|
|
364
369
|
|
|
370
|
+
function getDashboardUrl(port, preferNpx = false) {
|
|
371
|
+
const result = runOpenClaw(['dashboard', '--no-open'], {
|
|
372
|
+
preferNpx,
|
|
373
|
+
timeout: preferNpx ? 60000 : 20000,
|
|
374
|
+
})
|
|
375
|
+
if (result.status === 0) {
|
|
376
|
+
const match = String(result.stdout || '').match(/Dashboard URL:\s*(\S+)/)
|
|
377
|
+
if (match) return match[1]
|
|
378
|
+
}
|
|
379
|
+
return `http://127.0.0.1:${port}/`
|
|
380
|
+
}
|
|
381
|
+
|
|
365
382
|
module.exports = {
|
|
366
383
|
name: 'OpenClaw',
|
|
367
384
|
id: 'openclaw',
|
|
@@ -452,10 +469,11 @@ module.exports = {
|
|
|
452
469
|
console.log(chalk.yellow(' ⚠️ Gateway 启动中,稍等几秒后刷新浏览器'))
|
|
453
470
|
}
|
|
454
471
|
|
|
455
|
-
const dashUrl =
|
|
456
|
-
console.log(chalk.cyan('\n →
|
|
472
|
+
const dashUrl = getDashboardUrl(gatewayPort, runtime.via === 'npx')
|
|
473
|
+
console.log(chalk.cyan('\n → 浏览器打开(推荐使用此地址):'))
|
|
457
474
|
console.log(chalk.bold.cyan(` ${dashUrl}`))
|
|
458
475
|
console.log(chalk.gray(` 默认模型: ${OPENCLAW_DEFAULT_MODEL}`))
|
|
476
|
+
console.log(chalk.gray(' 如在 Windows 上打开裸 http://127.0.0.1:PORT/ 仍报 Unauthorized,请使用上面的 dashboard 地址'))
|
|
459
477
|
|
|
460
478
|
return {
|
|
461
479
|
file: CONFIG_FILE,
|
|
@@ -477,11 +495,15 @@ module.exports = {
|
|
|
477
495
|
get hint() {
|
|
478
496
|
return `Gateway 已启动,默认模型为 ${getConfiguredPrimaryModel() || OPENCLAW_DEFAULT_MODEL}`
|
|
479
497
|
},
|
|
480
|
-
get
|
|
481
|
-
|
|
498
|
+
get launchSteps() {
|
|
499
|
+
const port = getConfiguredGatewayPort()
|
|
500
|
+
return [
|
|
501
|
+
{ cmd: getLaunchCommand(port), note: '先启动 OpenClaw Gateway' },
|
|
502
|
+
{ cmd: getDashboardCommand(), note: '再生成/打开可直接连接的 Dashboard 地址(推荐)' },
|
|
503
|
+
]
|
|
482
504
|
},
|
|
483
505
|
get launchNote() {
|
|
484
|
-
return `🌐
|
|
506
|
+
return `🌐 推荐运行 ${getDashboardCommand()};Windows 上不要只打开裸 http://127.0.0.1:${getConfiguredGatewayPort()}/`
|
|
485
507
|
},
|
|
486
508
|
installCmd: 'npm install -g openclaw@latest',
|
|
487
509
|
docsUrl: 'https://docs.openclaw.ai',
|