@simonyea/holysheep-cli 1.2.3 → 1.2.5
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 +150 -75
package/package.json
CHANGED
package/src/tools/openclaw.js
CHANGED
|
@@ -8,22 +8,25 @@
|
|
|
8
8
|
* 配置文件: ~/.openclaw/openclaw.json (JSON5 格式)
|
|
9
9
|
* 文档: https://docs.openclaw.ai
|
|
10
10
|
*
|
|
11
|
-
* HolySheep
|
|
12
|
-
*
|
|
11
|
+
* HolySheep 接入方式:
|
|
12
|
+
* 1. 直接写入完整的 openclaw.json(含 auth profile + gateway + 默认模型)
|
|
13
|
+
* 2. 自动启动 Gateway,用户直接打开 http://127.0.0.1:18789/
|
|
14
|
+
* 不需要用户手动跑 onboard / gateway start
|
|
13
15
|
*/
|
|
14
|
-
const fs
|
|
15
|
-
const path
|
|
16
|
-
const os
|
|
16
|
+
const fs = require('fs')
|
|
17
|
+
const path = require('path')
|
|
18
|
+
const os = require('os')
|
|
19
|
+
const crypto = require('crypto')
|
|
17
20
|
const { spawnSync } = require('child_process')
|
|
18
21
|
|
|
19
|
-
const OPENCLAW_DIR
|
|
20
|
-
const CONFIG_FILE
|
|
22
|
+
const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw')
|
|
23
|
+
const CONFIG_FILE = path.join(OPENCLAW_DIR, 'openclaw.json')
|
|
21
24
|
|
|
22
25
|
function readConfig() {
|
|
23
26
|
try {
|
|
24
27
|
if (fs.existsSync(CONFIG_FILE)) {
|
|
25
|
-
// openclaw.json 是 JSON5 格式,先去掉注释再 parse
|
|
26
28
|
const raw = fs.readFileSync(CONFIG_FILE, 'utf8')
|
|
29
|
+
// openclaw.json 是 JSON5 格式,先去掉注释再 parse
|
|
27
30
|
return JSON.parse(raw.replace(/\/\/[^\n]*/g, '').replace(/\/\*[\s\S]*?\*\//g, ''))
|
|
28
31
|
}
|
|
29
32
|
} catch {}
|
|
@@ -35,122 +38,194 @@ function writeConfig(data) {
|
|
|
35
38
|
fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), 'utf8')
|
|
36
39
|
}
|
|
37
40
|
|
|
41
|
+
/** 生成随机 Gateway token */
|
|
42
|
+
function genToken() {
|
|
43
|
+
return crypto.randomBytes(24).toString('hex')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* 构建完整的初始化配置
|
|
48
|
+
* 包含:auth profile(HolySheep Anthropic-compatible)+ gateway + 默认模型 + holysheep provider
|
|
49
|
+
*/
|
|
50
|
+
function buildFullConfig(existing, apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI) {
|
|
51
|
+
const config = JSON.parse(JSON.stringify(existing || {})) // deep clone
|
|
52
|
+
|
|
53
|
+
// ── 1. 环境变量(兼容 Anthropic SDK 自动读取)──────────────────────
|
|
54
|
+
if (!config.env) config.env = {}
|
|
55
|
+
config.env.ANTHROPIC_API_KEY = apiKey
|
|
56
|
+
config.env.ANTHROPIC_BASE_URL = baseUrlAnthropicNoV1 // https://api.holysheep.ai
|
|
57
|
+
|
|
58
|
+
// ── 2. Auth profile(Custom Provider,Anthropic-compatible)────────
|
|
59
|
+
// openclaw 通过 auth profile 管理凭证;写入一个 holysheep profile
|
|
60
|
+
if (!config.auth) config.auth = {}
|
|
61
|
+
if (!config.auth.profiles) config.auth.profiles = {}
|
|
62
|
+
config.auth.profiles.holysheep = {
|
|
63
|
+
type: 'api-key',
|
|
64
|
+
provider: 'anthropic',
|
|
65
|
+
apiKey,
|
|
66
|
+
baseUrl: baseUrlAnthropicNoV1,
|
|
67
|
+
}
|
|
68
|
+
config.auth.default = 'holysheep'
|
|
69
|
+
|
|
70
|
+
// ── 3. 默认模型 ────────────────────────────────────────────────────
|
|
71
|
+
if (!config.agents) config.agents = {}
|
|
72
|
+
if (!config.agents.defaults) config.agents.defaults = {}
|
|
73
|
+
// 总是覆写为 HolySheep 最新 Sonnet(用户可以在 /model 命令里切换)
|
|
74
|
+
config.agents.defaults.model = {
|
|
75
|
+
primary: 'anthropic/claude-sonnet-4-5-20250929',
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── 4. 自定义 holysheep provider(OpenAI-compatible,支持所有模型)
|
|
79
|
+
if (!config.models) config.models = {}
|
|
80
|
+
config.models.mode = 'merge'
|
|
81
|
+
if (!config.models.providers) config.models.providers = {}
|
|
82
|
+
config.models.providers.holysheep = {
|
|
83
|
+
baseUrl: baseUrlOpenAI, // https://api.holysheep.ai/v1
|
|
84
|
+
apiKey,
|
|
85
|
+
api: 'openai-completions',
|
|
86
|
+
models: [
|
|
87
|
+
{ id: 'claude-sonnet-4-5-20250929', name: 'Claude Sonnet 4.5 (HolySheep)' },
|
|
88
|
+
{ id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4 (HolySheep)' },
|
|
89
|
+
{ id: 'claude-opus-4-5-20251101', name: 'Claude Opus 4.5 (HolySheep)' },
|
|
90
|
+
{ id: 'claude-opus-4-20250514', name: 'Claude Opus 4 (HolySheep)' },
|
|
91
|
+
{ id: 'gpt-4o', name: 'GPT-4o (HolySheep)' },
|
|
92
|
+
{ id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro (HolySheep)' },
|
|
93
|
+
],
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── 5. Gateway 配置(如未设置则初始化,保留已有配置)────────────────
|
|
97
|
+
if (!config.gateway) config.gateway = {}
|
|
98
|
+
if (!config.gateway.port) config.gateway.port = 18789
|
|
99
|
+
if (!config.gateway.bind) config.gateway.bind = '127.0.0.1'
|
|
100
|
+
// 生成 gateway token(若已有则不覆盖)
|
|
101
|
+
if (!config.gateway.auth) config.gateway.auth = {}
|
|
102
|
+
if (!config.gateway.auth.token) {
|
|
103
|
+
config.gateway.auth.token = genToken()
|
|
104
|
+
config.gateway.auth.mode = 'token'
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── 6. Workspace 默认路径 ─────────────────────────────────────────
|
|
108
|
+
if (!config.workspace) {
|
|
109
|
+
config.workspace = path.join(OPENCLAW_DIR, 'workspace')
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return config
|
|
113
|
+
}
|
|
114
|
+
|
|
38
115
|
module.exports = {
|
|
39
116
|
name: 'OpenClaw',
|
|
40
|
-
id:
|
|
117
|
+
id: 'openclaw',
|
|
118
|
+
|
|
41
119
|
checkInstalled() {
|
|
42
120
|
return require('../utils/which').commandExists('openclaw')
|
|
43
121
|
},
|
|
122
|
+
|
|
44
123
|
isConfigured() {
|
|
45
124
|
const c = readConfig()
|
|
46
125
|
return !!(
|
|
47
|
-
c.
|
|
48
|
-
c.
|
|
126
|
+
c.auth?.profiles?.holysheep ||
|
|
127
|
+
c.env?.ANTHROPIC_BASE_URL?.includes('holysheep')
|
|
49
128
|
)
|
|
50
129
|
},
|
|
51
|
-
configure(apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI) {
|
|
52
|
-
const config = readConfig()
|
|
53
|
-
|
|
54
|
-
// 设置环境变量 — Anthropic provider 使用 ANTHROPIC_BASE_URL 覆盖默认地址
|
|
55
|
-
if (!config.env) config.env = {}
|
|
56
|
-
config.env.ANTHROPIC_API_KEY = apiKey
|
|
57
|
-
config.env.ANTHROPIC_BASE_URL = baseUrlAnthropicNoV1 // https://api.holysheep.ai
|
|
58
130
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (!config.agents.defaults.model) {
|
|
63
|
-
config.agents.defaults.model = { primary: 'anthropic/claude-sonnet-4-5-20250929' }
|
|
64
|
-
}
|
|
131
|
+
configure(apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI) {
|
|
132
|
+
const existing = readConfig()
|
|
133
|
+
const config = buildFullConfig(existing, apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI)
|
|
65
134
|
|
|
66
|
-
//
|
|
67
|
-
|
|
68
|
-
config.models.mode = 'merge'
|
|
69
|
-
if (!config.models.providers) config.models.providers = {}
|
|
70
|
-
config.models.providers.holysheep = {
|
|
71
|
-
baseUrl: baseUrlOpenAI, // https://api.holysheep.ai/v1
|
|
72
|
-
apiKey,
|
|
73
|
-
api: 'openai-completions',
|
|
74
|
-
models: [
|
|
75
|
-
{ id: 'claude-sonnet-4-5-20250929', name: 'Claude Sonnet 4.5 (HolySheep)' },
|
|
76
|
-
{ id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4 (HolySheep)' },
|
|
77
|
-
{ id: 'claude-opus-4-5-20251101', name: 'Claude Opus 4.5 (HolySheep)' },
|
|
78
|
-
{ id: 'claude-opus-4-20250514', name: 'Claude Opus 4 (HolySheep)' },
|
|
79
|
-
{ id: 'gpt-4o', name: 'GPT-4o (HolySheep)' },
|
|
80
|
-
{ id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro (HolySheep)' },
|
|
81
|
-
],
|
|
82
|
-
}
|
|
135
|
+
// 确保 workspace 目录存在(openclaw 首次启动需要)
|
|
136
|
+
fs.mkdirSync(config.workspace, { recursive: true })
|
|
83
137
|
|
|
84
138
|
writeConfig(config)
|
|
85
139
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
_autoStartGateway()
|
|
140
|
+
// 自动初始化并启动 Gateway
|
|
141
|
+
_initAndStartGateway()
|
|
89
142
|
|
|
90
143
|
return { file: CONFIG_FILE, hot: false, _gatewayStarted: true }
|
|
91
144
|
},
|
|
145
|
+
|
|
92
146
|
reset() {
|
|
93
147
|
const config = readConfig()
|
|
94
148
|
if (config.env) {
|
|
95
149
|
delete config.env.ANTHROPIC_API_KEY
|
|
96
150
|
delete config.env.ANTHROPIC_BASE_URL
|
|
97
151
|
}
|
|
98
|
-
if (config.
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// 如果默认模型是 anthropic/xxx,清掉
|
|
152
|
+
if (config.auth?.profiles) delete config.auth.profiles.holysheep
|
|
153
|
+
if (config.auth?.default === 'holysheep') delete config.auth.default
|
|
154
|
+
if (config.models?.providers) delete config.models.providers.holysheep
|
|
102
155
|
if (config.agents?.defaults?.model?.primary?.startsWith('anthropic/')) {
|
|
103
156
|
delete config.agents.defaults.model
|
|
104
157
|
}
|
|
105
158
|
writeConfig(config)
|
|
106
159
|
},
|
|
160
|
+
|
|
107
161
|
getConfigPath() { return CONFIG_FILE },
|
|
108
|
-
hint:
|
|
109
|
-
launchCmd:
|
|
162
|
+
hint: 'Gateway 已自动在后台启动,打开浏览器即可使用',
|
|
163
|
+
launchCmd: null,
|
|
110
164
|
launchNote: '🌐 打开浏览器访问 http://127.0.0.1:18789/',
|
|
111
165
|
installCmd: 'npm install -g openclaw@latest',
|
|
112
|
-
docsUrl:
|
|
166
|
+
docsUrl: 'https://docs.openclaw.ai',
|
|
113
167
|
}
|
|
114
168
|
|
|
115
169
|
/**
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
170
|
+
* 自动初始化并启动 OpenClaw Gateway
|
|
171
|
+
*
|
|
172
|
+
* 策略:
|
|
173
|
+
* 1. openclaw onboard --non-interactive --install-daemon
|
|
174
|
+
* 读取已写好的 openclaw.json,无交互完成初始化 + 注册系统服务 + 启动 gateway
|
|
175
|
+
* 2. 若 onboard 失败(如 Windows 不支持 daemon),直接 gateway start
|
|
176
|
+
* 3. 仍失败 → Windows: start /B openclaw gateway;Unix: detached spawn
|
|
120
177
|
*/
|
|
121
|
-
function
|
|
178
|
+
function _initAndStartGateway() {
|
|
122
179
|
const chalk = require('chalk')
|
|
123
|
-
const
|
|
180
|
+
const isWin = process.platform === 'win32'
|
|
181
|
+
const bin = 'openclaw'
|
|
124
182
|
|
|
125
|
-
console.log(chalk.gray('\n ⚙️
|
|
183
|
+
console.log(chalk.gray('\n ⚙️ 正在初始化并启动 OpenClaw Gateway(约 15 秒)...'))
|
|
184
|
+
|
|
185
|
+
// Step 1: 无交互 onboard + install-daemon
|
|
186
|
+
const r1 = spawnSync(bin, ['onboard', '--non-interactive', '--install-daemon'], {
|
|
187
|
+
shell: true,
|
|
188
|
+
timeout: 60000,
|
|
189
|
+
stdio: 'pipe',
|
|
190
|
+
env: { ...process.env },
|
|
191
|
+
})
|
|
126
192
|
|
|
127
|
-
// 先直接 start
|
|
128
|
-
const r1 = spawnSync(bin, ['gateway', 'start'], { shell: true, timeout: 15000 })
|
|
129
193
|
if (r1.status === 0) {
|
|
130
|
-
console.log(chalk.green(' ✓ OpenClaw Gateway 已在后台启动'))
|
|
194
|
+
console.log(chalk.green(' ✓ OpenClaw 初始化完成,Gateway 已在后台启动'))
|
|
195
|
+
console.log(chalk.cyan(' → 浏览器打开: http://127.0.0.1:18789/'))
|
|
131
196
|
return
|
|
132
197
|
}
|
|
133
198
|
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
stdio: 'ignore',
|
|
199
|
+
// Step 2: onboard 失败,直接 gateway start
|
|
200
|
+
const r2 = spawnSync(bin, ['gateway', 'start'], {
|
|
201
|
+
shell: true,
|
|
202
|
+
timeout: 15000,
|
|
203
|
+
stdio: 'pipe',
|
|
140
204
|
})
|
|
141
|
-
|
|
142
|
-
const r2 = spawnSync(bin, ['gateway', 'start'], { shell: true, timeout: 15000 })
|
|
143
205
|
if (r2.status === 0) {
|
|
144
|
-
console.log(chalk.green(' ✓ OpenClaw Gateway
|
|
206
|
+
console.log(chalk.green(' ✓ OpenClaw Gateway 已启动'))
|
|
207
|
+
console.log(chalk.cyan(' → 浏览器打开: http://127.0.0.1:18789/'))
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Step 3: fallback — 后台守护进程
|
|
212
|
+
if (isWin) {
|
|
213
|
+
// Windows: 用 cmd start 开新窗口后台运行
|
|
214
|
+
spawnSync('cmd', ['/c', `start /B "" openclaw gateway --port 18789`], {
|
|
215
|
+
shell: true, stdio: 'ignore',
|
|
216
|
+
})
|
|
145
217
|
} else {
|
|
146
|
-
// 仍然失败 → fallback:前台静默运行(detached)
|
|
147
218
|
const { spawn } = require('child_process')
|
|
148
|
-
const child = spawn(bin, ['gateway'], {
|
|
149
|
-
shell: true,
|
|
219
|
+
const child = spawn(bin, ['gateway', '--port', '18789'], {
|
|
150
220
|
detached: true,
|
|
151
|
-
stdio:
|
|
221
|
+
stdio: 'ignore',
|
|
152
222
|
})
|
|
153
223
|
child.unref()
|
|
154
|
-
console.log(chalk.green(' ✓ OpenClaw Gateway 已启动(前台守护模式)'))
|
|
155
224
|
}
|
|
225
|
+
|
|
226
|
+
// 等 3 秒让 gateway 起来
|
|
227
|
+
const t = Date.now(); while (Date.now() - t < 3000) {}
|
|
228
|
+
|
|
229
|
+
console.log(chalk.green(' ✓ OpenClaw Gateway 已启动'))
|
|
230
|
+
console.log(chalk.cyan(' → 浏览器打开: http://127.0.0.1:18789/'))
|
|
156
231
|
}
|