vibeusage 0.2.23 → 0.3.1
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 +25 -20
- package/README.zh-CN.md +7 -2
- package/node_modules/@insforge/sdk/LICENSE +201 -201
- package/node_modules/@insforge/sdk/README.md +326 -259
- package/node_modules/@insforge/sdk/dist/index.d.mts +377 -182
- package/node_modules/@insforge/sdk/dist/index.d.ts +377 -182
- package/node_modules/@insforge/sdk/dist/index.js +1172 -677
- package/node_modules/@insforge/sdk/dist/index.js.map +1 -1
- package/node_modules/@insforge/sdk/dist/index.mjs +1171 -677
- package/node_modules/@insforge/sdk/dist/index.mjs.map +1 -1
- package/node_modules/@insforge/sdk/package.json +68 -68
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.d.ts +1120 -43
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.js +179 -5
- package/node_modules/@insforge/shared-schemas/dist/ai-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.d.ts +25 -25
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.js +2 -2
- package/node_modules/@insforge/shared-schemas/dist/ai.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.d.ts +197 -51
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.js +87 -23
- package/node_modules/@insforge/shared-schemas/dist/auth-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.d.ts +32 -3
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.js +21 -3
- package/node_modules/@insforge/shared-schemas/dist/auth.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.d.ts +380 -0
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js +74 -0
- package/node_modules/@insforge/shared-schemas/dist/cloud-events.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/database-api.schema.d.ts +13 -13
- package/node_modules/@insforge/shared-schemas/dist/database-api.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/database-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.d.ts +735 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.js +209 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.d.ts +37 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.js +25 -0
- package/node_modules/@insforge/shared-schemas/dist/deployments.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.d.ts +5 -1
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.js +34 -4
- package/node_modules/@insforge/shared-schemas/dist/docs.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/email-api.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/email-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.d.ts +186 -6
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.js +21 -2
- package/node_modules/@insforge/shared-schemas/dist/functions-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions.schema.d.ts +5 -5
- package/node_modules/@insforge/shared-schemas/dist/functions.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/functions.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/index.d.ts +24 -18
- package/node_modules/@insforge/shared-schemas/dist/index.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/index.js +24 -18
- package/node_modules/@insforge/shared-schemas/dist/index.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs-api.schema.js +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.d.ts +43 -0
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.js +11 -0
- package/node_modules/@insforge/shared-schemas/dist/logs.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.d.ts +229 -172
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.js +27 -7
- package/node_modules/@insforge/shared-schemas/dist/metadata.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.d.ts +51 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.js +31 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.d.ts +31 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.js +12 -0
- package/node_modules/@insforge/shared-schemas/dist/rate-limit.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.d.ts +39 -20
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.js +5 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.d.ts +12 -4
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.js +6 -0
- package/node_modules/@insforge/shared-schemas/dist/realtime.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.d.ts +287 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js +81 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.d.ts +77 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.js +36 -0
- package/node_modules/@insforge/shared-schemas/dist/schedules.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.d.ts +113 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.js +31 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets-api.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.d.ts +31 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.d.ts.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.js +13 -0
- package/node_modules/@insforge/shared-schemas/dist/secrets.schema.js.map +1 -0
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.d.ts +27 -2
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.js +9 -1
- package/node_modules/@insforge/shared-schemas/dist/storage-api.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.d.ts +17 -0
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.d.ts.map +1 -1
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.js +6 -0
- package/node_modules/@insforge/shared-schemas/dist/storage.schema.js.map +1 -1
- package/node_modules/@insforge/shared-schemas/package.json +2 -1
- package/package.json +5 -6
- package/src/cli.js +2 -2
- package/src/commands/init.js +20 -362
- package/src/commands/status.js +58 -51
- package/src/commands/sync.js +37 -25
- package/src/commands/uninstall.js +121 -104
- package/src/lib/claude-config.js +130 -35
- package/src/lib/diagnostics.js +88 -57
- package/src/lib/doctor.js +50 -0
- package/src/lib/insforge-client.js +13 -9
- package/src/lib/integrations/claude.js +106 -0
- package/src/lib/integrations/codex.js +88 -0
- package/src/lib/integrations/context.js +76 -0
- package/src/lib/integrations/every-code.js +88 -0
- package/src/lib/integrations/gemini.js +86 -0
- package/src/lib/integrations/index.js +85 -0
- package/src/lib/integrations/openclaw-legacy.js +123 -0
- package/src/lib/integrations/openclaw-session.js +132 -0
- package/src/lib/integrations/opencode.js +86 -0
- package/src/lib/integrations/utils.js +39 -0
- package/src/lib/opencode-sqlite.js +113 -0
- package/src/lib/opencode-usage-audit.js +3 -2
- package/src/lib/rollout.js +227 -1
- package/src/lib/runtime-config.js +7 -5
- package/src/lib/vibeusage-api.js +11 -7
- package/src/shared/copy-registry.cjs +142 -0
- package/src/shared/copy-registry.cjs.d.ts +33 -0
- package/src/shared/runtime-defaults.cjs +11 -0
- package/src/shared/runtime-defaults.cjs.d.ts +3 -0
- package/src/shared/vibeusage-function-contract.cjs +34 -0
- package/src/shared/vibeusage-function-contract.cjs.d.ts +4 -0
- package/src/commands/activate-if-needed.js +0 -41
- package/src/lib/activation-check.js +0 -341
package/src/commands/sync.js
CHANGED
|
@@ -68,9 +68,11 @@ async function cmdSync(argv) {
|
|
|
68
68
|
const xdgDataHome = process.env.XDG_DATA_HOME || path.join(home, ".local", "share");
|
|
69
69
|
const opencodeHome = process.env.OPENCODE_HOME || path.join(xdgDataHome, "opencode");
|
|
70
70
|
const opencodeStorageDir = path.join(opencodeHome, "storage");
|
|
71
|
+
const opencodeDbPath = path.join(opencodeHome, "opencode.db");
|
|
71
72
|
|
|
72
|
-
// OpenClaw
|
|
73
|
-
// We still parse all regular sources so model/source attribution stays
|
|
73
|
+
// OpenClaw session-plugin integration: allow a plugin-triggered sync to request incremental parsing
|
|
74
|
+
// for a single session jsonl. We still parse all regular sources so model/source attribution stays
|
|
75
|
+
// complete (e.g. Kimi sessions).
|
|
74
76
|
const openclawSignal = opts.fromOpenclaw
|
|
75
77
|
? resolveOpenclawSignal({ home, env: process.env })
|
|
76
78
|
: null;
|
|
@@ -119,7 +121,7 @@ async function cmdSync(argv) {
|
|
|
119
121
|
|
|
120
122
|
let openclawResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
121
123
|
if (openclawFiles.length > 0) {
|
|
122
|
-
// Only runs when explicitly triggered by OpenClaw
|
|
124
|
+
// Only runs when explicitly triggered by OpenClaw session-plugin events.
|
|
123
125
|
openclawResult = await parseOpenclawIncremental({
|
|
124
126
|
sessionFiles: openclawFiles,
|
|
125
127
|
cursors,
|
|
@@ -194,29 +196,28 @@ async function cmdSync(argv) {
|
|
|
194
196
|
|
|
195
197
|
const opencodeFiles = await listOpencodeMessageFiles(opencodeStorageDir);
|
|
196
198
|
let opencodeResult = { filesProcessed: 0, eventsAggregated: 0, bucketsQueued: 0 };
|
|
197
|
-
if (opencodeFiles.length > 0) {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
opencodeResult = await parseOpencodeIncremental({
|
|
204
|
-
messageFiles: opencodeFiles,
|
|
205
|
-
cursors,
|
|
206
|
-
queuePath,
|
|
207
|
-
projectQueuePath,
|
|
208
|
-
onProgress: (p) => {
|
|
209
|
-
if (!progress?.enabled) return;
|
|
210
|
-
const pct = p.total > 0 ? p.index / p.total : 1;
|
|
211
|
-
progress.update(
|
|
212
|
-
`Parsing Opencode ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(
|
|
213
|
-
p.total,
|
|
214
|
-
)} files | buckets ${formatNumber(p.bucketsQueued)}`,
|
|
215
|
-
);
|
|
216
|
-
},
|
|
217
|
-
source: "opencode",
|
|
218
|
-
});
|
|
199
|
+
if (progress?.enabled && opencodeFiles.length > 0) {
|
|
200
|
+
progress.start(
|
|
201
|
+
`Parsing Opencode ${renderBar(0)} 0/${formatNumber(opencodeFiles.length)} files | buckets 0`,
|
|
202
|
+
);
|
|
219
203
|
}
|
|
204
|
+
opencodeResult = await parseOpencodeIncremental({
|
|
205
|
+
messageFiles: opencodeFiles,
|
|
206
|
+
opencodeDbPath,
|
|
207
|
+
cursors,
|
|
208
|
+
queuePath,
|
|
209
|
+
projectQueuePath,
|
|
210
|
+
onProgress: (p) => {
|
|
211
|
+
if (!progress?.enabled) return;
|
|
212
|
+
const pct = p.total > 0 ? p.index / p.total : 1;
|
|
213
|
+
progress.update(
|
|
214
|
+
`Parsing Opencode ${renderBar(pct)} ${formatNumber(p.index)}/${formatNumber(
|
|
215
|
+
p.total,
|
|
216
|
+
)} files | buckets ${formatNumber(p.bucketsQueued)}`,
|
|
217
|
+
);
|
|
218
|
+
},
|
|
219
|
+
source: "opencode",
|
|
220
|
+
});
|
|
220
221
|
|
|
221
222
|
if (cursors?.projectHourly?.projects && projectQueuePath && projectQueueStatePath) {
|
|
222
223
|
for (const [projectKey, meta] of Object.entries(cursors.projectHourly.projects)) {
|
|
@@ -414,6 +415,17 @@ async function cmdSync(argv) {
|
|
|
414
415
|
.filter(Boolean)
|
|
415
416
|
.join("\n"),
|
|
416
417
|
);
|
|
418
|
+
if (
|
|
419
|
+
opencodeResult.sqliteStatus === "missing-sqlite3" ||
|
|
420
|
+
opencodeResult.sqliteStatus === "query-failed"
|
|
421
|
+
) {
|
|
422
|
+
const reason = opencodeResult.sqliteErrorCode
|
|
423
|
+
? `${opencodeResult.sqliteStatus} (${opencodeResult.sqliteErrorCode})`
|
|
424
|
+
: opencodeResult.sqliteStatus;
|
|
425
|
+
process.stderr.write(
|
|
426
|
+
`Warning: OpenCode usage may be incomplete because the SQLite reader is ${reason}. Run \`vibeusage doctor\` for details.\n`,
|
|
427
|
+
);
|
|
428
|
+
}
|
|
417
429
|
}
|
|
418
430
|
} finally {
|
|
419
431
|
progress?.stop();
|
|
@@ -2,73 +2,27 @@ const os = require("node:os");
|
|
|
2
2
|
const path = require("node:path");
|
|
3
3
|
const fs = require("node:fs/promises");
|
|
4
4
|
|
|
5
|
-
const { restoreCodexNotify, restoreEveryCodeNotify } = require("../lib/codex-config");
|
|
6
|
-
const { removeClaudeHook, buildClaudeHookCommand } = require("../lib/claude-config");
|
|
7
|
-
const {
|
|
8
|
-
resolveGeminiConfigDir,
|
|
9
|
-
resolveGeminiSettingsPath,
|
|
10
|
-
buildGeminiHookCommand,
|
|
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
5
|
const { resolveTrackerPaths } = require("../lib/tracker-paths");
|
|
6
|
+
const { createIntegrationContext, uninstallIntegrations } = require("../lib/integrations");
|
|
17
7
|
|
|
18
8
|
async function cmdUninstall(argv) {
|
|
19
9
|
const opts = parseArgs(argv);
|
|
20
10
|
const home = os.homedir();
|
|
21
11
|
const { trackerDir, binDir } = await resolveTrackerPaths({ 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
|
-
const geminiConfigDir = resolveGeminiConfigDir({ home, env: process.env });
|
|
28
|
-
const geminiSettingsPath = resolveGeminiSettingsPath({ configDir: geminiConfigDir });
|
|
29
|
-
const opencodeConfigDir = resolveOpencodeConfigDir({ home, env: process.env });
|
|
30
12
|
const notifyPath = path.join(binDir, "notify.cjs");
|
|
31
|
-
const
|
|
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
|
-
const claudeHookCommand = buildClaudeHookCommand(notifyPath);
|
|
36
|
-
const geminiHookCommand = buildGeminiHookCommand(notifyPath);
|
|
37
|
-
|
|
38
|
-
const codexConfigExists = await isFile(codexConfigPath);
|
|
39
|
-
const codeConfigExists = await isFile(codeConfigPath);
|
|
40
|
-
const claudeConfigExists = await isFile(claudeSettingsPath);
|
|
41
|
-
const geminiConfigExists = await isDir(geminiConfigDir);
|
|
42
|
-
const opencodeConfigExists = await isDir(opencodeConfigDir);
|
|
43
|
-
const codexRestore = codexConfigExists
|
|
44
|
-
? await restoreCodexNotify({
|
|
45
|
-
codexConfigPath,
|
|
46
|
-
notifyOriginalPath,
|
|
47
|
-
notifyCmd: codexNotifyCmd,
|
|
48
|
-
})
|
|
49
|
-
: { restored: false, skippedReason: "config-missing" };
|
|
50
|
-
const codeRestore = codeConfigExists
|
|
51
|
-
? await restoreEveryCodeNotify({
|
|
52
|
-
codeConfigPath,
|
|
53
|
-
notifyOriginalPath: codeNotifyOriginalPath,
|
|
54
|
-
notifyCmd: codeNotifyCmd,
|
|
55
|
-
})
|
|
56
|
-
: { restored: false, skippedReason: "config-missing" };
|
|
57
|
-
const claudeRemove = claudeConfigExists
|
|
58
|
-
? await removeClaudeHook({ settingsPath: claudeSettingsPath, hookCommand: claudeHookCommand })
|
|
59
|
-
: { removed: false, skippedReason: "config-missing" };
|
|
60
|
-
const geminiRemove = geminiConfigExists
|
|
61
|
-
? await removeGeminiHook({ settingsPath: geminiSettingsPath, hookCommand: geminiHookCommand })
|
|
62
|
-
: { removed: false, skippedReason: "config-missing" };
|
|
63
|
-
const opencodeRemove = opencodeConfigExists
|
|
64
|
-
? await removeOpencodePlugin({ configDir: opencodeConfigDir })
|
|
65
|
-
: { removed: false, skippedReason: "config-missing" };
|
|
66
|
-
const openclawSessionPluginRemove = await removeOpenclawSessionPluginConfig({
|
|
13
|
+
const integrationContext = await createIntegrationContext({
|
|
67
14
|
home,
|
|
68
|
-
trackerDir,
|
|
69
15
|
env: process.env,
|
|
16
|
+
trackerPaths: { trackerDir, binDir, rootDir: path.dirname(trackerDir) },
|
|
17
|
+
notifyPath,
|
|
70
18
|
});
|
|
71
|
-
const
|
|
19
|
+
const codexConfigExists = await isFile(integrationContext.codex.configPath);
|
|
20
|
+
const codeConfigExists = await isFile(integrationContext.everyCode.configPath);
|
|
21
|
+
const claudeConfigExists = await isFile(integrationContext.claude.settingsPath);
|
|
22
|
+
const geminiConfigExists = await isDir(integrationContext.gemini.configDir);
|
|
23
|
+
const opencodeConfigExists = await isDir(integrationContext.opencode.configDir);
|
|
24
|
+
const integrationResults = await uninstallIntegrations(integrationContext);
|
|
25
|
+
const resultByName = new Map(integrationResults.map((result) => [result.name, result]));
|
|
72
26
|
|
|
73
27
|
// Remove installed notify handler.
|
|
74
28
|
await fs.unlink(notifyPath).catch(() => {});
|
|
@@ -83,53 +37,73 @@ async function cmdUninstall(argv) {
|
|
|
83
37
|
process.stdout.write(
|
|
84
38
|
[
|
|
85
39
|
"Uninstalled:",
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
: "- Codex notify:
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
: "
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
:
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
40
|
+
renderRestoreLine({
|
|
41
|
+
exists: codexConfigExists,
|
|
42
|
+
result: resultByName.get("codex"),
|
|
43
|
+
missingText: "- Codex notify: skipped (config.toml not found)",
|
|
44
|
+
restoredText: (result) =>
|
|
45
|
+
`- Codex notify restored: ${result.detail || integrationContext.codex.configPath}`,
|
|
46
|
+
noChangeText: "- Codex notify: no change",
|
|
47
|
+
skippedText: "- Codex notify: skipped (no backup; not installed)",
|
|
48
|
+
}),
|
|
49
|
+
renderRestoreLine({
|
|
50
|
+
exists: codeConfigExists,
|
|
51
|
+
result: resultByName.get("every-code"),
|
|
52
|
+
missingText: "- Every Code notify: skipped (config.toml not found)",
|
|
53
|
+
restoredText: (result) =>
|
|
54
|
+
`- Every Code notify restored: ${result.detail || integrationContext.everyCode.configPath}`,
|
|
55
|
+
noChangeText: "- Every Code notify: no change",
|
|
56
|
+
skippedText: "- Every Code notify: skipped (no backup; not installed)",
|
|
57
|
+
}),
|
|
58
|
+
renderHookLine({
|
|
59
|
+
exists: claudeConfigExists,
|
|
60
|
+
result: resultByName.get("claude"),
|
|
61
|
+
missingText: "- Claude hooks: skipped (settings.json not found)",
|
|
62
|
+
removedText: (result) =>
|
|
63
|
+
`- Claude hooks removed: ${result.detail || integrationContext.claude.settingsPath}`,
|
|
64
|
+
noChangeText: "- Claude hooks: no change",
|
|
65
|
+
skippedText: "- Claude hooks: skipped",
|
|
66
|
+
}),
|
|
67
|
+
renderHookLine({
|
|
68
|
+
exists: geminiConfigExists,
|
|
69
|
+
result: resultByName.get("gemini"),
|
|
70
|
+
missingText: `- Gemini hooks: skipped (${integrationContext.gemini.configDir} not found)`,
|
|
71
|
+
removedText: (result) =>
|
|
72
|
+
`- Gemini hooks removed: ${result.detail || integrationContext.gemini.settingsPath}`,
|
|
73
|
+
noChangeText: "- Gemini hooks: no change",
|
|
74
|
+
skippedText: "- Gemini hooks: skipped",
|
|
75
|
+
}),
|
|
76
|
+
renderHookLine({
|
|
77
|
+
exists: opencodeConfigExists,
|
|
78
|
+
result: resultByName.get("opencode"),
|
|
79
|
+
missingText: `- Opencode plugin: skipped (${integrationContext.opencode.configDir} not found)`,
|
|
80
|
+
removedText: (result) =>
|
|
81
|
+
`- Opencode plugin removed: ${result.detail || integrationContext.opencode.configDir}`,
|
|
82
|
+
noChangeText: "- Opencode plugin: no change",
|
|
83
|
+
skippedText: "- Opencode plugin: skipped (unexpected content)",
|
|
84
|
+
}),
|
|
85
|
+
renderHookLine({
|
|
86
|
+
exists: true,
|
|
87
|
+
result: resultByName.get("openclaw-session"),
|
|
88
|
+
missingText: "- OpenClaw session plugin: skipped (openclaw config not found)",
|
|
89
|
+
removedText: (result) =>
|
|
90
|
+
`- OpenClaw session plugin removed: ${result.detail || result.openclawConfigPath || "unknown"}`,
|
|
91
|
+
noChangeText: "- OpenClaw session plugin: no change",
|
|
92
|
+
skippedText: "- OpenClaw session plugin: no change",
|
|
93
|
+
unreadableText: (result) =>
|
|
94
|
+
`- OpenClaw session plugin: skipped (${result.detail || "openclaw config unreadable"})`,
|
|
95
|
+
}),
|
|
96
|
+
renderHookLine({
|
|
97
|
+
exists: true,
|
|
98
|
+
result: resultByName.get("openclaw-legacy"),
|
|
99
|
+
missingText: "- OpenClaw hook (legacy): skipped (openclaw config not found)",
|
|
100
|
+
removedText: (result) =>
|
|
101
|
+
`- OpenClaw hook (legacy) removed: ${result.detail || result.openclawConfigPath || "unknown"}`,
|
|
102
|
+
noChangeText: "- OpenClaw hook (legacy): no change",
|
|
103
|
+
skippedText: "- OpenClaw hook (legacy): no change",
|
|
104
|
+
unreadableText: (result) =>
|
|
105
|
+
`- OpenClaw hook (legacy): skipped (${result.detail || "openclaw config unreadable"})`,
|
|
106
|
+
}),
|
|
133
107
|
opts.purge ? `- Purged: ${path.join(home, ".vibeusage")}` : "- Purge: skipped (use --purge)",
|
|
134
108
|
"",
|
|
135
109
|
].join("\n"),
|
|
@@ -157,6 +131,49 @@ async function isFile(p) {
|
|
|
157
131
|
}
|
|
158
132
|
}
|
|
159
133
|
|
|
134
|
+
function renderRestoreLine({ exists, result, missingText, restoredText, noChangeText, skippedText }) {
|
|
135
|
+
if (!exists) return missingText;
|
|
136
|
+
if (!result) return noChangeText;
|
|
137
|
+
if (result.status === "restored" || result.status === "removed") {
|
|
138
|
+
return restoredText(result);
|
|
139
|
+
}
|
|
140
|
+
if (result.skippedReason === "no-backup-not-installed") {
|
|
141
|
+
return skippedText;
|
|
142
|
+
}
|
|
143
|
+
return noChangeText;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function renderHookLine({
|
|
147
|
+
exists,
|
|
148
|
+
result,
|
|
149
|
+
missingText,
|
|
150
|
+
removedText,
|
|
151
|
+
noChangeText,
|
|
152
|
+
skippedText,
|
|
153
|
+
unreadableText = null,
|
|
154
|
+
}) {
|
|
155
|
+
if (!exists) return missingText;
|
|
156
|
+
if (!result) return noChangeText;
|
|
157
|
+
if (result.status === "removed" || result.status === "updated") {
|
|
158
|
+
return removedText(result);
|
|
159
|
+
}
|
|
160
|
+
if (result.skippedReason === "hook-missing") {
|
|
161
|
+
return noChangeText;
|
|
162
|
+
}
|
|
163
|
+
if (result.skippedReason === "unexpected-content") {
|
|
164
|
+
return skippedText;
|
|
165
|
+
}
|
|
166
|
+
if (result.skippedReason === "openclaw-config-missing") {
|
|
167
|
+
return missingText;
|
|
168
|
+
}
|
|
169
|
+
if (result.skippedReason === "openclaw-config-unreadable") {
|
|
170
|
+
return typeof unreadableText === "function"
|
|
171
|
+
? unreadableText(result)
|
|
172
|
+
: unreadableText || skippedText;
|
|
173
|
+
}
|
|
174
|
+
return noChangeText;
|
|
175
|
+
}
|
|
176
|
+
|
|
160
177
|
async function isDir(p) {
|
|
161
178
|
try {
|
|
162
179
|
const st = await fs.stat(p);
|
package/src/lib/claude-config.js
CHANGED
|
@@ -3,57 +3,64 @@ const path = require("node:path");
|
|
|
3
3
|
|
|
4
4
|
const { ensureDir, readJson, writeJson } = require("./fs");
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const DEFAULT_EVENTS = ["Stop", "SessionEnd"];
|
|
7
7
|
|
|
8
|
-
async function upsertClaudeHook({ settingsPath, hookCommand,
|
|
8
|
+
async function upsertClaudeHook({ settingsPath, hookCommand, events = DEFAULT_EVENTS }) {
|
|
9
9
|
const existing = await readJson(settingsPath);
|
|
10
10
|
const settings = normalizeSettings(existing);
|
|
11
11
|
const hooks = normalizeHooks(settings.hooks);
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
12
|
+
const targetEvents = normalizeEvents(events);
|
|
13
|
+
let changed = false;
|
|
14
|
+
const nextHooks = { ...hooks };
|
|
15
|
+
|
|
16
|
+
for (const event of targetEvents) {
|
|
17
|
+
const entries = normalizeEntries(nextHooks[event]);
|
|
18
|
+
const normalized = normalizeEntriesForCommand(entries, hookCommand);
|
|
19
|
+
if (normalized.changed) {
|
|
20
|
+
nextHooks[event] = normalized.entries;
|
|
21
|
+
changed = true;
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (hasHook(entries, hookCommand)) continue;
|
|
21
26
|
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
nextHooks[event] = entries.concat([{ hooks: [{ type: "command", command: hookCommand }] }]);
|
|
28
|
+
changed = true;
|
|
24
29
|
}
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
const nextHooks = { ...hooks, [event]: nextEntries };
|
|
28
|
-
const nextSettings = { ...settings, hooks: nextHooks };
|
|
31
|
+
if (!changed) return { changed: false, backupPath: null };
|
|
29
32
|
|
|
33
|
+
const nextSettings = { ...settings, hooks: nextHooks };
|
|
30
34
|
const backupPath = await writeClaudeSettings({ settingsPath, settings: nextSettings });
|
|
31
35
|
return { changed: true, backupPath };
|
|
32
36
|
}
|
|
33
37
|
|
|
34
|
-
async function removeClaudeHook({ settingsPath, hookCommand,
|
|
38
|
+
async function removeClaudeHook({ settingsPath, hookCommand, events = DEFAULT_EVENTS }) {
|
|
35
39
|
const existing = await readJson(settingsPath);
|
|
36
40
|
if (!existing) return { removed: false, skippedReason: "settings-missing" };
|
|
37
41
|
|
|
38
42
|
const settings = normalizeSettings(existing);
|
|
39
43
|
const hooks = normalizeHooks(settings.hooks);
|
|
40
|
-
const
|
|
41
|
-
if (entries.length === 0) return { removed: false, skippedReason: "hook-missing" };
|
|
42
|
-
|
|
44
|
+
const targetEvents = normalizeEvents(events);
|
|
43
45
|
let removed = false;
|
|
44
|
-
const
|
|
45
|
-
for (const
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
48
|
-
|
|
46
|
+
const nextHooks = { ...hooks };
|
|
47
|
+
for (const event of targetEvents) {
|
|
48
|
+
const entries = normalizeEntries(hooks[event]);
|
|
49
|
+
if (entries.length === 0) continue;
|
|
50
|
+
|
|
51
|
+
const nextEntries = [];
|
|
52
|
+
for (const entry of entries) {
|
|
53
|
+
const res = stripHookFromEntry(entry, hookCommand);
|
|
54
|
+
if (res.removed) removed = true;
|
|
55
|
+
if (res.entry) nextEntries.push(res.entry);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (nextEntries.length > 0) nextHooks[event] = nextEntries;
|
|
59
|
+
else delete nextHooks[event];
|
|
49
60
|
}
|
|
50
61
|
|
|
51
62
|
if (!removed) return { removed: false, skippedReason: "hook-missing" };
|
|
52
63
|
|
|
53
|
-
const nextHooks = { ...hooks };
|
|
54
|
-
if (nextEntries.length > 0) nextHooks[event] = nextEntries;
|
|
55
|
-
else delete nextHooks[event];
|
|
56
|
-
|
|
57
64
|
const nextSettings = { ...settings };
|
|
58
65
|
if (Object.keys(nextHooks).length > 0) nextSettings.hooks = nextHooks;
|
|
59
66
|
else delete nextSettings.hooks;
|
|
@@ -62,13 +69,31 @@ async function removeClaudeHook({ settingsPath, hookCommand, event = DEFAULT_EVE
|
|
|
62
69
|
return { removed: true, skippedReason: null, backupPath };
|
|
63
70
|
}
|
|
64
71
|
|
|
65
|
-
async function isClaudeHookConfigured({ settingsPath, hookCommand,
|
|
72
|
+
async function isClaudeHookConfigured({ settingsPath, hookCommand, events = DEFAULT_EVENTS }) {
|
|
73
|
+
const probe = await probeClaudeHook({ settingsPath, hookCommand, events });
|
|
74
|
+
return probe.configured;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function probeClaudeHook({ settingsPath, hookCommand, events = DEFAULT_EVENTS }) {
|
|
66
78
|
const settings = await readJson(settingsPath);
|
|
67
|
-
if (!settings || typeof settings !== "object")
|
|
79
|
+
if (!settings || typeof settings !== "object") {
|
|
80
|
+
return { configured: false, anyPresent: false, eventStates: {} };
|
|
81
|
+
}
|
|
68
82
|
const hooks = settings.hooks;
|
|
69
|
-
if (!hooks || typeof hooks !== "object")
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
if (!hooks || typeof hooks !== "object") {
|
|
84
|
+
return { configured: false, anyPresent: false, eventStates: {} };
|
|
85
|
+
}
|
|
86
|
+
const targetEvents = normalizeEvents(events);
|
|
87
|
+
const eventStates = {};
|
|
88
|
+
for (const event of targetEvents) {
|
|
89
|
+
eventStates[event] = hasHook(normalizeEntries(hooks[event]), hookCommand);
|
|
90
|
+
}
|
|
91
|
+
const anyPresent = Object.values(eventStates).some(Boolean);
|
|
92
|
+
return {
|
|
93
|
+
configured: targetEvents.every((event) => eventStates[event] === true),
|
|
94
|
+
anyPresent,
|
|
95
|
+
eventStates,
|
|
96
|
+
};
|
|
72
97
|
}
|
|
73
98
|
|
|
74
99
|
function buildClaudeHookCommand(notifyPath) {
|
|
@@ -88,9 +113,27 @@ function normalizeEntries(raw) {
|
|
|
88
113
|
return Array.isArray(raw) ? raw.slice() : [];
|
|
89
114
|
}
|
|
90
115
|
|
|
116
|
+
function normalizeEvents(raw) {
|
|
117
|
+
const values = Array.isArray(raw) ? raw : [raw];
|
|
118
|
+
const out = [];
|
|
119
|
+
for (const value of values) {
|
|
120
|
+
if (typeof value !== "string") continue;
|
|
121
|
+
const normalized = value.trim();
|
|
122
|
+
if (!normalized || out.includes(normalized)) continue;
|
|
123
|
+
out.push(normalized);
|
|
124
|
+
}
|
|
125
|
+
return out.length > 0 ? out : DEFAULT_EVENTS.slice();
|
|
126
|
+
}
|
|
127
|
+
|
|
91
128
|
function normalizeCommand(cmd) {
|
|
92
129
|
if (Array.isArray(cmd)) return cmd.map((v) => String(v)).join("\u0000");
|
|
93
|
-
if (typeof cmd === "string")
|
|
130
|
+
if (typeof cmd === "string") {
|
|
131
|
+
const raw = cmd.trim();
|
|
132
|
+
if (!raw) return null;
|
|
133
|
+
const parsed = splitShellCommand(raw);
|
|
134
|
+
if (parsed) return parsed.join("\u0000");
|
|
135
|
+
return raw;
|
|
136
|
+
}
|
|
94
137
|
return null;
|
|
95
138
|
}
|
|
96
139
|
|
|
@@ -166,6 +209,56 @@ function quoteArg(value) {
|
|
|
166
209
|
return `"${v.replace(/"/g, '\\"')}"`;
|
|
167
210
|
}
|
|
168
211
|
|
|
212
|
+
function splitShellCommand(raw) {
|
|
213
|
+
const parts = [];
|
|
214
|
+
let current = "";
|
|
215
|
+
let quote = null;
|
|
216
|
+
let escaping = false;
|
|
217
|
+
|
|
218
|
+
for (let i = 0; i < raw.length; i += 1) {
|
|
219
|
+
const ch = raw[i];
|
|
220
|
+
|
|
221
|
+
if (escaping) {
|
|
222
|
+
current += ch;
|
|
223
|
+
escaping = false;
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (ch === "\\") {
|
|
228
|
+
escaping = true;
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (quote) {
|
|
233
|
+
if (ch === quote) {
|
|
234
|
+
quote = null;
|
|
235
|
+
} else {
|
|
236
|
+
current += ch;
|
|
237
|
+
}
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (ch === '"' || ch === "'") {
|
|
242
|
+
quote = ch;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (/\s/.test(ch)) {
|
|
247
|
+
if (current) {
|
|
248
|
+
parts.push(current);
|
|
249
|
+
current = "";
|
|
250
|
+
}
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
current += ch;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (escaping || quote) return null;
|
|
258
|
+
if (current) parts.push(current);
|
|
259
|
+
return parts.length > 0 ? parts : null;
|
|
260
|
+
}
|
|
261
|
+
|
|
169
262
|
async function writeClaudeSettings({ settingsPath, settings }) {
|
|
170
263
|
await ensureDir(path.dirname(settingsPath));
|
|
171
264
|
let backupPath = null;
|
|
@@ -183,8 +276,10 @@ async function writeClaudeSettings({ settingsPath, settings }) {
|
|
|
183
276
|
}
|
|
184
277
|
|
|
185
278
|
module.exports = {
|
|
279
|
+
DEFAULT_EVENTS,
|
|
186
280
|
upsertClaudeHook,
|
|
187
281
|
removeClaudeHook,
|
|
188
282
|
isClaudeHookConfigured,
|
|
283
|
+
probeClaudeHook,
|
|
189
284
|
buildClaudeHookCommand,
|
|
190
285
|
};
|