@simonyea/holysheep-cli 2.1.40 → 2.1.41
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/dist/configure-worker.js +4491 -0
- package/dist/index.js +9591 -0
- package/dist/process-proxy-inject.js +117 -0
- package/package.json +20 -7
- package/.gitea/workflows/sanity.yml +0 -125
- package/scripts/check-tarball-size.js +0 -44
- package/src/commands/balance.js +0 -57
- package/src/commands/claude-proxy.js +0 -248
- package/src/commands/claude.js +0 -135
- package/src/commands/doctor.js +0 -282
- package/src/commands/login.js +0 -211
- package/src/commands/openclaw.js +0 -258
- package/src/commands/reset.js +0 -53
- package/src/commands/setup.js +0 -493
- package/src/commands/upgrade.js +0 -168
- package/src/commands/webui.js +0 -622
- package/src/index.js +0 -226
- package/src/tools/aider.js +0 -78
- package/src/tools/antigravity.js +0 -42
- package/src/tools/claude-code.js +0 -228
- package/src/tools/claude-process-proxy.js +0 -1030
- package/src/tools/codex.js +0 -254
- package/src/tools/continue.js +0 -146
- package/src/tools/cursor.js +0 -71
- package/src/tools/droid.js +0 -281
- package/src/tools/env-config.js +0 -185
- package/src/tools/gemini-cli.js +0 -82
- package/src/tools/hermes.js +0 -354
- package/src/tools/index.js +0 -13
- package/src/tools/openclaw-bridge.js +0 -987
- package/src/tools/openclaw.js +0 -925
- package/src/tools/opencode.js +0 -227
- package/src/tools/process-proxy-inject.js +0 -142
- package/src/utils/config.js +0 -54
- package/src/utils/shell.js +0 -342
- package/src/utils/which.js +0 -176
- package/src/webui/aionui-runtime-fetcher.js +0 -429
- package/src/webui/aionui-runtime.js +0 -139
- package/src/webui/aionui-wrapper.js +0 -734
- package/src/webui/configure-worker.js +0 -67
- package/src/webui/server.js +0 -1572
- package/src/webui/workspace-runtime.js +0 -288
- package/src/webui/workspace-store.js +0 -325
- /package/{src/webui → dist}/index.html +0 -0
- /package/{src/tools → dist}/pty-hermes-wrapper.py +0 -0
package/src/tools/opencode.js
DELETED
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenCode 适配器 (anomalyco/opencode,原 sst/opencode)
|
|
3
|
-
*
|
|
4
|
-
* ⚠️ 重要:OpenCode 仓库已从 sst/opencode 迁移到 anomalyco/opencode
|
|
5
|
-
* 全局配置文件: ~/.config/opencode/opencode.json (不是 config.json!)
|
|
6
|
-
* 格式: JSON/JSONC,provider 配置格式如下:
|
|
7
|
-
*
|
|
8
|
-
* {
|
|
9
|
-
* "$schema": "https://opencode.ai/config.json",
|
|
10
|
-
* "model": "anthropic/claude-sonnet-4-5",
|
|
11
|
-
* "provider": {
|
|
12
|
-
* "anthropic": {
|
|
13
|
-
* "options": {
|
|
14
|
-
* "baseURL": "https://api.holysheep.ai/v1",
|
|
15
|
-
* "apiKey": "cr_xxx"
|
|
16
|
-
* }
|
|
17
|
-
* },
|
|
18
|
-
* "openai": {
|
|
19
|
-
* "options": {
|
|
20
|
-
* "baseURL": "https://api.holysheep.ai/v1",
|
|
21
|
-
* "apiKey": "cr_xxx"
|
|
22
|
-
* }
|
|
23
|
-
* }
|
|
24
|
-
* }
|
|
25
|
-
* }
|
|
26
|
-
*
|
|
27
|
-
* 安装方式(推荐): brew install anomalyco/tap/opencode
|
|
28
|
-
* 或: npm i -g opencode-ai@latest
|
|
29
|
-
* 说明:OpenCode 的 anthropic provider 会自行拼接 /messages,
|
|
30
|
-
* 因此这里必须写 /v1 基地址,最终才会落到 /v1/messages。
|
|
31
|
-
* 官方文档: https://opencode.ai/docs/config
|
|
32
|
-
*/
|
|
33
|
-
const fs = require('fs')
|
|
34
|
-
const path = require('path')
|
|
35
|
-
const os = require('os')
|
|
36
|
-
|
|
37
|
-
function getConfigFile() {
|
|
38
|
-
const candidates = [
|
|
39
|
-
// 新版标准路径(官方文档)
|
|
40
|
-
path.join(os.homedir(), '.config', 'opencode', 'opencode.json'),
|
|
41
|
-
// 旧版路径兼容
|
|
42
|
-
path.join(os.homedir(), '.config', 'opencode', 'config.json'),
|
|
43
|
-
path.join(os.homedir(), '.opencode', 'opencode.json'),
|
|
44
|
-
path.join(os.homedir(), '.opencode', 'config.json'),
|
|
45
|
-
// Windows
|
|
46
|
-
path.join(os.homedir(), 'AppData', 'Roaming', 'opencode', 'opencode.json'),
|
|
47
|
-
]
|
|
48
|
-
for (const f of candidates) {
|
|
49
|
-
if (fs.existsSync(f)) return f
|
|
50
|
-
}
|
|
51
|
-
// 默认路径(新版标准)
|
|
52
|
-
return path.join(os.homedir(), '.config', 'opencode', 'opencode.json')
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function readConfig() {
|
|
56
|
-
const file = getConfigFile()
|
|
57
|
-
try {
|
|
58
|
-
if (fs.existsSync(file)) {
|
|
59
|
-
// 支持 JSONC(带注释的 JSON)— 只删行首注释,避免误删 URL 中的 //
|
|
60
|
-
const content = fs.readFileSync(file, 'utf8')
|
|
61
|
-
return JSON.parse(content.replace(/^\s*\/\/[^\n]*/gm, '').replace(/\/\*[\s\S]*?\*\//g, ''))
|
|
62
|
-
}
|
|
63
|
-
} catch {}
|
|
64
|
-
return {}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function writeConfig(data) {
|
|
68
|
-
const file = getConfigFile()
|
|
69
|
-
fs.mkdirSync(path.dirname(file), { recursive: true })
|
|
70
|
-
fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8')
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* [HolySheep fork v2.1.36 / hs25] OpenCode OAuth credential store paths.
|
|
75
|
-
* OpenCode >=1.3.0 keeps `opencode auth login` tokens in auth.json. If an
|
|
76
|
-
* Anthropic OAuth access token lives here, it OVERRIDES the config-file
|
|
77
|
-
* `baseURL`, routing requests straight to api.anthropic.com — bypassing the
|
|
78
|
-
* HolySheep relay. Symptom in the wild:
|
|
79
|
-
* "Third-party apps now draw from your extra usage, not your plan limits."
|
|
80
|
-
* which is Anthropic's plan-limit string, i.e. the request never hit us.
|
|
81
|
-
*/
|
|
82
|
-
function getAuthFileCandidates() {
|
|
83
|
-
const home = os.homedir()
|
|
84
|
-
if (process.platform === 'win32') {
|
|
85
|
-
return [
|
|
86
|
-
path.join(process.env.LOCALAPPDATA || path.join(home, 'AppData', 'Local'), 'opencode', 'auth.json'),
|
|
87
|
-
path.join(home, '.local', 'share', 'opencode', 'auth.json'),
|
|
88
|
-
]
|
|
89
|
-
}
|
|
90
|
-
const xdg = process.env.XDG_DATA_HOME || path.join(home, '.local', 'share')
|
|
91
|
-
return [
|
|
92
|
-
path.join(xdg, 'opencode', 'auth.json'),
|
|
93
|
-
path.join(home, '.local', 'share', 'opencode', 'auth.json'),
|
|
94
|
-
path.join(home, 'Library', 'Application Support', 'opencode', 'auth.json'),
|
|
95
|
-
]
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/**
|
|
99
|
-
* Strip `anthropic` and `openai` OAuth tokens from OpenCode's auth.json.
|
|
100
|
-
* Uses `delete` on individual keys so other provider logins (github,
|
|
101
|
-
* deepseek, zhipu, …) survive. Deletes the whole file if empty after purge.
|
|
102
|
-
* Returns { touched: string[], keysAfter: string[] } for test assertions.
|
|
103
|
-
*/
|
|
104
|
-
function purgeOauthAuth() {
|
|
105
|
-
const touched = []
|
|
106
|
-
let keysAfter = []
|
|
107
|
-
for (const file of getAuthFileCandidates()) {
|
|
108
|
-
try {
|
|
109
|
-
if (!fs.existsSync(file)) continue
|
|
110
|
-
const raw = fs.readFileSync(file, 'utf8')
|
|
111
|
-
let data
|
|
112
|
-
try { data = JSON.parse(raw) } catch { continue }
|
|
113
|
-
if (!data || typeof data !== 'object') continue
|
|
114
|
-
|
|
115
|
-
let changed = false
|
|
116
|
-
for (const key of ['anthropic', 'openai']) {
|
|
117
|
-
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
118
|
-
delete data[key]
|
|
119
|
-
changed = true
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
if (!changed) continue
|
|
123
|
-
|
|
124
|
-
touched.push(file)
|
|
125
|
-
const remaining = Object.keys(data)
|
|
126
|
-
keysAfter = remaining
|
|
127
|
-
if (remaining.length === 0) {
|
|
128
|
-
try { fs.unlinkSync(file) } catch {}
|
|
129
|
-
} else {
|
|
130
|
-
fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8')
|
|
131
|
-
}
|
|
132
|
-
} catch {
|
|
133
|
-
// best-effort
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
return { touched, keysAfter }
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
module.exports = {
|
|
141
|
-
name: 'OpenCode',
|
|
142
|
-
id: 'opencode',
|
|
143
|
-
checkInstalled() {
|
|
144
|
-
return require('../utils/which').commandExists('opencode')
|
|
145
|
-
},
|
|
146
|
-
isConfigured() {
|
|
147
|
-
const c = readConfig()
|
|
148
|
-
return !!(
|
|
149
|
-
c.provider?.anthropic?.options?.baseURL?.includes('holysheep') ||
|
|
150
|
-
c.provider?.openai?.options?.baseURL?.includes('holysheep')
|
|
151
|
-
)
|
|
152
|
-
},
|
|
153
|
-
configure(apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI, primaryModel) {
|
|
154
|
-
// [HolySheep fork v2.1.36 / hs25] Strip any stale Anthropic/OpenAI OAuth
|
|
155
|
-
// tokens from OpenCode's auth.json BEFORE writing the config file, so
|
|
156
|
-
// the relay baseURL in opencode.json actually takes effect.
|
|
157
|
-
try { purgeOauthAuth() } catch {}
|
|
158
|
-
const config = readConfig()
|
|
159
|
-
if (!config.provider) config.provider = {}
|
|
160
|
-
|
|
161
|
-
// 设置 schema(方便编辑器自动补全)
|
|
162
|
-
if (!config['$schema']) config['$schema'] = 'https://opencode.ai/config.json'
|
|
163
|
-
|
|
164
|
-
// OpenCode 的 anthropic provider 需要 /v1 基地址,否则会打到 /messages 并返回 404。
|
|
165
|
-
config.provider.anthropic = {
|
|
166
|
-
options: {
|
|
167
|
-
baseURL: `${String(baseUrlAnthropicNoV1 || '').replace(/\/+$/, '')}/v1`,
|
|
168
|
-
apiKey,
|
|
169
|
-
},
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// 配置 OpenAI provider(GPT 模型)
|
|
173
|
-
config.provider.openai = {
|
|
174
|
-
options: {
|
|
175
|
-
baseURL: baseUrlOpenAI, // https://api.holysheep.ai/v1
|
|
176
|
-
apiKey,
|
|
177
|
-
},
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// 设置默认模型
|
|
181
|
-
config.model = `anthropic/${primaryModel || 'claude-sonnet-4-6'}`
|
|
182
|
-
|
|
183
|
-
// [HolySheep fork v2.1.27 / hs20] Disable the giant OpenCode tools that
|
|
184
|
-
// blow the Anthropic request body past ~25 KB. When bodies exceed ~40 KB
|
|
185
|
-
// against the HolySheep relay, Anthropic returns a plan-limit error:
|
|
186
|
-
// "Third-party apps now draw from your extra usage, not your plan limits"
|
|
187
|
-
// which OpenCode surfaces as a silent `stop_reason: end_turn` with
|
|
188
|
-
// zero tokens (class-C failure). Removing just the biggest schemas
|
|
189
|
-
// (todowrite ~8.8 KB, task ~4.4 KB, webfetch/skill smaller) brings
|
|
190
|
-
// bodies below the threshold while keeping bash/read/glob/grep/edit/write
|
|
191
|
-
// which are the core coding tools users actually need.
|
|
192
|
-
//
|
|
193
|
-
// Users who want these tools back can set HOLYSHEEP_OPENCODE_KEEP_ALL_TOOLS=1
|
|
194
|
-
// in their shell, or manually remove the entries from opencode.json.
|
|
195
|
-
if (!process.env.HOLYSHEEP_OPENCODE_KEEP_ALL_TOOLS) {
|
|
196
|
-
const tools = { ...(config.tools || {}) }
|
|
197
|
-
if (tools.todowrite === undefined) tools.todowrite = false
|
|
198
|
-
if (tools.todoread === undefined) tools.todoread = false
|
|
199
|
-
if (tools.task === undefined) tools.task = false
|
|
200
|
-
if (tools.skill === undefined) tools.skill = false
|
|
201
|
-
if (tools.webfetch === undefined) tools.webfetch = false
|
|
202
|
-
config.tools = tools
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
writeConfig(config)
|
|
206
|
-
return { file: getConfigFile(), hot: false }
|
|
207
|
-
},
|
|
208
|
-
reset() {
|
|
209
|
-
const config = readConfig()
|
|
210
|
-
if (config.provider) {
|
|
211
|
-
delete config.provider.anthropic
|
|
212
|
-
delete config.provider.openai
|
|
213
|
-
}
|
|
214
|
-
writeConfig(config)
|
|
215
|
-
// [HolySheep fork v2.1.36 / hs25] Also purge OAuth tokens so a subsequent
|
|
216
|
-
// reconfigure doesn't silently fall back to direct-to-Anthropic.
|
|
217
|
-
try { purgeOauthAuth() } catch {}
|
|
218
|
-
},
|
|
219
|
-
getConfigPath() { return getConfigFile() },
|
|
220
|
-
hint: '切换后重启 OpenCode 生效;配置文件: ~/.config/opencode/opencode.json',
|
|
221
|
-
launchCmd: 'opencode',
|
|
222
|
-
installCmd: 'brew install anomalyco/tap/opencode # 或: npm i -g opencode-ai@latest',
|
|
223
|
-
docsUrl: 'https://opencode.ai',
|
|
224
|
-
// [HolySheep fork v2.1.36 / hs25] Exposed for tests — see tests/opencode-auth-purge.test.js
|
|
225
|
-
_purgeOauthAuth: purgeOauthAuth,
|
|
226
|
-
_getAuthFileCandidates: getAuthFileCandidates,
|
|
227
|
-
}
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
/**
|
|
3
|
-
* 进程级代理注入 — 通过 NODE_OPTIONS=--require 加载
|
|
4
|
-
*
|
|
5
|
-
* Node.js v22+ 中 tls.connect / undici 不走 net.createConnection,
|
|
6
|
-
* 直接调用 net.Socket.prototype.connect,因此同时 patch 两者。
|
|
7
|
-
*
|
|
8
|
-
* 原理:将所有非本地 TCP 连接重定向到本地 bridge(CONNECT 代理),
|
|
9
|
-
* bridge 再通过 relay-control 分配的 node 节点出口,使 CRS 看到
|
|
10
|
-
* 可信的节点 IP。
|
|
11
|
-
*/
|
|
12
|
-
const net = require('net')
|
|
13
|
-
const { URL } = require('url')
|
|
14
|
-
|
|
15
|
-
const raw = process.env.HS_PROXY_URL
|
|
16
|
-
if (!raw) return
|
|
17
|
-
|
|
18
|
-
let parsed
|
|
19
|
-
try {
|
|
20
|
-
parsed = new URL(raw)
|
|
21
|
-
} catch {
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const PROXY_HOST = parsed.hostname
|
|
26
|
-
const PROXY_PORT = Number(parsed.port) || 80
|
|
27
|
-
const SKIP = new Set(['127.0.0.1', '::1', 'localhost', '0.0.0.0', PROXY_HOST])
|
|
28
|
-
// 阻断直连 Anthropic — 强制 Claude Code 走 ANTHROPIC_BASE_URL (HTTP 路径 → proxy → CRS)
|
|
29
|
-
// CONNECT 隧道因 TLS SNI 不匹配无法改写目标,只能阻断
|
|
30
|
-
const BLOCK = new Set(['api.anthropic.com'])
|
|
31
|
-
|
|
32
|
-
function shouldProxy(host, port) {
|
|
33
|
-
if (!port || !host || SKIP.has(host)) return false
|
|
34
|
-
if (BLOCK.has(host)) return 'block'
|
|
35
|
-
return true
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// api.anthropic.com → api.holysheep.ai,让 CONNECT 隧道也走 CRS 多账号调度
|
|
39
|
-
// 否则 CONNECT 隧道直达 Anthropic,绕过 CRS,单 token 直连容易触发 429
|
|
40
|
-
function rewriteHost(host) {
|
|
41
|
-
if (host === 'api.anthropic.com') return 'api.holysheep.ai'
|
|
42
|
-
return host
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function setupTunnel(sock, host, port, origEmit) {
|
|
46
|
-
const targetHost = rewriteHost(host)
|
|
47
|
-
sock.write(`CONNECT ${targetHost}:${port} HTTP/1.1\r\nHost: ${targetHost}:${port}\r\n\r\n`)
|
|
48
|
-
let buf = Buffer.alloc(0)
|
|
49
|
-
sock.on('data', function onData(chunk) {
|
|
50
|
-
buf = Buffer.concat([buf, chunk])
|
|
51
|
-
const i = buf.indexOf('\r\n\r\n')
|
|
52
|
-
if (i === -1) return
|
|
53
|
-
sock.removeListener('data', onData)
|
|
54
|
-
if (!buf.slice(0, i).toString().includes(' 200 ')) {
|
|
55
|
-
sock.emit = origEmit
|
|
56
|
-
sock.destroy(new Error(`CONNECT ${host}:${port} failed: ${buf.slice(0, buf.indexOf('\r\n')).toString()}`))
|
|
57
|
-
return
|
|
58
|
-
}
|
|
59
|
-
const rest = buf.slice(i + 4)
|
|
60
|
-
if (rest.length) sock.unshift(rest)
|
|
61
|
-
sock.emit = origEmit
|
|
62
|
-
origEmit('connect')
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function patchEmitAndConnect(sock, host, port, connectFn) {
|
|
67
|
-
let tunnelReady = false
|
|
68
|
-
const origEmit = sock.emit.bind(sock)
|
|
69
|
-
|
|
70
|
-
sock.emit = function(type) {
|
|
71
|
-
if (type === 'connect' && !tunnelReady) {
|
|
72
|
-
tunnelReady = true
|
|
73
|
-
setupTunnel(sock, host, port, origEmit)
|
|
74
|
-
return false
|
|
75
|
-
}
|
|
76
|
-
return origEmit.apply(null, arguments)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return connectFn()
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// ── patch net.Socket.prototype.connect (Node.js v22+ TLS / undici) ───────────
|
|
83
|
-
const _origSocketConnect = net.Socket.prototype.connect
|
|
84
|
-
|
|
85
|
-
net.Socket.prototype.connect = function(options) {
|
|
86
|
-
const isObj = options !== null && typeof options === 'object'
|
|
87
|
-
const host = isObj ? String(options.host || options.hostname || '') : String(options || '')
|
|
88
|
-
const port = isObj ? Number(options.port || 0) : Number(arguments[1] || 0)
|
|
89
|
-
|
|
90
|
-
const action = shouldProxy(host, port)
|
|
91
|
-
if (!action) return _origSocketConnect.apply(this, arguments)
|
|
92
|
-
if (action === 'block') {
|
|
93
|
-
// 阻断直连 Anthropic,触发连接失败,Claude Code 会回退到 ANTHROPIC_BASE_URL
|
|
94
|
-
const sock = this
|
|
95
|
-
process.nextTick(() => sock.destroy(new Error(`ECONNREFUSED: blocked direct connection to ${host}`)))
|
|
96
|
-
return sock
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const sock = this
|
|
100
|
-
const proxyOpts = isObj
|
|
101
|
-
? { ...options, host: PROXY_HOST, port: PROXY_PORT }
|
|
102
|
-
: { host: PROXY_HOST, port: PROXY_PORT }
|
|
103
|
-
|
|
104
|
-
return patchEmitAndConnect(sock, host, port, () =>
|
|
105
|
-
_origSocketConnect.call(sock, proxyOpts)
|
|
106
|
-
)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// ── patch net.createConnection (兼容直接调用的旧代码) ─────────────────────────
|
|
110
|
-
const _origCreate = net.createConnection
|
|
111
|
-
|
|
112
|
-
function proxied(options, cb) {
|
|
113
|
-
const isObj = options !== null && typeof options === 'object'
|
|
114
|
-
const host = isObj ? String(options.host || options.hostname || 'localhost') : String(options || 'localhost')
|
|
115
|
-
const port = isObj ? Number(options.port || 0) : Number(arguments[1] || 0)
|
|
116
|
-
|
|
117
|
-
const action = shouldProxy(host, port)
|
|
118
|
-
if (!action) return _origCreate.apply(this, arguments)
|
|
119
|
-
if (action === 'block') {
|
|
120
|
-
const sock = new net.Socket()
|
|
121
|
-
process.nextTick(() => sock.destroy(new Error(`ECONNREFUSED: blocked direct connection to ${host}`)))
|
|
122
|
-
return sock
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const sock = _origCreate({ host: PROXY_HOST, port: PROXY_PORT })
|
|
126
|
-
if (typeof cb === 'function') sock.once('connect', cb)
|
|
127
|
-
|
|
128
|
-
let tunnelReady = false
|
|
129
|
-
const origEmit = sock.emit.bind(sock)
|
|
130
|
-
sock.emit = function(type) {
|
|
131
|
-
if (type === 'connect' && !tunnelReady) {
|
|
132
|
-
tunnelReady = true
|
|
133
|
-
setupTunnel(sock, host, port, origEmit)
|
|
134
|
-
return false
|
|
135
|
-
}
|
|
136
|
-
return origEmit.apply(null, arguments)
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return sock
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
net.createConnection = net.connect = proxied
|
package/src/utils/config.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 本地配置管理 — 存储 API Key 和用户偏好
|
|
3
|
-
* 配置文件: ~/.holysheep/config.json
|
|
4
|
-
*/
|
|
5
|
-
const fs = require('fs')
|
|
6
|
-
const path = require('path')
|
|
7
|
-
const os = require('os')
|
|
8
|
-
|
|
9
|
-
const CONFIG_DIR = path.join(os.homedir(), '.holysheep')
|
|
10
|
-
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json')
|
|
11
|
-
|
|
12
|
-
const BASE_URL_ANTHROPIC = 'https://api.holysheep.ai' // 不带 /v1 (Anthropic SDK)
|
|
13
|
-
const BASE_URL_OPENAI = 'https://api.holysheep.ai/v1' // 带 /v1 (OpenAI 兼容)
|
|
14
|
-
const BASE_URL_CLAUDE_RELAY = process.env.HOLYSHEEP_CLAUDE_RELAY_URL || 'https://api.holysheep.ai/claude-relay'
|
|
15
|
-
const SHOP_URL = 'https://holysheep.ai'
|
|
16
|
-
|
|
17
|
-
function ensureDir() {
|
|
18
|
-
if (!fs.existsSync(CONFIG_DIR)) {
|
|
19
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true })
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function loadConfig() {
|
|
24
|
-
try {
|
|
25
|
-
if (fs.existsSync(CONFIG_FILE)) {
|
|
26
|
-
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))
|
|
27
|
-
}
|
|
28
|
-
} catch {}
|
|
29
|
-
return {}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function saveConfig(data) {
|
|
33
|
-
ensureDir()
|
|
34
|
-
const current = loadConfig()
|
|
35
|
-
const merged = { ...current, ...data }
|
|
36
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2))
|
|
37
|
-
return merged
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getApiKey() {
|
|
41
|
-
return loadConfig().apiKey || process.env.HOLYSHEEP_API_KEY || ''
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
module.exports = {
|
|
45
|
-
CONFIG_DIR,
|
|
46
|
-
CONFIG_FILE,
|
|
47
|
-
BASE_URL_ANTHROPIC,
|
|
48
|
-
BASE_URL_OPENAI,
|
|
49
|
-
BASE_URL_CLAUDE_RELAY,
|
|
50
|
-
SHOP_URL,
|
|
51
|
-
loadConfig,
|
|
52
|
-
saveConfig,
|
|
53
|
-
getApiKey,
|
|
54
|
-
}
|