@simonyea/holysheep-cli 1.2.3 → 1.2.4

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tools/openclaw.js +131 -77
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonyea/holysheep-cli",
3
- "version": "1.2.3",
3
+ "version": "1.2.4",
4
4
  "description": "一键配置所有 AI 编程工具接入 HolySheep API — Claude Code / Codex / Gemini CLI / OpenCode / OpenClaw / Aider / Cursor",
5
5
  "keywords": [
6
6
  "claude",
@@ -8,22 +8,25 @@
8
8
  * 配置文件: ~/.openclaw/openclaw.json (JSON5 格式)
9
9
  * 文档: https://docs.openclaw.ai
10
10
  *
11
- * HolySheep 接入方式:通过 env.ANTHROPIC_API_KEY + env.ANTHROPIC_BASE_URL
12
- * 设置 Anthropic provider 自定义 base URL 指向 HolySheep 中继
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 = require('fs')
15
- const path = require('path')
16
- const os = require('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 = path.join(os.homedir(), '.openclaw')
20
- const CONFIG_FILE = path.join(OPENCLAW_DIR, 'openclaw.json')
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,173 @@ 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-4-6(用户可以在 /model 命令里切换)
74
+ config.agents.defaults.model = {
75
+ primary: 'anthropic/claude-sonnet-4-6',
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-6', name: 'Claude Sonnet 4.6 (HolySheep)' },
88
+ { id: 'claude-sonnet-4-5-20250929', name: 'Claude Sonnet 4.5 (HolySheep)' },
89
+ { id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4 (HolySheep)' },
90
+ { id: 'claude-opus-4-5-20251101', name: 'Claude Opus 4.5 (HolySheep)' },
91
+ { id: 'claude-opus-4-20250514', name: 'Claude Opus 4 (HolySheep)' },
92
+ { id: 'gpt-4o', name: 'GPT-4o (HolySheep)' },
93
+ { id: 'gemini-2.5-pro', name: 'Gemini 2.5 Pro (HolySheep)' },
94
+ ],
95
+ }
96
+
97
+ // ── 5. Gateway 配置(如未设置则初始化,保留已有配置)────────────────
98
+ if (!config.gateway) config.gateway = {}
99
+ if (!config.gateway.port) config.gateway.port = 18789
100
+ if (!config.gateway.bind) config.gateway.bind = '127.0.0.1'
101
+ // 生成 gateway token(若已有则不覆盖)
102
+ if (!config.gateway.auth) config.gateway.auth = {}
103
+ if (!config.gateway.auth.token) {
104
+ config.gateway.auth.token = genToken()
105
+ config.gateway.auth.mode = 'token'
106
+ }
107
+
108
+ // ── 6. Workspace 默认路径 ─────────────────────────────────────────
109
+ if (!config.workspace) {
110
+ config.workspace = path.join(OPENCLAW_DIR, 'workspace')
111
+ }
112
+
113
+ return config
114
+ }
115
+
38
116
  module.exports = {
39
117
  name: 'OpenClaw',
40
- id: 'openclaw',
118
+ id: 'openclaw',
119
+
41
120
  checkInstalled() {
42
121
  return require('../utils/which').commandExists('openclaw')
43
122
  },
123
+
44
124
  isConfigured() {
45
125
  const c = readConfig()
46
126
  return !!(
47
- c.env?.ANTHROPIC_BASE_URL?.includes('holysheep') ||
48
- c.models?.providers?.holysheep
127
+ c.auth?.profiles?.holysheep ||
128
+ c.env?.ANTHROPIC_BASE_URL?.includes('holysheep')
49
129
  )
50
130
  },
51
- configure(apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI) {
52
- const config = readConfig()
53
131
 
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
-
59
- // 设置默认模型(如果未配置)
60
- if (!config.agents) config.agents = {}
61
- if (!config.agents.defaults) config.agents.defaults = {}
62
- if (!config.agents.defaults.model) {
63
- config.agents.defaults.model = { primary: 'anthropic/claude-sonnet-4-5-20250929' }
64
- }
132
+ configure(apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI) {
133
+ const existing = readConfig()
134
+ const config = buildFullConfig(existing, apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI)
65
135
 
66
- // 同时注册一个 holysheep 自定义 provider(支持所有模型)
67
- if (!config.models) config.models = {}
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
- }
136
+ // 确保 workspace 目录存在(openclaw 首次启动需要)
137
+ fs.mkdirSync(config.workspace, { recursive: true })
83
138
 
84
139
  writeConfig(config)
85
140
 
86
- // 自动启动 Gateway 后台服务(无需用户手动操作)
87
- // 先尝试安装 daemon(系统服务),再 start;失败不阻断配置流程
141
+ // 自动启动 Gateway,用户直接打开浏览器即可
88
142
  _autoStartGateway()
89
143
 
90
144
  return { file: CONFIG_FILE, hot: false, _gatewayStarted: true }
91
145
  },
146
+
92
147
  reset() {
93
148
  const config = readConfig()
94
149
  if (config.env) {
95
150
  delete config.env.ANTHROPIC_API_KEY
96
151
  delete config.env.ANTHROPIC_BASE_URL
97
152
  }
98
- if (config.models?.providers) {
99
- delete config.models.providers.holysheep
100
- }
101
- // 如果默认模型是 anthropic/xxx,清掉
153
+ if (config.auth?.profiles) delete config.auth.profiles.holysheep
154
+ if (config.auth?.default === 'holysheep') delete config.auth.default
155
+ if (config.models?.providers) delete config.models.providers.holysheep
102
156
  if (config.agents?.defaults?.model?.primary?.startsWith('anthropic/')) {
103
157
  delete config.agents.defaults.model
104
158
  }
105
159
  writeConfig(config)
106
160
  },
161
+
107
162
  getConfigPath() { return CONFIG_FILE },
108
- hint: 'Gateway 已自动启动,浏览器打开 http://127.0.0.1:18789/ 即可使用',
109
- launchCmd: null,
163
+ hint: 'Gateway 已自动在后台启动,打开浏览器即可使用',
164
+ launchCmd: null,
110
165
  launchNote: '🌐 打开浏览器访问 http://127.0.0.1:18789/',
111
166
  installCmd: 'npm install -g openclaw@latest',
112
- docsUrl: 'https://docs.openclaw.ai',
167
+ docsUrl: 'https://docs.openclaw.ai',
113
168
  }
114
169
 
115
170
  /**
116
- * 自动启动 OpenClaw Gateway 后台服务
117
- * 1. 先尝试 `openclaw gateway start`(已有 daemon 或上次安装过)
118
- * 2. 若失败,尝试 `openclaw onboard --install-daemon --yes`(无交互,自动安装系统服务)
119
- * 3. 再 `openclaw gateway start`
171
+ * 自动在后台启动 OpenClaw Gateway
172
+ *
173
+ * 策略(按顺序尝试):
174
+ * 1. openclaw gateway start — 已有 daemon/service 直接启动
175
+ * 2. openclaw gateway --port 18789 (detached) — 前台守护模式
176
+ *
177
+ * 不调用 onboard,因为配置文件已由 configure() 完整写入。
120
178
  */
121
179
  function _autoStartGateway() {
122
180
  const chalk = require('chalk')
123
- const bin = process.platform === 'win32' ? 'openclaw.cmd' : 'openclaw'
181
+ const bin = 'openclaw'
124
182
 
125
183
  console.log(chalk.gray('\n ⚙️ 正在启动 OpenClaw Gateway...'))
126
184
 
127
- // 先直接 start
128
- const r1 = spawnSync(bin, ['gateway', 'start'], { shell: true, timeout: 15000 })
185
+ // 先尝试 gateway start(若已注册为系统服务则直接生效)
186
+ const r1 = spawnSync(bin, ['gateway', 'start'], {
187
+ shell: true,
188
+ timeout: 15000,
189
+ stdio: 'pipe',
190
+ })
129
191
  if (r1.status === 0) {
130
192
  console.log(chalk.green(' ✓ OpenClaw Gateway 已在后台启动'))
131
193
  return
132
194
  }
133
195
 
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',
196
+ // 没有 daemon直接 detached 前台守护运行
197
+ const { spawn } = require('child_process')
198
+ const child = spawn(bin, ['gateway', '--port', '18789'], {
199
+ shell: true,
200
+ detached: true,
201
+ stdio: 'ignore',
140
202
  })
203
+ child.unref()
141
204
 
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
- }
205
+ // 2 秒让 gateway 起来
206
+ spawnSync('node', ['-e', 'setTimeout(()=>{},2000)'], { timeout: 3000 })
207
+
208
+ console.log(chalk.green(' ✓ OpenClaw Gateway 已启动'))
209
+ console.log(chalk.cyan(' → 浏览器打开: http://127.0.0.1:18789/'))
156
210
  }