vibeusage 0.2.20 → 0.2.22
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 +306 -173
- package/README.old.md +324 -0
- package/README.zh-CN.md +304 -188
- package/package.json +32 -30
- package/src/cli.js +41 -37
- package/src/commands/activate-if-needed.js +41 -0
- package/src/commands/diagnostics.js +8 -9
- package/src/commands/doctor.js +31 -26
- package/src/commands/init.js +324 -208
- package/src/commands/status.js +86 -80
- package/src/commands/sync.js +182 -130
- package/src/commands/uninstall.js +69 -58
- package/src/lib/activation-check.js +290 -0
- package/src/lib/browser-auth.js +52 -54
- package/src/lib/claude-config.js +25 -25
- package/src/lib/cli-ui.js +35 -35
- package/src/lib/codex-config.js +40 -36
- package/src/lib/debug-flags.js +2 -2
- package/src/lib/diagnostics.js +73 -55
- package/src/lib/doctor.js +139 -132
- package/src/lib/fs.js +17 -17
- package/src/lib/gemini-config.js +44 -40
- package/src/lib/init-flow.js +16 -22
- package/src/lib/insforge-client.js +10 -10
- package/src/lib/insforge.js +9 -3
- package/src/lib/openclaw-hook.js +91 -67
- package/src/lib/openclaw-session-plugin.js +520 -0
- package/src/lib/opencode-config.js +31 -32
- package/src/lib/opencode-usage-audit.js +34 -31
- package/src/lib/progress.js +12 -13
- package/src/lib/project-usage-purge.js +23 -17
- package/src/lib/prompt.js +8 -4
- package/src/lib/rollout.js +342 -241
- package/src/lib/runtime-config.js +34 -22
- package/src/lib/subscriptions.js +94 -92
- package/src/lib/tracker-paths.js +6 -6
- package/src/lib/upload-throttle.js +35 -16
- package/src/lib/uploader.js +33 -29
- package/src/lib/vibeusage-api.js +72 -56
- package/src/lib/vibeusage-public-repo.js +41 -24
|
@@ -1,36 +1,37 @@
|
|
|
1
|
-
const os = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const fs = require(
|
|
1
|
+
const os = require("node:os");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const fs = require("node:fs/promises");
|
|
4
4
|
|
|
5
|
-
const { restoreCodexNotify, restoreEveryCodeNotify } = require(
|
|
6
|
-
const { removeClaudeHook, buildClaudeHookCommand } = require(
|
|
5
|
+
const { restoreCodexNotify, restoreEveryCodeNotify } = require("../lib/codex-config");
|
|
6
|
+
const { removeClaudeHook, buildClaudeHookCommand } = require("../lib/claude-config");
|
|
7
7
|
const {
|
|
8
8
|
resolveGeminiConfigDir,
|
|
9
9
|
resolveGeminiSettingsPath,
|
|
10
10
|
buildGeminiHookCommand,
|
|
11
|
-
removeGeminiHook
|
|
12
|
-
} = require(
|
|
13
|
-
const { resolveOpencodeConfigDir, removeOpencodePlugin } = require(
|
|
14
|
-
const { removeOpenclawHookConfig } = require(
|
|
15
|
-
const {
|
|
11
|
+
removeGeminiHook,
|
|
12
|
+
} = require("../lib/gemini-config");
|
|
13
|
+
const { resolveOpencodeConfigDir, removeOpencodePlugin } = require("../lib/opencode-config");
|
|
14
|
+
const { removeOpenclawHookConfig } = require("../lib/openclaw-hook");
|
|
15
|
+
const { removeOpenclawSessionPluginConfig } = require("../lib/openclaw-session-plugin");
|
|
16
|
+
const { resolveTrackerPaths } = require("../lib/tracker-paths");
|
|
16
17
|
|
|
17
18
|
async function cmdUninstall(argv) {
|
|
18
19
|
const opts = parseArgs(argv);
|
|
19
20
|
const home = os.homedir();
|
|
20
21
|
const { trackerDir, binDir } = await resolveTrackerPaths({ home });
|
|
21
|
-
const codexHome = process.env.CODEX_HOME || path.join(home,
|
|
22
|
-
const codexConfigPath = path.join(codexHome,
|
|
23
|
-
const codeHome = process.env.CODE_HOME || path.join(home,
|
|
24
|
-
const codeConfigPath = path.join(codeHome,
|
|
25
|
-
const claudeSettingsPath = path.join(home,
|
|
22
|
+
const codexHome = process.env.CODEX_HOME || path.join(home, ".codex");
|
|
23
|
+
const codexConfigPath = path.join(codexHome, "config.toml");
|
|
24
|
+
const codeHome = process.env.CODE_HOME || path.join(home, ".code");
|
|
25
|
+
const codeConfigPath = path.join(codeHome, "config.toml");
|
|
26
|
+
const claudeSettingsPath = path.join(home, ".claude", "settings.json");
|
|
26
27
|
const geminiConfigDir = resolveGeminiConfigDir({ home, env: process.env });
|
|
27
28
|
const geminiSettingsPath = resolveGeminiSettingsPath({ configDir: geminiConfigDir });
|
|
28
29
|
const opencodeConfigDir = resolveOpencodeConfigDir({ home, env: process.env });
|
|
29
|
-
const notifyPath = path.join(binDir,
|
|
30
|
-
const notifyOriginalPath = path.join(trackerDir,
|
|
31
|
-
const codeNotifyOriginalPath = path.join(trackerDir,
|
|
32
|
-
const codexNotifyCmd = [
|
|
33
|
-
const codeNotifyCmd = [
|
|
30
|
+
const notifyPath = path.join(binDir, "notify.cjs");
|
|
31
|
+
const notifyOriginalPath = path.join(trackerDir, "codex_notify_original.json");
|
|
32
|
+
const codeNotifyOriginalPath = path.join(trackerDir, "code_notify_original.json");
|
|
33
|
+
const codexNotifyCmd = ["/usr/bin/env", "node", notifyPath];
|
|
34
|
+
const codeNotifyCmd = ["/usr/bin/env", "node", notifyPath, "--source=every-code"];
|
|
34
35
|
const claudeHookCommand = buildClaudeHookCommand(notifyPath);
|
|
35
36
|
const geminiHookCommand = buildGeminiHookCommand(notifyPath);
|
|
36
37
|
|
|
@@ -43,85 +44,95 @@ async function cmdUninstall(argv) {
|
|
|
43
44
|
? await restoreCodexNotify({
|
|
44
45
|
codexConfigPath,
|
|
45
46
|
notifyOriginalPath,
|
|
46
|
-
notifyCmd: codexNotifyCmd
|
|
47
|
+
notifyCmd: codexNotifyCmd,
|
|
47
48
|
})
|
|
48
|
-
: { restored: false, skippedReason:
|
|
49
|
+
: { restored: false, skippedReason: "config-missing" };
|
|
49
50
|
const codeRestore = codeConfigExists
|
|
50
51
|
? await restoreEveryCodeNotify({
|
|
51
52
|
codeConfigPath,
|
|
52
53
|
notifyOriginalPath: codeNotifyOriginalPath,
|
|
53
|
-
notifyCmd: codeNotifyCmd
|
|
54
|
+
notifyCmd: codeNotifyCmd,
|
|
54
55
|
})
|
|
55
|
-
: { restored: false, skippedReason:
|
|
56
|
+
: { restored: false, skippedReason: "config-missing" };
|
|
56
57
|
const claudeRemove = claudeConfigExists
|
|
57
58
|
? await removeClaudeHook({ settingsPath: claudeSettingsPath, hookCommand: claudeHookCommand })
|
|
58
|
-
: { removed: false, skippedReason:
|
|
59
|
+
: { removed: false, skippedReason: "config-missing" };
|
|
59
60
|
const geminiRemove = geminiConfigExists
|
|
60
61
|
? await removeGeminiHook({ settingsPath: geminiSettingsPath, hookCommand: geminiHookCommand })
|
|
61
|
-
: { removed: false, skippedReason:
|
|
62
|
+
: { removed: false, skippedReason: "config-missing" };
|
|
62
63
|
const opencodeRemove = opencodeConfigExists
|
|
63
64
|
? await removeOpencodePlugin({ configDir: opencodeConfigDir })
|
|
64
|
-
: { removed: false, skippedReason:
|
|
65
|
+
: { removed: false, skippedReason: "config-missing" };
|
|
66
|
+
const openclawSessionPluginRemove = await removeOpenclawSessionPluginConfig({
|
|
67
|
+
home,
|
|
68
|
+
trackerDir,
|
|
69
|
+
env: process.env,
|
|
70
|
+
});
|
|
65
71
|
const openclawHookRemove = await removeOpenclawHookConfig({ home, trackerDir, env: process.env });
|
|
66
72
|
|
|
67
73
|
// Remove installed notify handler.
|
|
68
74
|
await fs.unlink(notifyPath).catch(() => {});
|
|
69
75
|
|
|
70
76
|
// Remove local app runtime (installed by init for notify-driven sync).
|
|
71
|
-
await fs.rm(path.join(trackerDir,
|
|
77
|
+
await fs.rm(path.join(trackerDir, "app"), { recursive: true, force: true }).catch(() => {});
|
|
72
78
|
|
|
73
79
|
if (opts.purge) {
|
|
74
|
-
await fs.rm(path.join(home,
|
|
80
|
+
await fs.rm(path.join(home, ".vibeusage"), { recursive: true, force: true }).catch(() => {});
|
|
75
81
|
}
|
|
76
82
|
|
|
77
83
|
process.stdout.write(
|
|
78
84
|
[
|
|
79
|
-
|
|
85
|
+
"Uninstalled:",
|
|
80
86
|
codexConfigExists
|
|
81
87
|
? codexRestore?.restored
|
|
82
88
|
? `- Codex notify restored: ${codexConfigPath}`
|
|
83
|
-
: codexRestore?.skippedReason ===
|
|
84
|
-
?
|
|
85
|
-
:
|
|
86
|
-
:
|
|
89
|
+
: codexRestore?.skippedReason === "no-backup-not-installed"
|
|
90
|
+
? "- Codex notify: skipped (no backup; not installed)"
|
|
91
|
+
: "- Codex notify: no change"
|
|
92
|
+
: "- Codex notify: skipped (config.toml not found)",
|
|
87
93
|
codeConfigExists
|
|
88
94
|
? codeRestore?.restored
|
|
89
95
|
? `- Every Code notify restored: ${codeConfigPath}`
|
|
90
|
-
: codeRestore?.skippedReason ===
|
|
91
|
-
?
|
|
92
|
-
:
|
|
93
|
-
:
|
|
96
|
+
: codeRestore?.skippedReason === "no-backup-not-installed"
|
|
97
|
+
? "- Every Code notify: skipped (no backup; not installed)"
|
|
98
|
+
: "- Every Code notify: no change"
|
|
99
|
+
: "- Every Code notify: skipped (config.toml not found)",
|
|
94
100
|
claudeConfigExists
|
|
95
101
|
? claudeRemove?.removed
|
|
96
102
|
? `- Claude hooks removed: ${claudeSettingsPath}`
|
|
97
|
-
: claudeRemove?.skippedReason ===
|
|
98
|
-
?
|
|
99
|
-
:
|
|
100
|
-
:
|
|
103
|
+
: claudeRemove?.skippedReason === "hook-missing"
|
|
104
|
+
? "- Claude hooks: no change"
|
|
105
|
+
: "- Claude hooks: skipped"
|
|
106
|
+
: "- Claude hooks: skipped (settings.json not found)",
|
|
101
107
|
geminiConfigExists
|
|
102
108
|
? geminiRemove?.removed
|
|
103
109
|
? `- Gemini hooks removed: ${geminiSettingsPath}`
|
|
104
|
-
: geminiRemove?.skippedReason ===
|
|
105
|
-
?
|
|
106
|
-
:
|
|
110
|
+
: geminiRemove?.skippedReason === "hook-missing"
|
|
111
|
+
? "- Gemini hooks: no change"
|
|
112
|
+
: "- Gemini hooks: skipped"
|
|
107
113
|
: `- Gemini hooks: skipped (${geminiConfigDir} not found)`,
|
|
108
114
|
opencodeConfigExists
|
|
109
115
|
? opencodeRemove?.removed
|
|
110
116
|
? `- Opencode plugin removed: ${opencodeConfigDir}`
|
|
111
|
-
: opencodeRemove?.skippedReason ===
|
|
112
|
-
?
|
|
113
|
-
: opencodeRemove?.skippedReason ===
|
|
114
|
-
?
|
|
115
|
-
:
|
|
117
|
+
: opencodeRemove?.skippedReason === "plugin-missing"
|
|
118
|
+
? "- Opencode plugin: no change"
|
|
119
|
+
: opencodeRemove?.skippedReason === "unexpected-content"
|
|
120
|
+
? "- Opencode plugin: skipped (unexpected content)"
|
|
121
|
+
: "- Opencode plugin: skipped"
|
|
116
122
|
: `- Opencode plugin: skipped (${opencodeConfigDir} not found)`,
|
|
123
|
+
openclawSessionPluginRemove?.removed
|
|
124
|
+
? `- OpenClaw session plugin removed: ${openclawSessionPluginRemove.openclawConfigPath}`
|
|
125
|
+
: openclawSessionPluginRemove?.skippedReason === "openclaw-config-missing"
|
|
126
|
+
? "- OpenClaw session plugin: skipped (openclaw config not found)"
|
|
127
|
+
: "- OpenClaw session plugin: no change",
|
|
117
128
|
openclawHookRemove?.removed
|
|
118
|
-
? `- OpenClaw hook removed: ${openclawHookRemove.openclawConfigPath}`
|
|
119
|
-
: openclawHookRemove?.skippedReason ===
|
|
120
|
-
?
|
|
121
|
-
:
|
|
122
|
-
opts.purge ? `- Purged: ${path.join(home,
|
|
123
|
-
|
|
124
|
-
].join(
|
|
129
|
+
? `- OpenClaw hook (legacy) removed: ${openclawHookRemove.openclawConfigPath}`
|
|
130
|
+
: openclawHookRemove?.skippedReason === "openclaw-config-missing"
|
|
131
|
+
? "- OpenClaw hook (legacy): skipped (openclaw config not found)"
|
|
132
|
+
: "- OpenClaw hook (legacy): no change",
|
|
133
|
+
opts.purge ? `- Purged: ${path.join(home, ".vibeusage")}` : "- Purge: skipped (use --purge)",
|
|
134
|
+
"",
|
|
135
|
+
].join("\n"),
|
|
125
136
|
);
|
|
126
137
|
}
|
|
127
138
|
|
|
@@ -129,7 +140,7 @@ function parseArgs(argv) {
|
|
|
129
140
|
const out = { purge: false };
|
|
130
141
|
for (let i = 0; i < argv.length; i++) {
|
|
131
142
|
const a = argv[i];
|
|
132
|
-
if (a ===
|
|
143
|
+
if (a === "--purge") out.purge = true;
|
|
133
144
|
else throw new Error(`Unknown option: ${a}`);
|
|
134
145
|
}
|
|
135
146
|
return out;
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
const os = require("node:os");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const fs = require("node:fs/promises");
|
|
4
|
+
const { readJson } = require("./fs");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 跨 AI CLI 自动激活检测
|
|
8
|
+
* 在 vibeusage 各种命令执行时顺带检测并完成配置
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const AI_CLIS = [
|
|
12
|
+
{
|
|
13
|
+
name: "codex",
|
|
14
|
+
displayName: "Codex",
|
|
15
|
+
checkInstalled: checkCodexInstalled,
|
|
16
|
+
checkConfigured: checkCodexConfigured,
|
|
17
|
+
configure: configureCodex,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "claude-code",
|
|
21
|
+
displayName: "Claude Code",
|
|
22
|
+
checkInstalled: checkClaudeCodeInstalled,
|
|
23
|
+
checkConfigured: checkClaudeCodeConfigured,
|
|
24
|
+
configure: configureClaudeCode,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
name: "opencode",
|
|
28
|
+
displayName: "OpenCode",
|
|
29
|
+
checkInstalled: checkOpencodeInstalled,
|
|
30
|
+
checkConfigured: checkOpencodeConfigured,
|
|
31
|
+
configure: configureOpencode,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: "every-code",
|
|
35
|
+
displayName: "Every Code",
|
|
36
|
+
checkInstalled: checkEveryCodeInstalled,
|
|
37
|
+
checkConfigured: checkEveryCodeConfigured,
|
|
38
|
+
configure: configureEveryCode,
|
|
39
|
+
},
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 检测所有 AI CLI 并自动配置未完成的
|
|
44
|
+
* @param {Object} options
|
|
45
|
+
* @param {string} options.home - home目录
|
|
46
|
+
* @param {boolean} options.silent - 是否静默模式
|
|
47
|
+
* @param {boolean} options.autoConfigure - 是否自动配置(否则仅提示)
|
|
48
|
+
*/
|
|
49
|
+
async function checkAndActivate({ home = os.homedir(), silent = true, autoConfigure = true } = {}) {
|
|
50
|
+
const results = [];
|
|
51
|
+
|
|
52
|
+
for (const cli of AI_CLIS) {
|
|
53
|
+
try {
|
|
54
|
+
const isInstalled = await cli.checkInstalled({ home });
|
|
55
|
+
if (!isInstalled) continue;
|
|
56
|
+
|
|
57
|
+
const isConfigured = await cli.checkConfigured({ home });
|
|
58
|
+
if (isConfigured) continue;
|
|
59
|
+
|
|
60
|
+
// 发现已安装但未配置的 CLI
|
|
61
|
+
if (autoConfigure) {
|
|
62
|
+
const success = await cli.configure({ home, silent });
|
|
63
|
+
results.push({
|
|
64
|
+
name: cli.name,
|
|
65
|
+
displayName: cli.displayName,
|
|
66
|
+
action: success ? "configured" : "failed",
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!silent && success) {
|
|
70
|
+
console.log(`✅ 已自动配置 ${cli.displayName} 集成`);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
results.push({
|
|
74
|
+
name: cli.name,
|
|
75
|
+
displayName: cli.displayName,
|
|
76
|
+
action: "pending",
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (!silent) {
|
|
80
|
+
console.log(`⏳ 检测到 ${cli.displayName} 未配置,运行 'vibeusage init' 以配置`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
// 静默忽略错误,不影响主流程
|
|
85
|
+
if (!silent) {
|
|
86
|
+
console.error(`检查 ${cli.displayName} 失败:`, err.message);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return results;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ===== Codex 检测与配置 =====
|
|
95
|
+
|
|
96
|
+
async function checkCodexInstalled({ home }) {
|
|
97
|
+
const configPath = path.join(home, ".codex", "config.toml");
|
|
98
|
+
try {
|
|
99
|
+
await fs.access(configPath);
|
|
100
|
+
return true;
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function checkCodexConfigured({ home }) {
|
|
107
|
+
const configPath = path.join(home, ".codex", "config.toml");
|
|
108
|
+
try {
|
|
109
|
+
const content = await fs.readFile(configPath, "utf8");
|
|
110
|
+
// 检查是否已配置 notify
|
|
111
|
+
return content.includes("vibeusage") || content.includes("notify");
|
|
112
|
+
} catch {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function configureCodex({ home, silent }) {
|
|
118
|
+
try {
|
|
119
|
+
// 使用现有的 codex-config 模块
|
|
120
|
+
const { upsertCodexNotify } = require("./codex-config");
|
|
121
|
+
const notifyCmd = path.join(home, ".vibeusage", "bin", "notify.cjs");
|
|
122
|
+
const codexConfigPath = path.join(home, ".codex", "config.toml");
|
|
123
|
+
const notifyOriginalPath = path.join(home, ".vibeusage", "backups", "codex-notify-original.json");
|
|
124
|
+
|
|
125
|
+
await upsertCodexNotify({
|
|
126
|
+
codexConfigPath,
|
|
127
|
+
notifyCmd,
|
|
128
|
+
notifyOriginalPath,
|
|
129
|
+
});
|
|
130
|
+
return true;
|
|
131
|
+
} catch (err) {
|
|
132
|
+
if (!silent) console.error("配置 Codex 失败:", err.message);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ===== Claude Code 检测与配置 =====
|
|
138
|
+
|
|
139
|
+
async function checkClaudeCodeInstalled({ home }) {
|
|
140
|
+
const settingsPath = path.join(home, ".claude", "settings.json");
|
|
141
|
+
try {
|
|
142
|
+
await fs.access(settingsPath);
|
|
143
|
+
return true;
|
|
144
|
+
} catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function checkClaudeCodeConfigured({ home }) {
|
|
150
|
+
const settingsPath = path.join(home, ".claude", "settings.json");
|
|
151
|
+
try {
|
|
152
|
+
const settings = await readJson(settingsPath);
|
|
153
|
+
// 检查是否已有 vibeusage 相关的 hook
|
|
154
|
+
const hooks = settings?.hooks?.SessionStart || [];
|
|
155
|
+
return hooks.some(h =>
|
|
156
|
+
h.hooks?.some(hook => hook.command?.includes("vibeusage"))
|
|
157
|
+
);
|
|
158
|
+
} catch {
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function configureClaudeCode({ home, silent }) {
|
|
164
|
+
try {
|
|
165
|
+
const settingsPath = path.join(home, ".claude", "settings.json");
|
|
166
|
+
const settings = (await readJson(settingsPath)) || {};
|
|
167
|
+
|
|
168
|
+
// 添加 SessionStart hook
|
|
169
|
+
if (!settings.hooks) settings.hooks = {};
|
|
170
|
+
if (!settings.hooks.SessionStart) settings.hooks.SessionStart = [];
|
|
171
|
+
|
|
172
|
+
// 检查是否已存在
|
|
173
|
+
const exists = settings.hooks.SessionStart.some(h =>
|
|
174
|
+
h.matcher === "startup" &&
|
|
175
|
+
h.hooks?.some(hook => hook.command?.includes("vibeusage activate-if-needed"))
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
if (!exists) {
|
|
179
|
+
settings.hooks.SessionStart.push({
|
|
180
|
+
matcher: "startup",
|
|
181
|
+
hooks: [{
|
|
182
|
+
type: "command",
|
|
183
|
+
command: "vibeusage activate-if-needed --silent 2>/dev/null || true"
|
|
184
|
+
}]
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2) + "\n", "utf8");
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
if (!silent) console.error("配置 Claude Code 失败:", err.message);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ===== OpenCode 检测与配置 =====
|
|
197
|
+
|
|
198
|
+
async function checkOpencodeInstalled({ home }) {
|
|
199
|
+
const configPath = path.join(home, ".config", "opencode", "opencode.json");
|
|
200
|
+
try {
|
|
201
|
+
await fs.access(configPath);
|
|
202
|
+
return true;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function checkOpencodeConfigured({ home }) {
|
|
209
|
+
const pluginDir = path.join(home, ".config", "opencode", "plugins");
|
|
210
|
+
try {
|
|
211
|
+
const files = await fs.readdir(pluginDir);
|
|
212
|
+
return files.some(f => f.includes("vibeusage"));
|
|
213
|
+
} catch {
|
|
214
|
+
return false;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
async function configureOpencode({ home, silent }) {
|
|
219
|
+
try {
|
|
220
|
+
const pluginDir = path.join(home, ".config", "opencode", "plugins");
|
|
221
|
+
await fs.mkdir(pluginDir, { recursive: true });
|
|
222
|
+
|
|
223
|
+
const pluginPath = path.join(pluginDir, "vibeusage-activation.js");
|
|
224
|
+
const pluginCode = `export const VibeusageActivation = async ({ $ }) => {
|
|
225
|
+
return {
|
|
226
|
+
"session.created": async () => {
|
|
227
|
+
await $'vibeusage activate-if-needed --silent'.quiet().nothrow();
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
};`;
|
|
231
|
+
|
|
232
|
+
await fs.writeFile(pluginPath, pluginCode, "utf8");
|
|
233
|
+
return true;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
if (!silent) console.error("配置 OpenCode 失败:", err.message);
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ===== Every Code 检测与配置 =====
|
|
241
|
+
|
|
242
|
+
async function checkEveryCodeInstalled({ home }) {
|
|
243
|
+
const configPath = path.join(home, ".code", "config.toml");
|
|
244
|
+
try {
|
|
245
|
+
await fs.access(configPath);
|
|
246
|
+
return true;
|
|
247
|
+
} catch {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async function checkEveryCodeConfigured({ home }) {
|
|
253
|
+
const configPath = path.join(home, ".code", "config.toml");
|
|
254
|
+
try {
|
|
255
|
+
const content = await fs.readFile(configPath, "utf8");
|
|
256
|
+
return content.includes("vibeusage");
|
|
257
|
+
} catch {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
async function configureEveryCode({ home, silent }) {
|
|
263
|
+
try {
|
|
264
|
+
// Every Code 配置类似 Codex
|
|
265
|
+
const configPath = path.join(home, ".code", "config.toml");
|
|
266
|
+
let content = "";
|
|
267
|
+
try {
|
|
268
|
+
content = await fs.readFile(configPath, "utf8");
|
|
269
|
+
} catch {
|
|
270
|
+
content = "";
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
const notifyCmd = path.join(home, ".vibeusage", "bin", "notify.cjs");
|
|
274
|
+
const notifyLine = `notify = ["/usr/bin/env", "node", "${notifyCmd}"]`;
|
|
275
|
+
|
|
276
|
+
if (!content.includes("vibeusage")) {
|
|
277
|
+
content = content.trim() + "\n\n# vibeusage integration\n" + notifyLine + "\n";
|
|
278
|
+
await fs.writeFile(configPath, content, "utf8");
|
|
279
|
+
}
|
|
280
|
+
return true;
|
|
281
|
+
} catch (err) {
|
|
282
|
+
if (!silent) console.error("配置 Every Code 失败:", err.message);
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
module.exports = {
|
|
288
|
+
checkAndActivate,
|
|
289
|
+
AI_CLIS,
|
|
290
|
+
};
|