vibeusage 0.2.21 → 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 -32
- 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 +285 -218
- package/src/commands/status.js +86 -83
- package/src/commands/sync.js +182 -130
- package/src/commands/uninstall.js +66 -62
- 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 +70 -57
- 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 +89 -66
- package/src/lib/openclaw-session-plugin.js +116 -92
- 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,37 +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 { removeOpenclawSessionPluginConfig } = require(
|
|
16
|
-
const { resolveTrackerPaths } = require(
|
|
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");
|
|
17
17
|
|
|
18
18
|
async function cmdUninstall(argv) {
|
|
19
19
|
const opts = parseArgs(argv);
|
|
20
20
|
const home = os.homedir();
|
|
21
21
|
const { trackerDir, binDir } = await resolveTrackerPaths({ home });
|
|
22
|
-
const codexHome = process.env.CODEX_HOME || path.join(home,
|
|
23
|
-
const codexConfigPath = path.join(codexHome,
|
|
24
|
-
const codeHome = process.env.CODE_HOME || path.join(home,
|
|
25
|
-
const codeConfigPath = path.join(codeHome,
|
|
26
|
-
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");
|
|
27
27
|
const geminiConfigDir = resolveGeminiConfigDir({ home, env: process.env });
|
|
28
28
|
const geminiSettingsPath = resolveGeminiSettingsPath({ configDir: geminiConfigDir });
|
|
29
29
|
const opencodeConfigDir = resolveOpencodeConfigDir({ home, env: process.env });
|
|
30
|
-
const notifyPath = path.join(binDir,
|
|
31
|
-
const notifyOriginalPath = path.join(trackerDir,
|
|
32
|
-
const codeNotifyOriginalPath = path.join(trackerDir,
|
|
33
|
-
const codexNotifyCmd = [
|
|
34
|
-
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"];
|
|
35
35
|
const claudeHookCommand = buildClaudeHookCommand(notifyPath);
|
|
36
36
|
const geminiHookCommand = buildGeminiHookCommand(notifyPath);
|
|
37
37
|
|
|
@@ -44,91 +44,95 @@ async function cmdUninstall(argv) {
|
|
|
44
44
|
? await restoreCodexNotify({
|
|
45
45
|
codexConfigPath,
|
|
46
46
|
notifyOriginalPath,
|
|
47
|
-
notifyCmd: codexNotifyCmd
|
|
47
|
+
notifyCmd: codexNotifyCmd,
|
|
48
48
|
})
|
|
49
|
-
: { restored: false, skippedReason:
|
|
49
|
+
: { restored: false, skippedReason: "config-missing" };
|
|
50
50
|
const codeRestore = codeConfigExists
|
|
51
51
|
? await restoreEveryCodeNotify({
|
|
52
52
|
codeConfigPath,
|
|
53
53
|
notifyOriginalPath: codeNotifyOriginalPath,
|
|
54
|
-
notifyCmd: codeNotifyCmd
|
|
54
|
+
notifyCmd: codeNotifyCmd,
|
|
55
55
|
})
|
|
56
|
-
: { restored: false, skippedReason:
|
|
56
|
+
: { restored: false, skippedReason: "config-missing" };
|
|
57
57
|
const claudeRemove = claudeConfigExists
|
|
58
58
|
? await removeClaudeHook({ settingsPath: claudeSettingsPath, hookCommand: claudeHookCommand })
|
|
59
|
-
: { removed: false, skippedReason:
|
|
59
|
+
: { removed: false, skippedReason: "config-missing" };
|
|
60
60
|
const geminiRemove = geminiConfigExists
|
|
61
61
|
? await removeGeminiHook({ settingsPath: geminiSettingsPath, hookCommand: geminiHookCommand })
|
|
62
|
-
: { removed: false, skippedReason:
|
|
62
|
+
: { removed: false, skippedReason: "config-missing" };
|
|
63
63
|
const opencodeRemove = opencodeConfigExists
|
|
64
64
|
? await removeOpencodePlugin({ configDir: opencodeConfigDir })
|
|
65
|
-
: { removed: false, skippedReason:
|
|
66
|
-
const openclawSessionPluginRemove = await removeOpenclawSessionPluginConfig({
|
|
65
|
+
: { removed: false, skippedReason: "config-missing" };
|
|
66
|
+
const openclawSessionPluginRemove = await removeOpenclawSessionPluginConfig({
|
|
67
|
+
home,
|
|
68
|
+
trackerDir,
|
|
69
|
+
env: process.env,
|
|
70
|
+
});
|
|
67
71
|
const openclawHookRemove = await removeOpenclawHookConfig({ home, trackerDir, env: process.env });
|
|
68
72
|
|
|
69
73
|
// Remove installed notify handler.
|
|
70
74
|
await fs.unlink(notifyPath).catch(() => {});
|
|
71
75
|
|
|
72
76
|
// Remove local app runtime (installed by init for notify-driven sync).
|
|
73
|
-
await fs.rm(path.join(trackerDir,
|
|
77
|
+
await fs.rm(path.join(trackerDir, "app"), { recursive: true, force: true }).catch(() => {});
|
|
74
78
|
|
|
75
79
|
if (opts.purge) {
|
|
76
|
-
await fs.rm(path.join(home,
|
|
80
|
+
await fs.rm(path.join(home, ".vibeusage"), { recursive: true, force: true }).catch(() => {});
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
process.stdout.write(
|
|
80
84
|
[
|
|
81
|
-
|
|
85
|
+
"Uninstalled:",
|
|
82
86
|
codexConfigExists
|
|
83
87
|
? codexRestore?.restored
|
|
84
88
|
? `- Codex notify restored: ${codexConfigPath}`
|
|
85
|
-
: codexRestore?.skippedReason ===
|
|
86
|
-
?
|
|
87
|
-
:
|
|
88
|
-
:
|
|
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)",
|
|
89
93
|
codeConfigExists
|
|
90
94
|
? codeRestore?.restored
|
|
91
95
|
? `- Every Code notify restored: ${codeConfigPath}`
|
|
92
|
-
: codeRestore?.skippedReason ===
|
|
93
|
-
?
|
|
94
|
-
:
|
|
95
|
-
:
|
|
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)",
|
|
96
100
|
claudeConfigExists
|
|
97
101
|
? claudeRemove?.removed
|
|
98
102
|
? `- Claude hooks removed: ${claudeSettingsPath}`
|
|
99
|
-
: claudeRemove?.skippedReason ===
|
|
100
|
-
?
|
|
101
|
-
:
|
|
102
|
-
:
|
|
103
|
+
: claudeRemove?.skippedReason === "hook-missing"
|
|
104
|
+
? "- Claude hooks: no change"
|
|
105
|
+
: "- Claude hooks: skipped"
|
|
106
|
+
: "- Claude hooks: skipped (settings.json not found)",
|
|
103
107
|
geminiConfigExists
|
|
104
108
|
? geminiRemove?.removed
|
|
105
109
|
? `- Gemini hooks removed: ${geminiSettingsPath}`
|
|
106
|
-
: geminiRemove?.skippedReason ===
|
|
107
|
-
?
|
|
108
|
-
:
|
|
110
|
+
: geminiRemove?.skippedReason === "hook-missing"
|
|
111
|
+
? "- Gemini hooks: no change"
|
|
112
|
+
: "- Gemini hooks: skipped"
|
|
109
113
|
: `- Gemini hooks: skipped (${geminiConfigDir} not found)`,
|
|
110
114
|
opencodeConfigExists
|
|
111
115
|
? opencodeRemove?.removed
|
|
112
116
|
? `- Opencode plugin removed: ${opencodeConfigDir}`
|
|
113
|
-
: opencodeRemove?.skippedReason ===
|
|
114
|
-
?
|
|
115
|
-
: opencodeRemove?.skippedReason ===
|
|
116
|
-
?
|
|
117
|
-
:
|
|
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"
|
|
118
122
|
: `- Opencode plugin: skipped (${opencodeConfigDir} not found)`,
|
|
119
123
|
openclawSessionPluginRemove?.removed
|
|
120
124
|
? `- OpenClaw session plugin removed: ${openclawSessionPluginRemove.openclawConfigPath}`
|
|
121
|
-
: openclawSessionPluginRemove?.skippedReason ===
|
|
122
|
-
?
|
|
123
|
-
:
|
|
125
|
+
: openclawSessionPluginRemove?.skippedReason === "openclaw-config-missing"
|
|
126
|
+
? "- OpenClaw session plugin: skipped (openclaw config not found)"
|
|
127
|
+
: "- OpenClaw session plugin: no change",
|
|
124
128
|
openclawHookRemove?.removed
|
|
125
129
|
? `- OpenClaw hook (legacy) removed: ${openclawHookRemove.openclawConfigPath}`
|
|
126
|
-
: openclawHookRemove?.skippedReason ===
|
|
127
|
-
?
|
|
128
|
-
:
|
|
129
|
-
opts.purge ? `- Purged: ${path.join(home,
|
|
130
|
-
|
|
131
|
-
].join(
|
|
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"),
|
|
132
136
|
);
|
|
133
137
|
}
|
|
134
138
|
|
|
@@ -136,7 +140,7 @@ function parseArgs(argv) {
|
|
|
136
140
|
const out = { purge: false };
|
|
137
141
|
for (let i = 0; i < argv.length; i++) {
|
|
138
142
|
const a = argv[i];
|
|
139
|
-
if (a ===
|
|
143
|
+
if (a === "--purge") out.purge = true;
|
|
140
144
|
else throw new Error(`Unknown option: ${a}`);
|
|
141
145
|
}
|
|
142
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
|
+
};
|