@simonyea/holysheep-cli 1.7.38 → 1.7.40

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.7.38",
3
+ "version": "1.7.40",
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",
@@ -63,28 +63,47 @@ function killPortListeners(port) {
63
63
  return killed
64
64
  }
65
65
 
66
+ function waitPortFree(port, maxTries, intervalMs) {
67
+ for (let i = 0; i < maxTries; i++) {
68
+ if (openclawTool.getPortListeners(port).length === 0) return true
69
+ const t0 = Date.now()
70
+ while (Date.now() - t0 < intervalMs) {}
71
+ }
72
+ return openclawTool.getPortListeners(port).length === 0
73
+ }
74
+
75
+ // 在 kill gateway 之前调用:禁用 watchdog,bridge 不会因 gateway 短暂下线而自杀
76
+ function disableWatchdog() {
77
+ const { BRIDGE_CONFIG_FILE } = require('../tools/openclaw-bridge')
78
+ const fs = require('fs')
79
+ try {
80
+ const raw = JSON.parse(fs.readFileSync(BRIDGE_CONFIG_FILE, 'utf8'))
81
+ if (!raw.watchdog) raw.watchdog = {}
82
+ raw.watchdog.enabled = false
83
+ raw.gatewayPid = null // 清旧 PID,防止 PID 检查误判
84
+ fs.writeFileSync(BRIDGE_CONFIG_FILE, JSON.stringify(raw, null, 2), 'utf8')
85
+ } catch (e) {
86
+ console.log(chalk.yellow(` ⚠ 无法更新 Bridge 配置: ${e.message}`))
87
+ }
88
+ }
89
+
90
+ // 在 gateway 确认就绪后调用:重新启用 watchdog,写入真实 gatewayStartedAt
66
91
  function refreshGatewayStartedAt(gatewayPort) {
67
92
  const { BRIDGE_CONFIG_FILE } = require('../tools/openclaw-bridge')
68
93
  const fs = require('fs')
69
94
  try {
70
95
  const raw = JSON.parse(fs.readFileSync(BRIDGE_CONFIG_FILE, 'utf8'))
71
96
  raw.gatewayStartedAt = new Date().toISOString()
72
- raw.gatewayPort = gatewayPort
73
97
  raw.gatewayPid = null
98
+ raw.gatewayPort = gatewayPort
74
99
  raw.gatewayLaunchMode = 'direct-process'
100
+ if (!raw.watchdog) raw.watchdog = {}
101
+ raw.watchdog.startupGraceMs = 30000
102
+ raw.watchdog.enabled = true // gateway 已就绪,安全地重新启用 watchdog
75
103
  fs.writeFileSync(BRIDGE_CONFIG_FILE, JSON.stringify(raw, null, 2), 'utf8')
76
104
  } catch {}
77
105
  }
78
106
 
79
- function waitPortFree(port, maxTries, intervalMs) {
80
- for (let i = 0; i < maxTries; i++) {
81
- if (openclawTool.getPortListeners(port).length === 0) return true
82
- const t0 = Date.now()
83
- while (Date.now() - t0 < intervalMs) {}
84
- }
85
- return openclawTool.getPortListeners(port).length === 0
86
- }
87
-
88
107
  function findChrome() {
89
108
  const { existsSync } = require('fs')
90
109
  if (process.platform === 'darwin') {
@@ -148,35 +167,34 @@ async function openclaw() {
148
167
  const bridgePort = openclawTool.getBridgePort()
149
168
  const gatewayPort = openclawTool.getGatewayPort()
150
169
 
151
- // 先杀掉旧 Bridge,再启动新的
152
- const existingBridgeListeners = openclawTool.getPortListeners(bridgePort)
153
- if (existingBridgeListeners.length > 0) {
154
- process.stdout.write(chalk.gray(` 正在重启 Bridge (端口 ${bridgePort})... `))
155
- killPortListeners(bridgePort)
156
- waitPortFree(bridgePort, 10, 200)
170
+ // 先禁用 watchdog,防止后续 kill gateway 期间 bridge 误判自杀
171
+ disableWatchdog()
172
+
173
+ // Bridge:只确保运行,不重启
174
+ if (checkBridgeHealth(bridgePort)) {
175
+ console.log(chalk.green(`✓ Bridge 已运行 (端口 ${bridgePort})`))
157
176
  } else {
158
177
  process.stdout.write(chalk.gray(` 正在启动 Bridge (端口 ${bridgePort})... `))
159
- }
160
-
161
- const scriptPath = path.join(__dirname, '..', 'index.js')
162
- const spawnCmd = isWin ? 'node' : process.execPath
163
- const child = spawn(spawnCmd, [scriptPath, 'openclaw-bridge', '--port', String(bridgePort)], {
164
- detached: true,
165
- stdio: 'ignore',
166
- windowsHide: true,
167
- })
168
- child.unref()
178
+ const scriptPath = path.join(__dirname, '..', 'index.js')
179
+ const spawnCmd = isWin ? 'node' : process.execPath
180
+ const child = spawn(spawnCmd, [scriptPath, 'openclaw-bridge', '--port', String(bridgePort)], {
181
+ detached: true,
182
+ stdio: 'ignore',
183
+ windowsHide: true,
184
+ })
185
+ child.unref()
169
186
 
170
- const bridgeReady = waitForPort(() => checkBridgeHealth(bridgePort), 10, 1000)
171
- if (bridgeReady) {
172
- console.log(chalk.green('✓'))
173
- } else {
174
- console.log(chalk.red('✗'))
175
- console.log(chalk.red(` Bridge 启动失败,请手动运行: hs openclaw-bridge --port ${bridgePort}`))
176
- process.exit(1)
187
+ const bridgeReady = waitForPort(() => checkBridgeHealth(bridgePort), 10, 1000)
188
+ if (bridgeReady) {
189
+ console.log(chalk.green('✓'))
190
+ } else {
191
+ console.log(chalk.red('✗'))
192
+ console.log(chalk.red(` Bridge 启动失败,请手动运行: hs openclaw-bridge --port ${bridgePort}`))
193
+ process.exit(1)
194
+ }
177
195
  }
178
196
 
179
- // 先杀掉旧 Gateway,再启动新的(避免端口残留导致新进程无法绑定)
197
+ // Gateway:kill 旧的,直接 spawn 新的(明确 --port,避免 service 配置不一致)
180
198
  const existingGatewayListeners = openclawTool.getPortListeners(gatewayPort)
181
199
  if (existingGatewayListeners.length > 0) {
182
200
  process.stdout.write(chalk.gray(` 正在重启 Gateway (端口 ${gatewayPort})... `))
@@ -187,27 +205,17 @@ async function openclaw() {
187
205
  }
188
206
 
189
207
  const preferNpx = runtime.via === 'npx'
190
- const gatewayStartArgs = preferNpx ? ['openclaw', 'gateway', 'start'] : ['gateway', 'start']
191
- const gatewayStartCmd = preferNpx ? 'npx' : 'openclaw'
192
- const serviceResult = spawnSync(gatewayStartCmd, gatewayStartArgs, {
208
+ const gatewayArgs = preferNpx
209
+ ? ['openclaw', 'gateway', '--port', String(gatewayPort)]
210
+ : ['gateway', '--port', String(gatewayPort)]
211
+ const gatewayCmd = preferNpx ? 'npx' : 'openclaw'
212
+ const gatewayChild = spawn(gatewayCmd, gatewayArgs, {
213
+ detached: true,
193
214
  stdio: 'ignore',
194
- timeout: 10000,
195
215
  shell: isWin,
216
+ windowsHide: true,
196
217
  })
197
-
198
- if (serviceResult.status !== 0) {
199
- const directArgs = preferNpx
200
- ? ['openclaw', 'gateway', '--port', String(gatewayPort)]
201
- : ['gateway', '--port', String(gatewayPort)]
202
- const directCmd = preferNpx ? 'npx' : 'openclaw'
203
- const gatewayChild = spawn(directCmd, directArgs, {
204
- detached: true,
205
- stdio: 'ignore',
206
- shell: isWin,
207
- windowsHide: true,
208
- })
209
- gatewayChild.unref()
210
- }
218
+ gatewayChild.unref()
211
219
 
212
220
  const gatewayReady = waitForPort(() => checkGatewayHealth(gatewayPort), 12, 1500)
213
221
  if (gatewayReady) {
@@ -217,14 +225,13 @@ async function openclaw() {
217
225
  console.log(chalk.yellow(' Gateway 未就绪,仍将尝试打开 Dashboard'))
218
226
  }
219
227
 
220
- // 刷新 bridge config 中的 gatewayStartedAt,让 watchdog 宽限期从现在起算
228
+ // Gateway 就绪后重新启用 watchdog,写入真实 gatewayStartedAt
221
229
  refreshGatewayStartedAt(gatewayPort)
222
230
 
223
231
  // 获取 Dashboard URL
224
232
  const fallbackUrl = `http://127.0.0.1:${gatewayPort}/`
225
233
  let dashUrl = fallbackUrl
226
234
  try {
227
- const preferNpx = runtime.via === 'npx'
228
235
  const dashArgs = preferNpx ? ['openclaw', 'dashboard', '--no-open'] : ['dashboard', '--no-open']
229
236
  const dashCmd = preferNpx ? 'npx' : 'openclaw'
230
237
  const result = spawnSync(dashCmd, dashArgs, {