@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 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.4",
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",
@@ -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 模型列表',
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * OpenClaw 适配器 (v2 — 基于实测的正确配置格式)
3
3
  *
4
- * 正确方案:写入 HolySheep 的 OpenAI + Anthropic provider,
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 = `http://127.0.0.1:${gatewayPort}/`
456
- console.log(chalk.cyan('\n → 浏览器打开(无需 token):'))
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 launchCmd() {
481
- return getLaunchCommand(getConfiguredGatewayPort())
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 `🌐 打开浏览器: http://127.0.0.1:${getConfiguredGatewayPort()}/`
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',