@simonyea/holysheep-cli 2.0.1 → 2.0.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/README.md +1 -0
- package/package.json +1 -1
- package/src/commands/webui.js +16 -3
- package/src/webui/aionui-runtime.js +9 -2
- package/tests/droid.test.js +0 -75
- package/tests/workspace-store.test.js +0 -57
package/README.md
CHANGED
|
@@ -225,6 +225,7 @@ A: OpenClaw 需要 Node.js 20+,运行 `node --version` 确认版本后重试
|
|
|
225
225
|
|
|
226
226
|
## Changelog
|
|
227
227
|
|
|
228
|
+
- **v2.0.2** — `hs web` 现在内置 AionUi runtime(含 darwin-arm64 bun 运行时),不再依赖开发机上的 `/Users/.../AionUi` 源码目录;npm 安装后可直接起 HolySheep 登录版 AionUi
|
|
228
229
|
- **v2.0.1** — `hs web` 默认切到 HolySheep 登录版 AionUi runtime;本机检测到 AionUi + bun 时直接接管 WebUI,起不来才回退旧版 shell
|
|
229
230
|
- **v2.0.0** — 修复 `hs web` 的关键可用性问题:工具探测改为异步缓存,避免 WebUI 首屏请求卡死;workspace 对话上游请求增加超时保护,防止发送消息时接口悬挂
|
|
230
231
|
- **v1.7.135** — Droid CLI 的 GPT-5.4 配置切回官方要求的 `provider=openai + https://api.holysheep.ai/v1`;同时服务端兼容桥接 `gpt-5.4` 的 `/responses` 请求到 `/v1/chat/completions`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonyea/holysheep-cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
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
|
"scripts": {
|
|
6
6
|
"test": "node tests/droid.test.js && node tests/workspace-store.test.js"
|
package/src/commands/webui.js
CHANGED
|
@@ -6,6 +6,19 @@
|
|
|
6
6
|
const chalk = require('chalk')
|
|
7
7
|
const { execSync } = require('child_process')
|
|
8
8
|
|
|
9
|
+
// AionUi runtime is opt-in. The bundled vendor/ directory is stripped from
|
|
10
|
+
// the npm package (see .npmignore) because it only works on darwin-arm64 and
|
|
11
|
+
// bloated 2.0.2 to 160MB. Developers who keep vendor/ around locally can
|
|
12
|
+
// still run AionUi via HOLYSHEEP_WEBUI_AIONUI=1. The legacy WebUI is the
|
|
13
|
+
// default for everyone else.
|
|
14
|
+
function shouldTryAionUi() {
|
|
15
|
+
if (process.env.HOLYSHEEP_WEBUI_AIONUI === '1') return true
|
|
16
|
+
// Back-compat: HOLYSHEEP_WEBUI_LEGACY used to be the only way to force
|
|
17
|
+
// legacy. It now has no effect (legacy is already the default) but we
|
|
18
|
+
// intentionally don't crash if someone still sets it.
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
|
|
9
22
|
async function webui(opts) {
|
|
10
23
|
const port = Number(opts.port) || 9876
|
|
11
24
|
|
|
@@ -18,15 +31,15 @@ async function webui(opts) {
|
|
|
18
31
|
let child = null
|
|
19
32
|
let mode = 'legacy'
|
|
20
33
|
|
|
21
|
-
if (
|
|
34
|
+
if (shouldTryAionUi()) {
|
|
22
35
|
try {
|
|
23
36
|
const { startAionUiRuntime } = require('../webui/aionui-runtime')
|
|
24
37
|
const result = await startAionUiRuntime(port)
|
|
25
38
|
child = result.child
|
|
26
39
|
mode = 'aionui'
|
|
27
|
-
console.log(chalk.green(
|
|
40
|
+
console.log(chalk.green('✓ AionUi runtime 已接管 hs web'))
|
|
28
41
|
} catch (error) {
|
|
29
|
-
console.log(chalk.yellow(`!
|
|
42
|
+
console.log(chalk.yellow(`! AionUi runtime 启动失败,回退旧版 WebUI: ${error.message}`))
|
|
30
43
|
}
|
|
31
44
|
}
|
|
32
45
|
|
|
@@ -7,6 +7,10 @@ const http = require('http')
|
|
|
7
7
|
const { execSync, spawn } = require('child_process')
|
|
8
8
|
|
|
9
9
|
function resolveBunPath() {
|
|
10
|
+
const bundledBun = path.join(__dirname, 'vendor', 'bun-darwin-arm64')
|
|
11
|
+
if (process.platform === 'darwin' && process.arch === 'arm64' && fs.existsSync(bundledBun)) {
|
|
12
|
+
return bundledBun
|
|
13
|
+
}
|
|
10
14
|
if (process.env.BUN && fs.existsSync(process.env.BUN)) return process.env.BUN
|
|
11
15
|
try {
|
|
12
16
|
const resolved = execSync('which bun', {
|
|
@@ -23,6 +27,7 @@ function resolveBunPath() {
|
|
|
23
27
|
function getRuntimeCandidates() {
|
|
24
28
|
const home = os.homedir()
|
|
25
29
|
return [
|
|
30
|
+
path.join(__dirname, 'vendor', 'aionui'),
|
|
26
31
|
process.env.HOLYSHEEP_AIONUI_DIR,
|
|
27
32
|
path.join(home, 'AionUi'),
|
|
28
33
|
path.join(home, 'Projects', 'AionUi'),
|
|
@@ -81,12 +86,14 @@ function waitForReady(port, timeoutMs = 15000) {
|
|
|
81
86
|
async function startAionUiRuntime(port) {
|
|
82
87
|
const runtimeDir = resolveAionUiRuntimeDir()
|
|
83
88
|
if (!runtimeDir) {
|
|
84
|
-
throw new Error(
|
|
89
|
+
throw new Error(
|
|
90
|
+
'AionUi runtime not bundled in this build — set HOLYSHEEP_WEBUI_AIONUI=1 with a local vendor/ directory to enable'
|
|
91
|
+
)
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
const bunPath = resolveBunPath()
|
|
88
95
|
if (!bunPath) {
|
|
89
|
-
throw new Error('bun is required to start the AionUi runtime')
|
|
96
|
+
throw new Error('bun is required to start the AionUi runtime (install: https://bun.sh)')
|
|
90
97
|
}
|
|
91
98
|
|
|
92
99
|
const child = spawn(bunPath, ['dist-server/server.mjs'], {
|
package/tests/droid.test.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
const assert = require('assert')
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const os = require('os')
|
|
4
|
-
const path = require('path')
|
|
5
|
-
|
|
6
|
-
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'hs-droid-test-'))
|
|
7
|
-
process.env.HOME = tempHome
|
|
8
|
-
|
|
9
|
-
const droid = require('../src/tools/droid')
|
|
10
|
-
|
|
11
|
-
const factoryDir = path.join(tempHome, '.factory')
|
|
12
|
-
const settingsFile = path.join(factoryDir, 'settings.json')
|
|
13
|
-
const configFile = path.join(factoryDir, 'config.json')
|
|
14
|
-
|
|
15
|
-
fs.mkdirSync(factoryDir, { recursive: true })
|
|
16
|
-
fs.writeFileSync(settingsFile, JSON.stringify({
|
|
17
|
-
customModels: [
|
|
18
|
-
{
|
|
19
|
-
model: 'gpt-5.4',
|
|
20
|
-
id: 'custom:gpt-5.4-0',
|
|
21
|
-
baseUrl: 'https://api.holysheep.ai',
|
|
22
|
-
provider: 'anthropic',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
model: 'local-test',
|
|
26
|
-
id: 'custom:local-test',
|
|
27
|
-
baseUrl: 'http://localhost:11434/v1',
|
|
28
|
-
provider: 'generic-chat-completion-api',
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
}, null, 2))
|
|
32
|
-
fs.writeFileSync(configFile, JSON.stringify({
|
|
33
|
-
customModels: [
|
|
34
|
-
{
|
|
35
|
-
model: 'gpt-5.4',
|
|
36
|
-
id: 'custom:gpt-5.4-0',
|
|
37
|
-
baseUrl: 'https://api.holysheep.ai',
|
|
38
|
-
provider: 'anthropic',
|
|
39
|
-
},
|
|
40
|
-
],
|
|
41
|
-
}, null, 2))
|
|
42
|
-
|
|
43
|
-
droid.configure(
|
|
44
|
-
'cr_test',
|
|
45
|
-
'https://api.holysheep.ai',
|
|
46
|
-
'https://api.holysheep.ai/v1',
|
|
47
|
-
'gpt-5.4',
|
|
48
|
-
['gpt-5.4', 'claude-sonnet-4-6', 'MiniMax-M2.7-highspeed'],
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'))
|
|
52
|
-
const legacy = JSON.parse(fs.readFileSync(configFile, 'utf8'))
|
|
53
|
-
|
|
54
|
-
for (const config of [settings, legacy]) {
|
|
55
|
-
const gpt = config.customModels.find((entry) => entry.id === 'custom:gpt-5.4-0')
|
|
56
|
-
assert.ok(gpt, 'GPT-5.4 custom model should be present')
|
|
57
|
-
assert.strictEqual(gpt.model, 'gpt-5.4')
|
|
58
|
-
assert.strictEqual(gpt.provider, 'openai')
|
|
59
|
-
assert.strictEqual(gpt.baseUrl, 'https://api.holysheep.ai/v1')
|
|
60
|
-
|
|
61
|
-
const sonnet = config.customModels.find((entry) => entry.id === 'custom:claude-sonnet-4-6-0')
|
|
62
|
-
assert.ok(sonnet, 'Sonnet custom model should be present')
|
|
63
|
-
assert.strictEqual(sonnet.provider, 'anthropic')
|
|
64
|
-
assert.strictEqual(sonnet.baseUrl, 'https://api.holysheep.ai')
|
|
65
|
-
|
|
66
|
-
const minimax = config.customModels.find((entry) => entry.id === 'custom:MiniMax-M2.7-highspeed-0')
|
|
67
|
-
assert.ok(minimax, 'MiniMax custom model should be present')
|
|
68
|
-
assert.strictEqual(minimax.provider, 'anthropic')
|
|
69
|
-
assert.strictEqual(minimax.baseUrl, 'https://api.holysheep.ai/minimax')
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const preserved = settings.customModels.find((entry) => entry.id === 'custom:local-test')
|
|
73
|
-
assert.ok(preserved, 'non-HolySheep custom models should be preserved')
|
|
74
|
-
|
|
75
|
-
console.log('droid config test passed')
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
const assert = require('assert')
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const os = require('os')
|
|
4
|
-
const path = require('path')
|
|
5
|
-
|
|
6
|
-
const tempHome = fs.mkdtempSync(path.join(os.tmpdir(), 'hs-workspace-test-'))
|
|
7
|
-
process.env.HOME = tempHome
|
|
8
|
-
|
|
9
|
-
const store = require('../src/webui/workspace-store')
|
|
10
|
-
|
|
11
|
-
store.saveHolySheepApiConfig({
|
|
12
|
-
apiKey: 'cr_test_runtime',
|
|
13
|
-
baseUrl: 'https://api.holysheep.ai/v1',
|
|
14
|
-
model: 'gpt-5.4',
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
const conversation = store.createConversation({
|
|
18
|
-
title: 'Build workspace shell',
|
|
19
|
-
toolId: 'codex',
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
store.addMessage(conversation.id, {
|
|
23
|
-
role: 'user',
|
|
24
|
-
content: 'Need AionUi-style sidebar and search box',
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
store.addMessage(conversation.id, {
|
|
28
|
-
role: 'assistant',
|
|
29
|
-
content: 'Implement the shell and keep the HolySheep tool APIs intact.',
|
|
30
|
-
})
|
|
31
|
-
|
|
32
|
-
const task = store.saveTask({
|
|
33
|
-
title: 'Nightly summary',
|
|
34
|
-
prompt: 'Summarize the latest workspace changes',
|
|
35
|
-
schedule: '1h',
|
|
36
|
-
active: true,
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
const stateFile = store.STATE_FILE
|
|
40
|
-
assert.ok(fs.existsSync(stateFile), 'workspace state file should be created')
|
|
41
|
-
|
|
42
|
-
const loadedConversation = store.getConversation(conversation.id)
|
|
43
|
-
assert.ok(loadedConversation, 'conversation should be persisted')
|
|
44
|
-
assert.strictEqual(loadedConversation.messages.length, 2, 'conversation messages should be stored')
|
|
45
|
-
|
|
46
|
-
const search = store.searchWorkspace('sidebar')
|
|
47
|
-
assert.strictEqual(search.conversations.length, 1, 'conversation search should match message content')
|
|
48
|
-
|
|
49
|
-
const tasks = store.listTasks()
|
|
50
|
-
assert.strictEqual(tasks.length, 1, 'scheduled task should be persisted')
|
|
51
|
-
assert.strictEqual(tasks[0].id, task.id, 'task id should remain stable')
|
|
52
|
-
|
|
53
|
-
const runtime = store.getHolySheepApiConfig()
|
|
54
|
-
assert.strictEqual(runtime.apiKey, 'cr_test_runtime', 'runtime api key should be stored')
|
|
55
|
-
assert.strictEqual(runtime.model, 'gpt-5.4', 'runtime model should be stored')
|
|
56
|
-
|
|
57
|
-
console.log('workspace store test passed')
|