@simonyea/holysheep-cli 1.3.0 → 1.3.2
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 +1 -1
- package/src/tools/openclaw.js +88 -128
package/package.json
CHANGED
package/src/tools/openclaw.js
CHANGED
|
@@ -1,45 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenClaw 适配器
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 策略:直接写正确格式的 openclaw.json,然后 doctor --fix 迁移,
|
|
5
|
+
* 最后用 wt/cmd 新窗口运行 gateway(Windows)或 detach(Unix)。
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
* openclaw onboard \
|
|
8
|
-
* --non-interactive \
|
|
9
|
-
* --auth-choice custom-api-key \
|
|
10
|
-
* --custom-base-url https://api.holysheep.ai \
|
|
11
|
-
* --custom-api-key <key> \
|
|
12
|
-
* --custom-model-id claude-sonnet-4-6 \
|
|
13
|
-
* --custom-compatibility anthropic \
|
|
14
|
-
* --install-daemon
|
|
15
|
-
*
|
|
16
|
-
* 文档: https://docs.openclaw.ai/start/wizard-cli-reference
|
|
7
|
+
* 文档: https://docs.openclaw.ai
|
|
17
8
|
*/
|
|
18
9
|
const fs = require('fs')
|
|
19
10
|
const path = require('path')
|
|
20
11
|
const os = require('os')
|
|
21
|
-
const
|
|
12
|
+
const crypto = require('crypto')
|
|
13
|
+
const { spawnSync, spawn, execSync } = require('child_process')
|
|
22
14
|
|
|
23
15
|
const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw')
|
|
24
16
|
const CONFIG_FILE = path.join(OPENCLAW_DIR, 'openclaw.json')
|
|
17
|
+
const isWin = process.platform === 'win32'
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
return { cmd: 'openclaw', args }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function run(args, opts = {}) {
|
|
36
|
-
const { cmd, args: fullArgs } = bin(...args)
|
|
37
|
-
return spawnSync(cmd, fullArgs, {
|
|
38
|
-
shell: true,
|
|
39
|
-
timeout: opts.timeout || 30000,
|
|
40
|
-
stdio: opts.stdio || 'pipe',
|
|
41
|
-
env: { ...process.env, ...(opts.env || {}) },
|
|
42
|
-
})
|
|
19
|
+
function npx(...args) {
|
|
20
|
+
// Windows 下始终用 npx 以绕过 PATH 问题
|
|
21
|
+
return isWin
|
|
22
|
+
? spawnSync('npx', ['openclaw', ...args], { shell: true, timeout: 30000, stdio: 'pipe' })
|
|
23
|
+
: spawnSync('openclaw', args, { shell: false, timeout: 30000, stdio: 'pipe' })
|
|
43
24
|
}
|
|
44
25
|
|
|
45
26
|
function readConfig() {
|
|
@@ -52,20 +33,50 @@ function readConfig() {
|
|
|
52
33
|
return {}
|
|
53
34
|
}
|
|
54
35
|
|
|
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
|
+
// 完全正确的格式,基于 openclaw 2026.3.x 实测
|
|
43
|
+
const config = {
|
|
44
|
+
agents: {
|
|
45
|
+
defaults: {
|
|
46
|
+
model: { primary: 'custom/claude-sonnet-4-6' }
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
gateway: {
|
|
50
|
+
port: 18789,
|
|
51
|
+
bind: 'loopback', // 新格式,不用 "127.0.0.1"
|
|
52
|
+
auth: {
|
|
53
|
+
mode: 'token',
|
|
54
|
+
token,
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
// Custom provider — OpenAI-compatible
|
|
58
|
+
providers: {
|
|
59
|
+
custom: {
|
|
60
|
+
baseUrl, // https://api.holysheep.ai
|
|
61
|
+
apiKey,
|
|
62
|
+
compatibility: 'anthropic',
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf8')
|
|
68
|
+
return token
|
|
69
|
+
}
|
|
70
|
+
|
|
55
71
|
module.exports = {
|
|
56
72
|
name: 'OpenClaw',
|
|
57
73
|
id: 'openclaw',
|
|
58
74
|
|
|
59
75
|
checkInstalled() {
|
|
60
|
-
// 先检测命令是否在 PATH 里
|
|
61
76
|
if (require('../utils/which').commandExists('openclaw')) return true
|
|
62
|
-
|
|
63
|
-
if (process.platform === 'win32') {
|
|
77
|
+
if (isWin) {
|
|
64
78
|
try {
|
|
65
|
-
|
|
66
|
-
'npx --yes openclaw --version',
|
|
67
|
-
{ stdio: 'ignore', timeout: 15000, shell: true }
|
|
68
|
-
)
|
|
79
|
+
execSync('npx openclaw --version', { stdio: 'ignore', timeout: 15000, shell: true })
|
|
69
80
|
return true
|
|
70
81
|
} catch {}
|
|
71
82
|
}
|
|
@@ -73,81 +84,45 @@ module.exports = {
|
|
|
73
84
|
},
|
|
74
85
|
|
|
75
86
|
isConfigured() {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
const cfg = JSON.stringify(c)
|
|
79
|
-
return cfg.includes('holysheep.ai') || cfg.includes('holysheep')
|
|
87
|
+
const cfg = JSON.stringify(readConfig())
|
|
88
|
+
return cfg.includes('holysheep.ai')
|
|
80
89
|
},
|
|
81
90
|
|
|
82
91
|
configure(apiKey, baseUrlAnthropicNoV1) {
|
|
83
92
|
const chalk = require('chalk')
|
|
93
|
+
console.log(chalk.gray('\n ⚙️ 正在配置 OpenClaw...'))
|
|
84
94
|
|
|
85
|
-
|
|
95
|
+
// 1. 写入正确格式配置
|
|
96
|
+
writeCorrectConfig(apiKey, baseUrlAnthropicNoV1)
|
|
86
97
|
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
run(['doctor', '--fix'], { stdio: 'ignore', timeout: 15000 })
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// Step 2: 用官方 onboard 非交互式命令完成配置 + 安装系统服务
|
|
93
|
-
const r = run([
|
|
94
|
-
'onboard',
|
|
95
|
-
'--non-interactive',
|
|
96
|
-
'--auth-choice', 'custom-api-key',
|
|
97
|
-
'--custom-base-url', baseUrlAnthropicNoV1, // https://api.holysheep.ai
|
|
98
|
-
'--custom-api-key', apiKey,
|
|
99
|
-
'--custom-model-id', 'claude-sonnet-4-6',
|
|
100
|
-
'--custom-compatibility', 'anthropic',
|
|
101
|
-
'--install-daemon',
|
|
102
|
-
], {
|
|
103
|
-
timeout: 90000,
|
|
104
|
-
stdio: 'pipe',
|
|
105
|
-
env: {
|
|
106
|
-
CUSTOM_API_KEY: apiKey,
|
|
107
|
-
ANTHROPIC_API_KEY: apiKey,
|
|
108
|
-
ANTHROPIC_BASE_URL: baseUrlAnthropicNoV1,
|
|
109
|
-
},
|
|
110
|
-
})
|
|
98
|
+
// 2. doctor --fix 修复任何兼容性问题
|
|
99
|
+
npx('doctor', '--fix')
|
|
111
100
|
|
|
112
|
-
|
|
113
|
-
|
|
101
|
+
// 3. 启动 Gateway
|
|
102
|
+
console.log(chalk.gray(' → 正在启动 Gateway...'))
|
|
103
|
+
const ok = _startGateway()
|
|
114
104
|
|
|
115
|
-
if (
|
|
116
|
-
console.log(chalk.green(' ✓ OpenClaw
|
|
105
|
+
if (ok) {
|
|
106
|
+
console.log(chalk.green(' ✓ OpenClaw Gateway 已启动'))
|
|
117
107
|
console.log(chalk.cyan(' → 浏览器打开: http://127.0.0.1:18789/'))
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
// onboard 失败,尝试直接启动 gateway
|
|
122
|
-
console.log(chalk.gray(' → 配置完成,正在启动 Gateway...'))
|
|
123
|
-
const started = _startGateway()
|
|
124
|
-
|
|
125
|
-
if (!started) {
|
|
126
|
-
// 给用户明确的手动命令
|
|
127
|
-
console.log(chalk.yellow('\n ⚠️ Gateway 需要手动启动,运行以下命令:'))
|
|
128
|
-
if (process.platform === 'win32') {
|
|
129
|
-
console.log(chalk.cyan(' npx openclaw gateway install'))
|
|
130
|
-
console.log(chalk.cyan(' npx openclaw gateway start'))
|
|
131
|
-
} else {
|
|
132
|
-
console.log(chalk.cyan(' openclaw gateway install'))
|
|
133
|
-
console.log(chalk.cyan(' openclaw gateway start'))
|
|
134
|
-
}
|
|
108
|
+
} else {
|
|
109
|
+
console.log(chalk.yellow(' ⚠️ 请手动启动 Gateway:'))
|
|
110
|
+
console.log(chalk.cyan(isWin ? ' npx openclaw gateway' : ' openclaw gateway'))
|
|
135
111
|
}
|
|
136
112
|
|
|
137
113
|
return { file: CONFIG_FILE, hot: false }
|
|
138
114
|
},
|
|
139
115
|
|
|
140
116
|
reset() {
|
|
141
|
-
|
|
117
|
+
try { fs.unlinkSync(CONFIG_FILE) } catch {}
|
|
142
118
|
},
|
|
143
119
|
|
|
144
120
|
getConfigPath() { return CONFIG_FILE },
|
|
145
|
-
hint:
|
|
146
|
-
launchCmd:
|
|
121
|
+
hint: 'Gateway 已启动,打开浏览器即可使用',
|
|
122
|
+
launchCmd: null,
|
|
147
123
|
get launchNote() {
|
|
148
|
-
const isWin = process.platform === 'win32'
|
|
149
124
|
return isWin
|
|
150
|
-
? '🌐 打开浏览器: http://127.0.0.1:18789/\n 如无法访问: npx openclaw gateway
|
|
125
|
+
? '🌐 打开浏览器: http://127.0.0.1:18789/\n 如无法访问: npx openclaw gateway'
|
|
151
126
|
: '🌐 打开浏览器: http://127.0.0.1:18789/'
|
|
152
127
|
},
|
|
153
128
|
installCmd: 'npm install -g openclaw@latest',
|
|
@@ -155,52 +130,37 @@ module.exports = {
|
|
|
155
130
|
}
|
|
156
131
|
|
|
157
132
|
/**
|
|
158
|
-
*
|
|
133
|
+
* 启动 Gateway 后台进程
|
|
134
|
+
* Windows: PowerShell Start-Process 开新的隐藏 PowerShell 窗口
|
|
135
|
+
* Unix: detached child process
|
|
159
136
|
*/
|
|
160
137
|
function _startGateway() {
|
|
161
|
-
const chalk = require('chalk')
|
|
162
|
-
const isWin = process.platform === 'win32'
|
|
163
|
-
|
|
164
|
-
// 先 install,再 start
|
|
165
|
-
run(['gateway', 'install'], { stdio: 'ignore', timeout: 20000 })
|
|
166
|
-
|
|
167
|
-
const r = run(['gateway', 'start'], { timeout: 10000 })
|
|
168
|
-
if (r.status === 0) {
|
|
169
|
-
console.log(chalk.green(' ✓ OpenClaw Gateway 已启动'))
|
|
170
|
-
console.log(chalk.cyan(' → 浏览器打开: http://127.0.0.1:18789/'))
|
|
171
|
-
return true
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// fallback: 直接运行 gateway 进程(Windows 用 Start-Process 后台)
|
|
175
138
|
if (isWin) {
|
|
139
|
+
// 用 Start-Process 开一个后台 powershell 窗口运行 npx openclaw gateway
|
|
176
140
|
spawnSync('powershell', [
|
|
177
141
|
'-NonInteractive', '-Command',
|
|
178
|
-
`Start-Process -FilePath
|
|
179
|
-
], { shell: false, timeout:
|
|
142
|
+
`Start-Process -FilePath powershell -ArgumentList @('-NonInteractive','-WindowStyle','Hidden','-Command','npx openclaw gateway --port 18789') -WindowStyle Hidden -PassThru | Out-Null`
|
|
143
|
+
], { shell: false, timeout: 10000, stdio: 'ignore' })
|
|
180
144
|
} else {
|
|
181
|
-
const { spawn } = require('child_process')
|
|
182
145
|
const child = spawn('openclaw', ['gateway', '--port', '18789'], {
|
|
183
|
-
detached: true,
|
|
146
|
+
detached: true,
|
|
147
|
+
stdio: 'ignore',
|
|
184
148
|
})
|
|
185
149
|
child.unref()
|
|
186
150
|
}
|
|
187
151
|
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
console.log(chalk.green(' ✓ OpenClaw Gateway 已启动'))
|
|
201
|
-
console.log(chalk.cyan(' → 浏览器打开: http://127.0.0.1:18789/'))
|
|
202
|
-
return true
|
|
203
|
-
} catch {
|
|
204
|
-
return false
|
|
152
|
+
// 等待 gateway 启动(最多 8 秒)
|
|
153
|
+
for (let i = 0; i < 8; i++) {
|
|
154
|
+
const t = Date.now(); while (Date.now() - t < 1000) {}
|
|
155
|
+
try {
|
|
156
|
+
execSync(
|
|
157
|
+
isWin
|
|
158
|
+
? 'powershell -NonInteractive -Command "try { (Invoke-WebRequest -Uri http://127.0.0.1:18789/ -TimeoutSec 1 -UseBasicParsing).StatusCode } catch { exit 1 }"'
|
|
159
|
+
: 'curl -sf http://127.0.0.1:18789/ -o /dev/null --max-time 1',
|
|
160
|
+
{ stdio: 'ignore', timeout: 3000 }
|
|
161
|
+
)
|
|
162
|
+
return true // 成功响应
|
|
163
|
+
} catch {}
|
|
205
164
|
}
|
|
165
|
+
return false
|
|
206
166
|
}
|