@simonyea/holysheep-cli 1.2.1 → 1.2.3
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 +55 -10
- package/src/utils/shell.js +37 -5
package/package.json
CHANGED
package/src/tools/openclaw.js
CHANGED
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
* HolySheep 接入方式:通过 env.ANTHROPIC_API_KEY + env.ANTHROPIC_BASE_URL
|
|
12
12
|
* 设置 Anthropic provider 自定义 base URL 指向 HolySheep 中继
|
|
13
13
|
*/
|
|
14
|
-
const fs
|
|
15
|
-
const path
|
|
16
|
-
const os
|
|
14
|
+
const fs = require('fs')
|
|
15
|
+
const path = require('path')
|
|
16
|
+
const os = require('os')
|
|
17
|
+
const { spawnSync } = require('child_process')
|
|
17
18
|
|
|
18
19
|
const OPENCLAW_DIR = path.join(os.homedir(), '.openclaw')
|
|
19
20
|
const CONFIG_FILE = path.join(OPENCLAW_DIR, 'openclaw.json')
|
|
@@ -81,7 +82,12 @@ module.exports = {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
writeConfig(config)
|
|
84
|
-
|
|
85
|
+
|
|
86
|
+
// 自动启动 Gateway 后台服务(无需用户手动操作)
|
|
87
|
+
// 先尝试安装 daemon(系统服务),再 start;失败不阻断配置流程
|
|
88
|
+
_autoStartGateway()
|
|
89
|
+
|
|
90
|
+
return { file: CONFIG_FILE, hot: false, _gatewayStarted: true }
|
|
85
91
|
},
|
|
86
92
|
reset() {
|
|
87
93
|
const config = readConfig()
|
|
@@ -99,13 +105,52 @@ module.exports = {
|
|
|
99
105
|
writeConfig(config)
|
|
100
106
|
},
|
|
101
107
|
getConfigPath() { return CONFIG_FILE },
|
|
102
|
-
hint: '
|
|
108
|
+
hint: 'Gateway 已自动启动,浏览器打开 http://127.0.0.1:18789/ 即可使用',
|
|
103
109
|
launchCmd: null,
|
|
104
|
-
|
|
105
|
-
{ cmd: 'npx openclaw onboard', note: '首次初始化(设置模型、鉴权等)' },
|
|
106
|
-
{ cmd: 'npx openclaw gateway start', note: '启动后台 Gateway 服务' },
|
|
107
|
-
{ cmd: 'npx openclaw dashboard', note: '打开 WebUI → http://127.0.0.1:18789/' },
|
|
108
|
-
],
|
|
110
|
+
launchNote: '🌐 打开浏览器访问 http://127.0.0.1:18789/',
|
|
109
111
|
installCmd: 'npm install -g openclaw@latest',
|
|
110
112
|
docsUrl: 'https://docs.openclaw.ai',
|
|
111
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* 自动启动 OpenClaw Gateway 后台服务
|
|
117
|
+
* 1. 先尝试 `openclaw gateway start`(已有 daemon 或上次安装过)
|
|
118
|
+
* 2. 若失败,尝试 `openclaw onboard --install-daemon --yes`(无交互,自动安装系统服务)
|
|
119
|
+
* 3. 再 `openclaw gateway start`
|
|
120
|
+
*/
|
|
121
|
+
function _autoStartGateway() {
|
|
122
|
+
const chalk = require('chalk')
|
|
123
|
+
const bin = process.platform === 'win32' ? 'openclaw.cmd' : 'openclaw'
|
|
124
|
+
|
|
125
|
+
console.log(chalk.gray('\n ⚙️ 正在启动 OpenClaw Gateway...'))
|
|
126
|
+
|
|
127
|
+
// 先直接 start
|
|
128
|
+
const r1 = spawnSync(bin, ['gateway', 'start'], { shell: true, timeout: 15000 })
|
|
129
|
+
if (r1.status === 0) {
|
|
130
|
+
console.log(chalk.green(' ✓ OpenClaw Gateway 已在后台启动'))
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// start 失败 → 先 onboard --install-daemon --yes(无交互)再 start
|
|
135
|
+
console.log(chalk.gray(' → 首次运行,正在初始化服务(约 10 秒)...'))
|
|
136
|
+
spawnSync(bin, ['onboard', '--install-daemon', '--yes'], {
|
|
137
|
+
shell: true,
|
|
138
|
+
timeout: 60000,
|
|
139
|
+
stdio: 'ignore',
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const r2 = spawnSync(bin, ['gateway', 'start'], { shell: true, timeout: 15000 })
|
|
143
|
+
if (r2.status === 0) {
|
|
144
|
+
console.log(chalk.green(' ✓ OpenClaw Gateway 已在后台启动'))
|
|
145
|
+
} else {
|
|
146
|
+
// 仍然失败 → fallback:前台静默运行(detached)
|
|
147
|
+
const { spawn } = require('child_process')
|
|
148
|
+
const child = spawn(bin, ['gateway'], {
|
|
149
|
+
shell: true,
|
|
150
|
+
detached: true,
|
|
151
|
+
stdio: 'ignore',
|
|
152
|
+
})
|
|
153
|
+
child.unref()
|
|
154
|
+
console.log(chalk.green(' ✓ OpenClaw Gateway 已启动(前台守护模式)'))
|
|
155
|
+
}
|
|
156
|
+
}
|
package/src/utils/shell.js
CHANGED
|
@@ -41,6 +41,25 @@ function removeHsBlock(content) {
|
|
|
41
41
|
return content.replace(re, '')
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
/**
|
|
45
|
+
* 移除 rc 文件里用户手动写的同名 export/set -gx 行
|
|
46
|
+
* 防止旧值在 holysheep-cli managed 块之后覆盖新值
|
|
47
|
+
*/
|
|
48
|
+
function removeStaleExports(content, keys, isFish = false) {
|
|
49
|
+
let result = content
|
|
50
|
+
for (const key of keys) {
|
|
51
|
+
if (isFish) {
|
|
52
|
+
// fish: set -gx KEY "..." 或 set -gx KEY ...
|
|
53
|
+
result = result.replace(new RegExp(`\\n?set\\s+-gx\\s+${escapeRegex(key)}\\s+[^\\n]*\\n?`, 'g'), '\n')
|
|
54
|
+
} else {
|
|
55
|
+
// bash/zsh: export KEY="..." 或 export KEY=...
|
|
56
|
+
result = result.replace(new RegExp(`\\n?export\\s+${escapeRegex(key)}=[^\\n]*\\n?`, 'g'), '\n')
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 清理多余空行
|
|
60
|
+
return result.replace(/\n{3,}/g, '\n\n')
|
|
61
|
+
}
|
|
62
|
+
|
|
44
63
|
function buildEnvBlock(envVars, isFish = false) {
|
|
45
64
|
const lines = [MARKER_START]
|
|
46
65
|
for (const [k, v] of Object.entries(envVars)) {
|
|
@@ -75,8 +94,12 @@ function writeEnvToShell(envVars) {
|
|
|
75
94
|
for (const file of files) {
|
|
76
95
|
let content = ''
|
|
77
96
|
try { content = fs.readFileSync(file, 'utf8') } catch {}
|
|
78
|
-
content = removeHsBlock(content)
|
|
79
97
|
const isFish = file.endsWith('config.fish')
|
|
98
|
+
// 1. 清理旧的 holysheep managed 块
|
|
99
|
+
content = removeHsBlock(content)
|
|
100
|
+
// 2. 清理用户手动写的同名 export(防止旧值覆盖新值)
|
|
101
|
+
content = removeStaleExports(content, Object.keys(envVars), isFish)
|
|
102
|
+
// 3. 追加新的 managed 块
|
|
80
103
|
content += buildEnvBlock(envVars, isFish)
|
|
81
104
|
fs.writeFileSync(file, content, 'utf8')
|
|
82
105
|
written.push(file)
|
|
@@ -84,15 +107,24 @@ function writeEnvToShell(envVars) {
|
|
|
84
107
|
return written
|
|
85
108
|
}
|
|
86
109
|
|
|
87
|
-
function removeEnvFromShell() {
|
|
110
|
+
function removeEnvFromShell(extraKeys = []) {
|
|
111
|
+
// 默认清理的 key 列表(holysheep 相关的所有环境变量)
|
|
112
|
+
const HS_KEYS = [
|
|
113
|
+
'ANTHROPIC_AUTH_TOKEN', 'ANTHROPIC_API_KEY', 'ANTHROPIC_BASE_URL',
|
|
114
|
+
'OPENAI_API_KEY', 'OPENAI_BASE_URL',
|
|
115
|
+
'HOLYSHEEP_API_KEY',
|
|
116
|
+
...extraKeys,
|
|
117
|
+
]
|
|
88
118
|
const files = getShellRcFiles()
|
|
89
119
|
const cleaned = []
|
|
90
120
|
for (const file of files) {
|
|
91
121
|
if (!fs.existsSync(file)) continue
|
|
122
|
+
const isFish = file.endsWith('config.fish')
|
|
92
123
|
let content = fs.readFileSync(file, 'utf8')
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
124
|
+
let updated = removeHsBlock(content)
|
|
125
|
+
updated = removeStaleExports(updated, HS_KEYS, isFish)
|
|
126
|
+
if (updated !== content) {
|
|
127
|
+
fs.writeFileSync(file, updated, 'utf8')
|
|
96
128
|
cleaned.push(file)
|
|
97
129
|
}
|
|
98
130
|
}
|