@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 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.4",
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",
@@ -3,7 +3,7 @@
3
3
  * 配置文件: ~/.factory/settings.json
4
4
  *
5
5
  * 使用 Droid 原生 customModels 配置 HolySheep 的多个模型入口:
6
- * - GPT 走 OpenAI 兼容入口: https://api.holysheep.ai/openai
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: '/openai',
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 rootUrl = String(baseUrlAnthropic || '').replace(/\/+$/, '')
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: `${rootUrl}${item.baseUrlSuffix}`,
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
- return customModels.some((item) =>
112
- typeof item.baseUrl === 'string' && item.baseUrl.includes('api.holysheep.ai')
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, _baseUrlOpenAI, _primaryModel, selectedModels) {
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
- ...buildCustomModels(apiKey, baseUrlAnthropic, selectedModels),
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 模型列表',
@@ -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 = `http://127.0.0.1:${gatewayPort}/`
456
- console.log(chalk.cyan('\n → 浏览器打开(无需 token):'))
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 launchCmd() {
481
- return getLaunchCommand(getConfiguredGatewayPort())
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 `🌐 打开浏览器: http://127.0.0.1:${getConfiguredGatewayPort()}/`
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',