@simonyea/holysheep-cli 1.4.5 → 1.4.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "1.4.5",
3
+ "version": "1.4.6",
4
4
  "description": "一键配置所有 AI 编程工具接入 HolySheep API — Claude Code / Codex / Gemini CLI / OpenCode / OpenClaw / Aider / Cursor",
5
5
  "keywords": [
6
6
  "claude",
@@ -1,70 +1,42 @@
1
1
  /**
2
- * OpenClaw 适配器
2
+ * OpenClaw 适配器 (v2 — 基于实测的正确配置格式)
3
3
  *
4
- * 策略:直接写正确格式的 openclaw.json,然后 doctor --fix 迁移,
5
- * 最后用 wt/cmd 新窗口运行 gateway(Windows)或 detach(Unix)。
4
+ * 正确方案:custom-api-key provider,配置在 models.providers
5
+ * provider name 自动生成为 "custom-api-{hostname}"
6
+ * 模型引用格式: "custom-api-holysheep-ai/claude-sonnet-4-6"
6
7
  *
7
- * 文档: https://docs.openclaw.ai
8
+ * 必须的 onboard 参数:
9
+ * --accept-risk --auth-choice custom-api-key
10
+ * --custom-base-url --custom-api-key --custom-model-id --custom-compatibility anthropic
11
+ * --install-daemon
8
12
  */
9
- const fs = require('fs')
10
- const path = require('path')
11
- const os = require('os')
12
- const crypto = require('crypto')
13
+ const fs = require('fs')
14
+ const path = require('path')
15
+ const os = require('os')
13
16
  const { spawnSync, spawn, execSync } = require('child_process')
14
17
 
15
18
  const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw')
16
19
  const CONFIG_FILE = path.join(OPENCLAW_DIR, 'openclaw.json')
17
20
  const isWin = process.platform === 'win32'
18
21
 
22
+ /** 运行 openclaw CLI */
19
23
  function npx(...args) {
20
- // Windows 下始终用 npx 以绕过 PATH 问题
21
24
  return isWin
22
- ? spawnSync('npx', ['openclaw', ...args], { shell: true, timeout: 30000, stdio: 'pipe' })
23
- : spawnSync('openclaw', args, { shell: false, timeout: 30000, stdio: 'pipe' })
25
+ ? spawnSync('npx', ['openclaw', ...args], { shell: true, timeout: 30000, stdio: 'pipe' })
26
+ : spawnSync('openclaw', args, { shell: false, timeout: 30000, stdio: 'pipe' })
24
27
  }
25
28
 
26
29
  function readConfig() {
27
30
  try {
28
31
  if (fs.existsSync(CONFIG_FILE)) {
29
32
  const raw = fs.readFileSync(CONFIG_FILE, 'utf8')
33
+ // 去掉 JSON5 注释再解析
30
34
  return JSON.parse(raw.replace(/\/\/[^\n]*/g, '').replace(/\/\*[\s\S]*?\*\//g, ''))
31
35
  }
32
36
  } catch {}
33
37
  return {}
34
38
  }
35
39
 
36
- /** 写入正确格式的 openclaw.json */
37
- function writeCorrectConfig(apiKey, baseUrl) {
38
- fs.mkdirSync(OPENCLAW_DIR, { recursive: true })
39
-
40
- const token = crypto.randomBytes(24).toString('hex')
41
-
42
- // HolySheep 是 Anthropic-compatible API
43
- // 正确做法: env.ANTHROPIC_API_KEY + env.ANTHROPIC_BASE_URL
44
- // 模型用 anthropic/claude-sonnet-4-6
45
- // 参考: https://docs.openclaw.ai/providers/claude-max-api-proxy
46
- const config = {
47
- env: {
48
- ANTHROPIC_API_KEY: apiKey,
49
- ANTHROPIC_BASE_URL: baseUrl,
50
- },
51
- agents: {
52
- defaults: {
53
- model: { primary: 'anthropic/claude-sonnet-4-6' }
54
- }
55
- },
56
- gateway: {
57
- mode: 'local',
58
- port: 18789,
59
- bind: 'loopback',
60
- auth: { mode: 'token', token }
61
- },
62
- }
63
-
64
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8')
65
- return token
66
- }
67
-
68
40
  module.exports = {
69
41
  name: 'OpenClaw',
70
42
  id: 'openclaw',
@@ -85,46 +57,57 @@ module.exports = {
85
57
  return cfg.includes('holysheep.ai')
86
58
  },
87
59
 
88
- configure(apiKey, baseUrlAnthropicNoV1) {
60
+ configure(apiKey, baseUrl) {
89
61
  const chalk = require('chalk')
90
62
  console.log(chalk.gray('\n ⚙️ 正在配置 OpenClaw...'))
91
63
 
92
- // 1. 删除旧配置确保干净
64
+ // 1. 删除旧配置,确保 onboard 会重新写入
93
65
  try { fs.unlinkSync(CONFIG_FILE) } catch {}
94
- const authProfilePath = path.join(OPENCLAW_DIR, 'agents', 'main', 'agent', 'auth-profiles.json')
95
- try { fs.unlinkSync(authProfilePath) } catch {}
96
-
97
- // 2. 写配置文件(env + gateway)
98
- writeCorrectConfig(apiKey, baseUrlAnthropicNoV1)
99
66
 
100
- // 3. doctor --fix 处理兼容性问题
101
- npx('doctor', '--fix')
67
+ // 2. openclaw 官方 onboard 命令写入正确配置
68
+ // 这会生成完整的 models.providers.custom-api-holysheep-ai 配置
69
+ console.log(chalk.gray(' → 写入配置...'))
70
+ const result = npx(
71
+ 'onboard',
72
+ '--non-interactive',
73
+ '--accept-risk',
74
+ '--auth-choice', 'custom-api-key',
75
+ '--custom-base-url', baseUrl,
76
+ '--custom-api-key', apiKey,
77
+ '--custom-model-id', 'claude-sonnet-4-6',
78
+ '--custom-compatibility', 'anthropic',
79
+ '--install-daemon',
80
+ )
81
+
82
+ if (result.status !== 0) {
83
+ // onboard 失败时 fallback:手写最小化配置
84
+ console.log(chalk.yellow(' ⚠️ onboard 失败,使用备用配置...'))
85
+ _writeFallbackConfig(apiKey, baseUrl)
86
+ }
102
87
 
103
- // 读取写入的 token(用于生成带 token 的直接访问 URL)
104
- let savedToken = ''
88
+ // 3. 读取 token
89
+ let token = ''
105
90
  try {
106
- const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))
107
- savedToken = cfg?.gateway?.auth?.token || ''
91
+ token = readConfig()?.gateway?.auth?.token || ''
108
92
  } catch {}
109
93
 
110
- // 3. 启动 Gateway
94
+ // 4. 启动 Gateway
111
95
  console.log(chalk.gray(' → 正在启动 Gateway...'))
112
96
  const ok = _startGateway()
113
97
 
114
98
  if (ok) {
115
99
  console.log(chalk.green(' ✓ OpenClaw Gateway 已启动'))
116
100
  } else {
117
- console.log(chalk.yellow(' ⚠️ Gateway 正在启动,请稍等几秒...'))
101
+ console.log(chalk.yellow(' ⚠️ Gateway 启动中,稍等几秒后刷新浏览器'))
118
102
  }
119
103
 
120
- // 打印带 token 的直接访问 URL(无需手动填 token
121
- const dashUrl = savedToken
122
- ? `http://127.0.0.1:18789/?token=${savedToken}`
104
+ const dashUrl = token
105
+ ? `http://127.0.0.1:18789/?token=${token}`
123
106
  : 'http://127.0.0.1:18789/'
124
- console.log(chalk.cyan(`\n → 浏览器打开(已含 token,直接可用):`))
107
+ console.log(chalk.cyan('\n → 浏览器打开(含 token,直接可用):'))
125
108
  console.log(chalk.bold.cyan(` ${dashUrl}`))
126
109
 
127
- return { file: CONFIG_FILE, hot: false, _dashUrl: dashUrl }
110
+ return { file: CONFIG_FILE, hot: false }
128
111
  },
129
112
 
130
113
  reset() {
@@ -135,10 +118,8 @@ module.exports = {
135
118
  hint: 'Gateway 已启动,打开浏览器即可使用',
136
119
  launchCmd: null,
137
120
  get launchNote() {
138
- // 读取 token,生成带 token 的 URL
139
121
  try {
140
- const cfg = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))
141
- const token = cfg?.gateway?.auth?.token
122
+ const token = readConfig()?.gateway?.auth?.token
142
123
  if (token) return `🌐 浏览器打开(含 token): http://127.0.0.1:18789/?token=${token}`
143
124
  } catch {}
144
125
  return '🌐 打开浏览器: http://127.0.0.1:18789/'
@@ -147,38 +128,74 @@ module.exports = {
147
128
  docsUrl: 'https://docs.openclaw.ai',
148
129
  }
149
130
 
150
- /**
151
- * 启动 Gateway 后台进程
152
- * Windows: PowerShell Start-Process 开新的隐藏 PowerShell 窗口
153
- * Unix: detached child process
154
- */
131
+ /** onboard 失败时的备用配置(基于实测的正确格式) */
132
+ function _writeFallbackConfig(apiKey, baseUrl) {
133
+ const { randomBytes } = require('crypto')
134
+ fs.mkdirSync(OPENCLAW_DIR, { recursive: true })
135
+
136
+ const hostname = new URL(baseUrl).hostname.replace(/\./g, '-')
137
+ const providerName = `custom-api-${hostname}`
138
+ const token = randomBytes(24).toString('hex')
139
+
140
+ const config = {
141
+ models: {
142
+ mode: 'merge',
143
+ providers: {
144
+ [providerName]: {
145
+ baseUrl,
146
+ apiKey,
147
+ api: 'anthropic-messages',
148
+ models: [{
149
+ id: 'claude-sonnet-4-6',
150
+ name: 'claude-sonnet-4-6 (HolySheep)',
151
+ reasoning: false,
152
+ input: ['text'],
153
+ contextWindow: 200000,
154
+ maxTokens: 16000,
155
+ }],
156
+ }
157
+ }
158
+ },
159
+ agents: {
160
+ defaults: {
161
+ model: { primary: `${providerName}/claude-sonnet-4-6` }
162
+ }
163
+ },
164
+ gateway: {
165
+ mode: 'local',
166
+ port: 18789,
167
+ bind: 'loopback',
168
+ auth: { mode: 'token', token },
169
+ }
170
+ }
171
+
172
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8')
173
+ }
174
+
175
+ /** 启动 Gateway 后台进程 */
155
176
  function _startGateway() {
156
177
  if (isWin) {
157
- // Windows: 用 shell:true + start 弹出新窗口运行 gateway
158
178
  spawnSync('cmd /c start cmd /k "npx openclaw gateway --port 18789"', [], {
159
- shell: true,
160
- timeout: 5000,
161
- stdio: 'ignore',
179
+ shell: true, timeout: 5000, stdio: 'ignore',
162
180
  })
163
181
  } else {
164
182
  const child = spawn('openclaw', ['gateway', '--port', '18789'], {
165
- detached: true,
166
- stdio: 'ignore',
183
+ detached: true, stdio: 'ignore',
167
184
  })
168
185
  child.unref()
169
186
  }
170
187
 
171
- // 等待 gateway 启动(最多 8 秒)
188
+ // 等待最多 8
172
189
  for (let i = 0; i < 8; i++) {
173
190
  const t = Date.now(); while (Date.now() - t < 1000) {}
174
191
  try {
175
192
  execSync(
176
193
  isWin
177
- ? 'powershell -NonInteractive -Command "try { (Invoke-WebRequest -Uri http://127.0.0.1:18789/ -TimeoutSec 1 -UseBasicParsing).StatusCode } catch { exit 1 }"'
194
+ ? 'powershell -NonInteractive -Command "try{(Invoke-WebRequest -Uri http://127.0.0.1:18789/ -TimeoutSec 1 -UseBasicParsing).StatusCode}catch{exit 1}"'
178
195
  : 'curl -sf http://127.0.0.1:18789/ -o /dev/null --max-time 1',
179
196
  { stdio: 'ignore', timeout: 3000 }
180
197
  )
181
- return true // 成功响应
198
+ return true
182
199
  } catch {}
183
200
  }
184
201
  return false