@simonyea/holysheep-cli 1.0.0

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.
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Aider 适配器
3
+ * Aider 是最流行的命令行 AI 结对编程工具
4
+ *
5
+ * 配置方式:
6
+ * 环境变量: AIDER_OPENAI_API_BASE + OPENAI_API_KEY
7
+ * 或: --openai-api-base + --anthropic-api-key
8
+ *
9
+ * Aider 支持 litellm 格式,可以接入任何 OpenAI 兼容端点
10
+ * 配置文件: ~/.aider.conf.yml
11
+ */
12
+ const fs = require('fs')
13
+ const path = require('path')
14
+ const os = require('os')
15
+
16
+ const CONFIG_FILE = path.join(os.homedir(), '.aider.conf.yml')
17
+
18
+ function readConfig() {
19
+ try {
20
+ if (fs.existsSync(CONFIG_FILE)) return fs.readFileSync(CONFIG_FILE, 'utf8')
21
+ } catch {}
22
+ return ''
23
+ }
24
+
25
+ function removeHsBlock(content) {
26
+ const lines = content.split('\n')
27
+ const result = []
28
+ let skip = false
29
+ for (const line of lines) {
30
+ if (line.includes('# holysheep-cli')) { skip = true; continue }
31
+ if (skip && line.startsWith('#')) { skip = false }
32
+ if (!skip) result.push(line)
33
+ }
34
+ return result.join('\n')
35
+ }
36
+
37
+ module.exports = {
38
+ name: 'Aider',
39
+ id: 'aider',
40
+ checkInstalled() {
41
+ try {
42
+ require('child_process').execSync('which aider', { stdio: 'ignore' })
43
+ return true
44
+ } catch { return false }
45
+ },
46
+ isConfigured() {
47
+ const content = readConfig()
48
+ return content.includes('holysheep')
49
+ },
50
+ configure(apiKey, baseUrlOpenAI) {
51
+ let content = readConfig()
52
+ content = removeHsBlock(content)
53
+ // Aider 用 openai-api-base (不带 /v1 的后缀) 或完整带 /v1
54
+ const block = `
55
+ # holysheep-cli managed — https://shop.holysheep.ai
56
+ openai-api-key: ${apiKey}
57
+ openai-api-base: ${baseUrlOpenAI}
58
+ model: openai/claude-sonnet-4-5
59
+ `
60
+ content += block
61
+ fs.writeFileSync(CONFIG_FILE, content.trim() + '\n', 'utf8')
62
+ return { file: CONFIG_FILE, hot: false }
63
+ },
64
+ reset() {
65
+ let content = readConfig()
66
+ content = removeHsBlock(content)
67
+ fs.writeFileSync(CONFIG_FILE, content, 'utf8')
68
+ },
69
+ getConfigPath() { return CONFIG_FILE },
70
+ hint: '也可用 aider --openai-api-base https://api.holysheep.ai/v1',
71
+ installCmd: 'pip install aider-chat',
72
+ docsUrl: 'https://aider.chat',
73
+ // Aider 优先用环境变量
74
+ envVars: (apiKey, baseUrlOpenAI) => ({
75
+ OPENAI_API_KEY: apiKey,
76
+ OPENAI_BASE_URL: baseUrlOpenAI,
77
+ AIDER_OPENAI_API_BASE: baseUrlOpenAI,
78
+ }),
79
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Claude Code 适配器
3
+ * 配置文件: ~/.claude/settings.json
4
+ * 支持热切换(不需要重启终端)
5
+ *
6
+ * 官方 env 字段:
7
+ * ANTHROPIC_AUTH_TOKEN — API Key (优先级最高)
8
+ * ANTHROPIC_BASE_URL — 不带 /v1
9
+ * CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC — 关闭遥测/更新检查
10
+ */
11
+ const fs = require('fs')
12
+ const path = require('path')
13
+ const os = require('os')
14
+
15
+ const SETTINGS_FILE = path.join(os.homedir(), '.claude', 'settings.json')
16
+
17
+ function readSettings() {
18
+ try {
19
+ const raw = fs.readFileSync(SETTINGS_FILE, 'utf8')
20
+ // Claude 的 settings.json 可能有控制字符,用容错解析
21
+ return JSON.parse(raw.replace(/[\x00-\x1F\x7F]/g, ' '))
22
+ } catch {
23
+ return {}
24
+ }
25
+ }
26
+
27
+ function writeSettings(data) {
28
+ const dir = path.dirname(SETTINGS_FILE)
29
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
30
+ fs.writeFileSync(SETTINGS_FILE, JSON.stringify(data, null, 2), 'utf8')
31
+ }
32
+
33
+ module.exports = {
34
+ name: 'Claude Code',
35
+ id: 'claude-code',
36
+ checkInstalled() {
37
+ // 检查是否安装了 claude CLI
38
+ try {
39
+ require('child_process').execSync('which claude', { stdio: 'ignore' })
40
+ return true
41
+ } catch {
42
+ return false
43
+ }
44
+ },
45
+ isConfigured() {
46
+ const s = readSettings()
47
+ return !!(s.env?.ANTHROPIC_AUTH_TOKEN || s.env?.ANTHROPIC_API_KEY)
48
+ },
49
+ configure(apiKey, baseUrl) {
50
+ const settings = readSettings()
51
+ if (!settings.env) settings.env = {}
52
+ // Claude Code 用 ANTHROPIC_AUTH_TOKEN(最高优先级),兼容 ANTHROPIC_API_KEY
53
+ settings.env.ANTHROPIC_AUTH_TOKEN = apiKey
54
+ settings.env.ANTHROPIC_BASE_URL = baseUrl
55
+ settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC = '1'
56
+ // 清理旧的同义字段
57
+ delete settings.env.ANTHROPIC_API_KEY
58
+ writeSettings(settings)
59
+ return { file: SETTINGS_FILE, hot: true }
60
+ },
61
+ reset() {
62
+ const settings = readSettings()
63
+ if (settings.env) {
64
+ delete settings.env.ANTHROPIC_AUTH_TOKEN
65
+ delete settings.env.ANTHROPIC_API_KEY
66
+ delete settings.env.ANTHROPIC_BASE_URL
67
+ delete settings.env.CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC
68
+ }
69
+ writeSettings(settings)
70
+ },
71
+ getConfigPath() { return SETTINGS_FILE },
72
+ hint: '支持热切换,无需重启终端',
73
+ installCmd: 'npm install -g @anthropic-ai/claude-code',
74
+ docsUrl: 'https://docs.anthropic.com/claude-code',
75
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * OpenAI Codex CLI 适配器
3
+ * 配置文件: ~/.codex/config.yaml 或 ~/.codex/config.json
4
+ * 环境变量: OPENAI_API_KEY, OPENAI_BASE_URL
5
+ *
6
+ * Codex 支持 custom provider 配置:
7
+ * providers:
8
+ * - name: HolySheep
9
+ * baseURL: https://api.holysheep.ai/v1
10
+ * envKey: OPENAI_API_KEY
11
+ *
12
+ * 注意: Codex 用 OpenAI 兼容格式,baseURL 需带 /v1
13
+ */
14
+ const fs = require('fs')
15
+ const path = require('path')
16
+ const os = require('os')
17
+
18
+ const CONFIG_DIR = path.join(os.homedir(), '.codex')
19
+ const CONFIG_YAML = path.join(CONFIG_DIR, 'config.yaml')
20
+ const CONFIG_JSON = path.join(CONFIG_DIR, 'config.json')
21
+
22
+ function readConfig() {
23
+ try {
24
+ if (fs.existsSync(CONFIG_YAML)) return { type: 'yaml', content: fs.readFileSync(CONFIG_YAML, 'utf8') }
25
+ if (fs.existsSync(CONFIG_JSON)) return { type: 'json', content: fs.readFileSync(CONFIG_JSON, 'utf8') }
26
+ } catch {}
27
+ return { type: 'yaml', content: '' }
28
+ }
29
+
30
+ function removeHolysheepProvider(yamlContent) {
31
+ // 简单移除已有的 holysheep provider 块
32
+ const lines = yamlContent.split('\n')
33
+ const result = []
34
+ let skip = false
35
+ for (const line of lines) {
36
+ if (line.includes('HolySheep') || line.includes('holysheep')) {
37
+ skip = true
38
+ // 也移除上一行的 `- name:` 前缀
39
+ if (result.length && result[result.length - 1].trim().startsWith('- name:')) {
40
+ result.pop()
41
+ }
42
+ continue
43
+ }
44
+ if (skip && (line.startsWith(' ') || line.trim() === '')) {
45
+ if (line.trim() === '') skip = false
46
+ continue
47
+ }
48
+ skip = false
49
+ result.push(line)
50
+ }
51
+ return result.join('\n')
52
+ }
53
+
54
+ module.exports = {
55
+ name: 'Codex CLI',
56
+ id: 'codex',
57
+ checkInstalled() {
58
+ try {
59
+ require('child_process').execSync('which codex', { stdio: 'ignore' })
60
+ return true
61
+ } catch { return false }
62
+ },
63
+ isConfigured() {
64
+ const { content } = readConfig()
65
+ return content.includes('holysheep') || content.includes('HolySheep')
66
+ },
67
+ configure(apiKey, baseUrlOpenAI) {
68
+ if (!fs.existsSync(CONFIG_DIR)) fs.mkdirSync(CONFIG_DIR, { recursive: true })
69
+
70
+ // 生成 YAML 格式配置(Codex 官方推荐)
71
+ let content = ''
72
+ if (fs.existsSync(CONFIG_YAML)) {
73
+ content = fs.readFileSync(CONFIG_YAML, 'utf8')
74
+ content = removeHolysheepProvider(content)
75
+ }
76
+
77
+ // 追加 holysheep provider + 设为默认 model provider
78
+ const providerBlock = `
79
+ # HolySheep API — https://shop.holysheep.ai
80
+ providers:
81
+ - name: HolySheep
82
+ baseURL: ${baseUrlOpenAI}
83
+ envKey: OPENAI_API_KEY
84
+ model: claude-sonnet-4-5
85
+ `
86
+ // 如果已有 providers 块,改为追加到列表
87
+ if (content.includes('providers:')) {
88
+ content = content.replace('providers:', `providers:\n - name: HolySheep\n baseURL: ${baseUrlOpenAI}\n envKey: OPENAI_API_KEY`)
89
+ } else {
90
+ content += providerBlock
91
+ }
92
+
93
+ fs.writeFileSync(CONFIG_YAML, content.trim() + '\n', 'utf8')
94
+
95
+ // 同时写入环境变量(Codex 通过 envKey 读取)
96
+ return {
97
+ file: CONFIG_YAML,
98
+ hot: false,
99
+ envVars: {
100
+ OPENAI_API_KEY: apiKey,
101
+ OPENAI_BASE_URL: baseUrlOpenAI,
102
+ },
103
+ }
104
+ },
105
+ reset() {
106
+ if (fs.existsSync(CONFIG_YAML)) {
107
+ let content = fs.readFileSync(CONFIG_YAML, 'utf8')
108
+ content = removeHolysheepProvider(content)
109
+ fs.writeFileSync(CONFIG_YAML, content, 'utf8')
110
+ }
111
+ },
112
+ getConfigPath() { return CONFIG_YAML },
113
+ hint: '切换后需重启终端或新开 terminal',
114
+ installCmd: 'npm install -g @openai/codex',
115
+ docsUrl: 'https://github.com/openai/codex',
116
+ envVarFormat: 'openai', // 告知 setup 命令写哪些 env vars
117
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Continue.dev 适配器 (VS Code / JetBrains 插件)
3
+ * 配置文件: ~/.continue/config.json
4
+ *
5
+ * Continue 支持自定义 provider,格式:
6
+ * {
7
+ * "models": [{
8
+ * "title": "HolySheep Claude",
9
+ * "provider": "openai",
10
+ * "model": "claude-sonnet-4-5",
11
+ * "apiKey": "cr_xxx",
12
+ * "apiBase": "https://api.holysheep.ai/v1"
13
+ * }]
14
+ * }
15
+ */
16
+ const fs = require('fs')
17
+ const path = require('path')
18
+ const os = require('os')
19
+
20
+ const CONFIG_FILE = path.join(os.homedir(), '.continue', 'config.json')
21
+
22
+ function readConfig() {
23
+ try {
24
+ if (fs.existsSync(CONFIG_FILE)) return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'))
25
+ } catch {}
26
+ return { models: [], tabAutocompleteModel: null }
27
+ }
28
+
29
+ function writeConfig(data) {
30
+ fs.mkdirSync(path.dirname(CONFIG_FILE), { recursive: true })
31
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2), 'utf8')
32
+ }
33
+
34
+ const HS_MODELS = (apiKey, baseUrl) => [
35
+ {
36
+ title: 'HolySheep — Claude Sonnet',
37
+ provider: 'openai',
38
+ model: 'claude-sonnet-4-5',
39
+ apiKey,
40
+ apiBase: baseUrl,
41
+ },
42
+ {
43
+ title: 'HolySheep — Claude Opus',
44
+ provider: 'openai',
45
+ model: 'claude-opus-4-5',
46
+ apiKey,
47
+ apiBase: baseUrl,
48
+ },
49
+ ]
50
+
51
+ module.exports = {
52
+ name: 'Continue.dev',
53
+ id: 'continue',
54
+ checkInstalled() {
55
+ return fs.existsSync(path.join(os.homedir(), '.continue'))
56
+ },
57
+ isConfigured() {
58
+ const c = readConfig()
59
+ return (c.models || []).some(m => m.apiBase?.includes('holysheep'))
60
+ },
61
+ configure(apiKey, baseUrlOpenAI) {
62
+ const config = readConfig()
63
+ // 移除旧的 holysheep models
64
+ config.models = (config.models || []).filter(m => !m.apiBase?.includes('holysheep'))
65
+ // 插入新的
66
+ config.models = [...HS_MODELS(apiKey, baseUrlOpenAI), ...config.models]
67
+ writeConfig(config)
68
+ return { file: CONFIG_FILE, hot: true }
69
+ },
70
+ reset() {
71
+ const config = readConfig()
72
+ config.models = (config.models || []).filter(m => !m.apiBase?.includes('holysheep'))
73
+ writeConfig(config)
74
+ },
75
+ getConfigPath() { return CONFIG_FILE },
76
+ hint: '配置后在 VS Code Continue 面板选择 HolySheep 模型即可使用',
77
+ installCmd: 'VS Code 插件市场搜索 "Continue"',
78
+ docsUrl: 'https://continue.dev',
79
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Cursor 适配器
3
+ * Cursor 是基于 VSCode 的 AI 编辑器
4
+ *
5
+ * Cursor 自定义 API 配置路径:
6
+ * Settings > Cursor > Models > Custom API Key
7
+ * 实际存储在: ~/Library/Application Support/Cursor/User/globalStorage/cursor.secretStorage/...
8
+ *
9
+ * 由于 Cursor 的 API 配置在加密的 secret storage 中,CLI 无法直接写入,
10
+ * 因此本适配器采用以下方式:
11
+ * 1. 打印详细的手动配置引导
12
+ * 2. 写入环境变量(部分 Cursor 功能如 API playground 会读取)
13
+ * 3. 生成 .cursor/mcp.json 供 Cursor MCP 功能使用
14
+ *
15
+ * Cursor OpenAI 兼容格式: 在 Settings > Models 中填入 base URL 和 API Key
16
+ */
17
+ const fs = require('fs')
18
+ const path = require('path')
19
+ const os = require('os')
20
+ const { execSync } = require('child_process')
21
+
22
+ function getCursorUserDir() {
23
+ const p = process.platform
24
+ if (p === 'darwin') return path.join(os.homedir(), 'Library', 'Application Support', 'Cursor', 'User')
25
+ if (p === 'win32') return path.join(os.homedir(), 'AppData', 'Roaming', 'Cursor', 'User')
26
+ return path.join(os.homedir(), '.config', 'Cursor', 'User')
27
+ }
28
+
29
+ function checkCursorInstalled() {
30
+ const p = process.platform
31
+ if (p === 'darwin') return fs.existsSync('/Applications/Cursor.app') || fs.existsSync(path.join(os.homedir(), 'Applications', 'Cursor.app'))
32
+ if (p === 'win32') return fs.existsSync(path.join(os.homedir(), 'AppData', 'Local', 'Programs', 'cursor', 'Cursor.exe'))
33
+ try { execSync('which cursor', { stdio: 'ignore' }); return true } catch {}
34
+ return false
35
+ }
36
+
37
+ module.exports = {
38
+ name: 'Cursor',
39
+ id: 'cursor',
40
+ checkInstalled() { return checkCursorInstalled() },
41
+ isConfigured() {
42
+ // Cursor 配置在加密存储中,无法直接读取,只检查环境变量
43
+ return !!(process.env.OPENAI_API_KEY && process.env.OPENAI_BASE_URL?.includes('holysheep'))
44
+ },
45
+ configure(apiKey, baseUrlOpenAI) {
46
+ // Cursor 无法通过文件直接写入 API Key(加密存储),
47
+ // 返回 manual 标志,setup 命令会打印引导信息
48
+ return {
49
+ manual: true,
50
+ steps: [
51
+ '打开 Cursor → Settings(⌘+,)→ 搜索 "Models"',
52
+ '找到 "OpenAI API Key" 或 "Custom API" 区域',
53
+ `填入 API Key: ${apiKey}`,
54
+ `填入 Base URL(Override OpenAI Base URL): ${baseUrlOpenAI}`,
55
+ '点击 "Verify" 验证连接',
56
+ '在模型列表中选择 claude-sonnet-4-5 或 claude-opus-4-5',
57
+ ],
58
+ imageHint: '💡 Cursor Settings → Models → Override OpenAI Base URL',
59
+ }
60
+ },
61
+ reset() {
62
+ // 提示手动清除
63
+ return { manual: true, steps: ['打开 Cursor Settings → Models → 清除 API Key 和 Base URL'] }
64
+ },
65
+ getConfigPath() { return getCursorUserDir() },
66
+ hint: '需要在 Cursor GUI 中手动配置(API Key 存储在加密区域)',
67
+ installCmd: '访问 https://cursor.sh 下载安装',
68
+ docsUrl: 'https://cursor.sh',
69
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Gemini CLI 适配器 (@google/gemini-cli)
3
+ *
4
+ * Gemini CLI 目前不原生支持自定义 base_url,
5
+ * 但可通过以下方式接入 OpenAI 兼容端点:
6
+ *
7
+ * 方式 A(推荐): 写入 ~/.gemini/settings.json 中的 otherAIProvider
8
+ * 支持 openaiCompatible 类型,需要 Gemini CLI >= 0.20
9
+ *
10
+ * 方式 B: 通过 GEMINI_API_KEY 环境变量 + GOOGLE_API_KEY 覆盖
11
+ * (仅适用于少数版本)
12
+ *
13
+ * 实际测试: Gemini CLI 0.30.0 支持 otherAIProvider 配置
14
+ * 参考: https://github.com/google-gemini/gemini-cli/blob/main/docs/configuration.md
15
+ */
16
+ const fs = require('fs')
17
+ const path = require('path')
18
+ const os = require('os')
19
+
20
+ const SETTINGS_FILE = path.join(os.homedir(), '.gemini', 'settings.json')
21
+
22
+ function readSettings() {
23
+ try {
24
+ if (fs.existsSync(SETTINGS_FILE)) {
25
+ return JSON.parse(fs.readFileSync(SETTINGS_FILE, 'utf8'))
26
+ }
27
+ } catch {}
28
+ return {}
29
+ }
30
+
31
+ function writeSettings(data) {
32
+ const dir = path.dirname(SETTINGS_FILE)
33
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
34
+ fs.writeFileSync(SETTINGS_FILE, JSON.stringify(data, null, 2), 'utf8')
35
+ }
36
+
37
+ module.exports = {
38
+ name: 'Gemini CLI',
39
+ id: 'gemini-cli',
40
+ checkInstalled() {
41
+ try {
42
+ require('child_process').execSync('which gemini', { stdio: 'ignore' })
43
+ return true
44
+ } catch { return false }
45
+ },
46
+ isConfigured() {
47
+ const s = readSettings()
48
+ return !!(s.otherAIProvider?.url?.includes('holysheep'))
49
+ },
50
+ configure(apiKey, baseUrlOpenAI) {
51
+ const settings = readSettings()
52
+
53
+ // Gemini CLI 的 otherAIProvider 支持 OpenAI 兼容格式
54
+ settings.otherAIProvider = {
55
+ url: baseUrlOpenAI, // 带 /v1
56
+ apiKey: apiKey,
57
+ model: 'claude-sonnet-4-5', // 默认推荐模型
58
+ }
59
+
60
+ // 同时保留原有 general 配置
61
+ writeSettings(settings)
62
+ return { file: SETTINGS_FILE, hot: false }
63
+ },
64
+ reset() {
65
+ const settings = readSettings()
66
+ delete settings.otherAIProvider
67
+ writeSettings(settings)
68
+ },
69
+ getConfigPath() { return SETTINGS_FILE },
70
+ hint: '使用 gemini -m claude-sonnet-4-5 指定模型',
71
+ installCmd: 'npm install -g @google/gemini-cli',
72
+ docsUrl: 'https://github.com/google-gemini/gemini-cli',
73
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * 所有工具适配器的统一入口
3
+ */
4
+ module.exports = [
5
+ require('./claude-code'),
6
+ require('./codex'),
7
+ require('./gemini-cli'),
8
+ require('./opencode'),
9
+ require('./openclaw'),
10
+ require('./aider'),
11
+ require('./cursor'),
12
+ require('./continue'),
13
+ ]
@@ -0,0 +1,79 @@
1
+ /**
2
+ * OpenClaw 适配器
3
+ * OpenClaw 是基于 Claude Code 架构的开源 AI 编程助手
4
+ * 配置方式与 Claude Code 几乎相同,使用 Anthropic API 格式
5
+ *
6
+ * 配置文件: ~/.openclaw/settings.json (或 ~/.claude/settings.json 共享)
7
+ * 环境变量: ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL
8
+ *
9
+ * OpenClaw 也支持通过 AGENTS.md 自定义 agent 行为
10
+ */
11
+ const fs = require('fs')
12
+ const path = require('path')
13
+ const os = require('os')
14
+
15
+ function getSettingsFile() {
16
+ // OpenClaw 可能用独立配置,也可能共享 Claude 配置
17
+ const openclaw = path.join(os.homedir(), '.openclaw', 'settings.json')
18
+ const claude = path.join(os.homedir(), '.claude', 'settings.json')
19
+ if (fs.existsSync(openclaw)) return openclaw
20
+ // 检查是否安装了 openclaw
21
+ try {
22
+ require('child_process').execSync('which openclaw', { stdio: 'ignore' })
23
+ return openclaw
24
+ } catch {
25
+ return openclaw // 默认路径
26
+ }
27
+ }
28
+
29
+ function readSettings(file) {
30
+ try {
31
+ if (fs.existsSync(file)) {
32
+ return JSON.parse(fs.readFileSync(file, 'utf8').replace(/[\x00-\x1F\x7F]/g, ' '))
33
+ }
34
+ } catch {}
35
+ return {}
36
+ }
37
+
38
+ function writeSettings(file, data) {
39
+ fs.mkdirSync(path.dirname(file), { recursive: true })
40
+ fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8')
41
+ }
42
+
43
+ module.exports = {
44
+ name: 'OpenClaw',
45
+ id: 'openclaw',
46
+ checkInstalled() {
47
+ try {
48
+ require('child_process').execSync('which openclaw', { stdio: 'ignore' })
49
+ return true
50
+ } catch { return false }
51
+ },
52
+ isConfigured() {
53
+ const file = getSettingsFile()
54
+ const s = readSettings(file)
55
+ return !!(s.env?.ANTHROPIC_API_KEY || s.env?.ANTHROPIC_AUTH_TOKEN)
56
+ },
57
+ configure(apiKey, baseUrlAnthropicNoV1) {
58
+ const file = getSettingsFile()
59
+ const settings = readSettings(file)
60
+ if (!settings.env) settings.env = {}
61
+ settings.env.ANTHROPIC_API_KEY = apiKey
62
+ settings.env.ANTHROPIC_BASE_URL = baseUrlAnthropicNoV1
63
+ writeSettings(file, settings)
64
+ return { file, hot: true }
65
+ },
66
+ reset() {
67
+ const file = getSettingsFile()
68
+ const settings = readSettings(file)
69
+ if (settings.env) {
70
+ delete settings.env.ANTHROPIC_API_KEY
71
+ delete settings.env.ANTHROPIC_BASE_URL
72
+ }
73
+ writeSettings(file, settings)
74
+ },
75
+ getConfigPath() { return getSettingsFile() },
76
+ hint: '配置方式同 Claude Code,支持热切换',
77
+ installCmd: '请访问 openclaw 官网下载安装',
78
+ docsUrl: 'https://github.com/iOfficeAI/AionUi',
79
+ }
@@ -0,0 +1,92 @@
1
+ /**
2
+ * OpenCode 适配器 (sst/opencode)
3
+ * 配置文件: ~/.config/opencode/config.json 或 ~/.opencode/config.json
4
+ * 格式: JSON,支持 providers 数组
5
+ *
6
+ * OpenCode provider 格式:
7
+ * {
8
+ * "providers": {
9
+ * "anthropic": {
10
+ * "apiKey": "cr_xxx",
11
+ * "baseURL": "https://api.holysheep.ai"
12
+ * },
13
+ * "openai": {
14
+ * "apiKey": "cr_xxx",
15
+ * "baseURL": "https://api.holysheep.ai/v1"
16
+ * }
17
+ * }
18
+ * }
19
+ */
20
+ const fs = require('fs')
21
+ const path = require('path')
22
+ const os = require('os')
23
+
24
+ function getConfigFile() {
25
+ const candidates = [
26
+ path.join(os.homedir(), '.config', 'opencode', 'config.json'),
27
+ path.join(os.homedir(), '.opencode', 'config.json'),
28
+ // Windows
29
+ path.join(os.homedir(), 'AppData', 'Roaming', 'opencode', 'config.json'),
30
+ ]
31
+ for (const f of candidates) {
32
+ if (fs.existsSync(f)) return f
33
+ }
34
+ // 默认路径
35
+ return path.join(os.homedir(), '.config', 'opencode', 'config.json')
36
+ }
37
+
38
+ function readConfig() {
39
+ const file = getConfigFile()
40
+ try {
41
+ if (fs.existsSync(file)) return JSON.parse(fs.readFileSync(file, 'utf8'))
42
+ } catch {}
43
+ return {}
44
+ }
45
+
46
+ function writeConfig(data) {
47
+ const file = getConfigFile()
48
+ fs.mkdirSync(path.dirname(file), { recursive: true })
49
+ fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf8')
50
+ }
51
+
52
+ module.exports = {
53
+ name: 'OpenCode',
54
+ id: 'opencode',
55
+ checkInstalled() {
56
+ try {
57
+ require('child_process').execSync('which opencode', { stdio: 'ignore' })
58
+ return true
59
+ } catch { return false }
60
+ },
61
+ isConfigured() {
62
+ const c = readConfig()
63
+ return !!(c.providers?.anthropic?.baseURL?.includes('holysheep') ||
64
+ c.providers?.openai?.baseURL?.includes('holysheep'))
65
+ },
66
+ configure(apiKey, baseUrlAnthropicNoV1, baseUrlOpenAI) {
67
+ const config = readConfig()
68
+ if (!config.providers) config.providers = {}
69
+ config.providers.anthropic = {
70
+ apiKey,
71
+ baseURL: baseUrlAnthropicNoV1,
72
+ }
73
+ config.providers.openai = {
74
+ apiKey,
75
+ baseURL: baseUrlOpenAI,
76
+ }
77
+ writeConfig(config)
78
+ return { file: getConfigFile(), hot: false }
79
+ },
80
+ reset() {
81
+ const config = readConfig()
82
+ if (config.providers) {
83
+ delete config.providers.anthropic
84
+ delete config.providers.openai
85
+ }
86
+ writeConfig(config)
87
+ },
88
+ getConfigPath() { return getConfigFile() },
89
+ hint: '切换后重启 OpenCode 生效',
90
+ installCmd: 'npm install -g opencode-ai',
91
+ docsUrl: 'https://github.com/sst/opencode',
92
+ }