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
package/src/lib/openclaw-hook.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
const os = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const fs = require(
|
|
4
|
-
const fssync = require(
|
|
5
|
-
const cp = require(
|
|
1
|
+
const os = require("node:os");
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const fs = require("node:fs/promises");
|
|
4
|
+
const fssync = require("node:fs");
|
|
5
|
+
const cp = require("node:child_process");
|
|
6
6
|
|
|
7
|
-
const OPENCLAW_HOOK_NAME =
|
|
8
|
-
const OPENCLAW_HOOK_DIRNAME =
|
|
7
|
+
const OPENCLAW_HOOK_NAME = "vibeusage-openclaw-sync";
|
|
8
|
+
const OPENCLAW_HOOK_DIRNAME = "openclaw-hook";
|
|
9
9
|
|
|
10
10
|
function resolveOpenclawHookPaths({ home = os.homedir(), trackerDir, env = process.env } = {}) {
|
|
11
|
-
if (!trackerDir) throw new Error(
|
|
11
|
+
if (!trackerDir) throw new Error("trackerDir is required");
|
|
12
12
|
|
|
13
13
|
const openclawConfigPath =
|
|
14
|
-
normalizeString(env.OPENCLAW_CONFIG_PATH) || path.join(home,
|
|
14
|
+
normalizeString(env.OPENCLAW_CONFIG_PATH) || path.join(home, ".openclaw", "openclaw.json");
|
|
15
15
|
|
|
16
16
|
const openclawHome =
|
|
17
17
|
normalizeString(env.VIBEUSAGE_OPENCLAW_HOME) ||
|
|
18
18
|
normalizeString(env.OPENCLAW_STATE_DIR) ||
|
|
19
|
-
path.join(home,
|
|
19
|
+
path.join(home, ".openclaw");
|
|
20
20
|
|
|
21
21
|
const hookDir = path.join(trackerDir, OPENCLAW_HOOK_DIRNAME);
|
|
22
22
|
const hookEntryDir = path.join(hookDir, OPENCLAW_HOOK_NAME);
|
|
@@ -26,21 +26,26 @@ function resolveOpenclawHookPaths({ home = os.homedir(), trackerDir, env = proce
|
|
|
26
26
|
hookDir,
|
|
27
27
|
hookEntryDir,
|
|
28
28
|
openclawConfigPath,
|
|
29
|
-
openclawHome
|
|
29
|
+
openclawHome,
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
async function installOpenclawHook({
|
|
33
|
+
async function installOpenclawHook({
|
|
34
|
+
home = os.homedir(),
|
|
35
|
+
trackerDir,
|
|
36
|
+
packageName = "vibeusage",
|
|
37
|
+
env = process.env,
|
|
38
|
+
} = {}) {
|
|
34
39
|
const paths = resolveOpenclawHookPaths({ home, trackerDir, env });
|
|
35
40
|
|
|
36
41
|
await ensureOpenclawHookFiles({
|
|
37
42
|
hookDir: paths.hookDir,
|
|
38
43
|
trackerDir,
|
|
39
44
|
packageName,
|
|
40
|
-
openclawHome: paths.openclawHome
|
|
45
|
+
openclawHome: paths.openclawHome,
|
|
41
46
|
});
|
|
42
47
|
|
|
43
|
-
const installResult = runOpenclawCli([
|
|
48
|
+
const installResult = runOpenclawCli(["hooks", "install", "--link", paths.hookDir], env);
|
|
44
49
|
if (installResult.skippedReason) {
|
|
45
50
|
return { configured: false, ...paths, ...installResult };
|
|
46
51
|
}
|
|
@@ -48,28 +53,37 @@ async function installOpenclawHook({ home = os.homedir(), trackerDir, packageNam
|
|
|
48
53
|
const state = await probeOpenclawHookState({ home, trackerDir, env });
|
|
49
54
|
return {
|
|
50
55
|
configured: state.configured,
|
|
51
|
-
changed: /Linked hook path:/i.test(installResult.stdout ||
|
|
56
|
+
changed: /Linked hook path:/i.test(installResult.stdout || ""),
|
|
52
57
|
...paths,
|
|
53
58
|
stdout: installResult.stdout,
|
|
54
59
|
stderr: installResult.stderr,
|
|
55
|
-
code: installResult.code
|
|
60
|
+
code: installResult.code,
|
|
56
61
|
};
|
|
57
62
|
}
|
|
58
63
|
|
|
59
|
-
async function ensureOpenclawHookFiles({
|
|
60
|
-
|
|
64
|
+
async function ensureOpenclawHookFiles({
|
|
65
|
+
hookDir,
|
|
66
|
+
trackerDir,
|
|
67
|
+
packageName = "vibeusage",
|
|
68
|
+
openclawHome,
|
|
69
|
+
} = {}) {
|
|
70
|
+
if (!hookDir || !trackerDir) throw new Error("hookDir and trackerDir are required");
|
|
61
71
|
|
|
62
72
|
const hookEntryDir = path.join(hookDir, OPENCLAW_HOOK_NAME);
|
|
63
73
|
await fs.mkdir(hookEntryDir, { recursive: true });
|
|
64
74
|
|
|
65
|
-
const hookMdPath = path.join(hookEntryDir,
|
|
66
|
-
const handlerPath = path.join(hookEntryDir,
|
|
75
|
+
const hookMdPath = path.join(hookEntryDir, "HOOK.md");
|
|
76
|
+
const handlerPath = path.join(hookEntryDir, "handler.js");
|
|
67
77
|
|
|
68
|
-
await fs.writeFile(hookMdPath, buildHookMarkdown(),
|
|
78
|
+
await fs.writeFile(hookMdPath, buildHookMarkdown(), "utf8");
|
|
69
79
|
await fs.writeFile(
|
|
70
80
|
handlerPath,
|
|
71
|
-
buildHookHandler({
|
|
72
|
-
|
|
81
|
+
buildHookHandler({
|
|
82
|
+
trackerDir,
|
|
83
|
+
packageName,
|
|
84
|
+
openclawHome: openclawHome || path.join(os.homedir(), ".openclaw"),
|
|
85
|
+
}),
|
|
86
|
+
"utf8",
|
|
73
87
|
);
|
|
74
88
|
}
|
|
75
89
|
|
|
@@ -78,21 +92,22 @@ async function probeOpenclawHookState({ home = os.homedir(), trackerDir, env = p
|
|
|
78
92
|
const { openclawConfigPath, hookDir, hookEntryDir, hookName } = paths;
|
|
79
93
|
|
|
80
94
|
const hookFilesReady =
|
|
81
|
-
fssync.existsSync(path.join(hookEntryDir,
|
|
95
|
+
fssync.existsSync(path.join(hookEntryDir, "HOOK.md")) &&
|
|
96
|
+
fssync.existsSync(path.join(hookEntryDir, "handler.js"));
|
|
82
97
|
|
|
83
98
|
let cfg = null;
|
|
84
99
|
try {
|
|
85
|
-
const raw = await fs.readFile(openclawConfigPath,
|
|
100
|
+
const raw = await fs.readFile(openclawConfigPath, "utf8");
|
|
86
101
|
cfg = JSON.parse(raw);
|
|
87
102
|
} catch (err) {
|
|
88
|
-
if (err?.code ===
|
|
103
|
+
if (err?.code === "ENOENT" || err?.code === "ENOTDIR") {
|
|
89
104
|
return {
|
|
90
105
|
configured: false,
|
|
91
106
|
enabled: false,
|
|
92
107
|
linked: false,
|
|
93
108
|
hookFilesReady,
|
|
94
|
-
skippedReason:
|
|
95
|
-
...paths
|
|
109
|
+
skippedReason: "openclaw-config-missing",
|
|
110
|
+
...paths,
|
|
96
111
|
};
|
|
97
112
|
}
|
|
98
113
|
return {
|
|
@@ -100,42 +115,48 @@ async function probeOpenclawHookState({ home = os.homedir(), trackerDir, env = p
|
|
|
100
115
|
enabled: false,
|
|
101
116
|
linked: false,
|
|
102
117
|
hookFilesReady,
|
|
103
|
-
skippedReason:
|
|
118
|
+
skippedReason: "openclaw-config-unreadable",
|
|
104
119
|
error: err?.message || String(err),
|
|
105
|
-
...paths
|
|
120
|
+
...paths,
|
|
106
121
|
};
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
const enabled = Boolean(cfg?.hooks?.internal?.entries?.[hookName]?.enabled);
|
|
110
|
-
const extraDirs = Array.isArray(cfg?.hooks?.internal?.load?.extraDirs)
|
|
125
|
+
const extraDirs = Array.isArray(cfg?.hooks?.internal?.load?.extraDirs)
|
|
126
|
+
? cfg.hooks.internal.load.extraDirs
|
|
127
|
+
: [];
|
|
111
128
|
const normalizedHookDir = path.resolve(hookDir);
|
|
112
|
-
const linked = extraDirs.some((entry) => path.resolve(String(entry ||
|
|
129
|
+
const linked = extraDirs.some((entry) => path.resolve(String(entry || "")) === normalizedHookDir);
|
|
113
130
|
|
|
114
131
|
return {
|
|
115
132
|
configured: enabled && linked,
|
|
116
133
|
enabled,
|
|
117
134
|
linked,
|
|
118
135
|
hookFilesReady,
|
|
119
|
-
...paths
|
|
136
|
+
...paths,
|
|
120
137
|
};
|
|
121
138
|
}
|
|
122
139
|
|
|
123
|
-
async function removeOpenclawHookConfig({
|
|
140
|
+
async function removeOpenclawHookConfig({
|
|
141
|
+
home = os.homedir(),
|
|
142
|
+
trackerDir,
|
|
143
|
+
env = process.env,
|
|
144
|
+
} = {}) {
|
|
124
145
|
const paths = resolveOpenclawHookPaths({ home, trackerDir, env });
|
|
125
146
|
const { openclawConfigPath, hookDir, hookName } = paths;
|
|
126
147
|
|
|
127
148
|
let cfg;
|
|
128
149
|
try {
|
|
129
|
-
cfg = JSON.parse(await fs.readFile(openclawConfigPath,
|
|
150
|
+
cfg = JSON.parse(await fs.readFile(openclawConfigPath, "utf8"));
|
|
130
151
|
} catch (err) {
|
|
131
|
-
if (err?.code ===
|
|
132
|
-
return { removed: false, skippedReason:
|
|
152
|
+
if (err?.code === "ENOENT" || err?.code === "ENOTDIR") {
|
|
153
|
+
return { removed: false, skippedReason: "openclaw-config-missing", ...paths };
|
|
133
154
|
}
|
|
134
155
|
return {
|
|
135
156
|
removed: false,
|
|
136
|
-
skippedReason:
|
|
157
|
+
skippedReason: "openclaw-config-unreadable",
|
|
137
158
|
error: err?.message || String(err),
|
|
138
|
-
...paths
|
|
159
|
+
...paths,
|
|
139
160
|
};
|
|
140
161
|
}
|
|
141
162
|
|
|
@@ -152,7 +173,7 @@ async function removeOpenclawHookConfig({ home = os.homedir(), trackerDir, env =
|
|
|
152
173
|
if (internal?.load && Array.isArray(internal.load.extraDirs)) {
|
|
153
174
|
const before = internal.load.extraDirs;
|
|
154
175
|
const target = path.resolve(hookDir);
|
|
155
|
-
const after = before.filter((entry) => path.resolve(String(entry ||
|
|
176
|
+
const after = before.filter((entry) => path.resolve(String(entry || "")) !== target);
|
|
156
177
|
if (after.length !== before.length) {
|
|
157
178
|
internal.load.extraDirs = after;
|
|
158
179
|
changed = true;
|
|
@@ -161,7 +182,7 @@ async function removeOpenclawHookConfig({ home = os.homedir(), trackerDir, env =
|
|
|
161
182
|
}
|
|
162
183
|
}
|
|
163
184
|
|
|
164
|
-
if (internal?.installs && typeof internal.installs ===
|
|
185
|
+
if (internal?.installs && typeof internal.installs === "object") {
|
|
165
186
|
const installs = internal.installs;
|
|
166
187
|
if (Object.prototype.hasOwnProperty.call(installs, hookName)) {
|
|
167
188
|
delete installs[hookName];
|
|
@@ -194,7 +215,7 @@ async function removeOpenclawHookConfig({ home = os.homedir(), trackerDir, env =
|
|
|
194
215
|
}
|
|
195
216
|
|
|
196
217
|
if (changed) {
|
|
197
|
-
await fs.writeFile(openclawConfigPath, `${JSON.stringify(cfg, null, 2)}\n`,
|
|
218
|
+
await fs.writeFile(openclawConfigPath, `${JSON.stringify(cfg, null, 2)}\n`, "utf8");
|
|
198
219
|
}
|
|
199
220
|
|
|
200
221
|
await fs.rm(hookDir, { recursive: true, force: true }).catch(() => {});
|
|
@@ -205,45 +226,45 @@ async function removeOpenclawHookConfig({ home = os.homedir(), trackerDir, env =
|
|
|
205
226
|
function runOpenclawCli(args, env = process.env) {
|
|
206
227
|
let res;
|
|
207
228
|
try {
|
|
208
|
-
res = cp.spawnSync(
|
|
229
|
+
res = cp.spawnSync("openclaw", args, {
|
|
209
230
|
env,
|
|
210
|
-
encoding:
|
|
211
|
-
timeout: 30_000
|
|
231
|
+
encoding: "utf8",
|
|
232
|
+
timeout: 30_000,
|
|
212
233
|
});
|
|
213
234
|
} catch (err) {
|
|
214
235
|
return {
|
|
215
236
|
code: 1,
|
|
216
|
-
skippedReason: err?.code ===
|
|
237
|
+
skippedReason: err?.code === "ENOENT" ? "openclaw-cli-missing" : "openclaw-cli-error",
|
|
217
238
|
error: err?.message || String(err),
|
|
218
|
-
stdout:
|
|
219
|
-
stderr:
|
|
239
|
+
stdout: "",
|
|
240
|
+
stderr: "",
|
|
220
241
|
};
|
|
221
242
|
}
|
|
222
243
|
|
|
223
|
-
if (res.error?.code ===
|
|
244
|
+
if (res.error?.code === "ENOENT") {
|
|
224
245
|
return {
|
|
225
246
|
code: 1,
|
|
226
|
-
skippedReason:
|
|
247
|
+
skippedReason: "openclaw-cli-missing",
|
|
227
248
|
error: res.error.message,
|
|
228
|
-
stdout: res.stdout ||
|
|
229
|
-
stderr: res.stderr ||
|
|
249
|
+
stdout: res.stdout || "",
|
|
250
|
+
stderr: res.stderr || "",
|
|
230
251
|
};
|
|
231
252
|
}
|
|
232
253
|
|
|
233
254
|
if ((res.status || 0) !== 0) {
|
|
234
255
|
return {
|
|
235
256
|
code: Number(res.status || 1),
|
|
236
|
-
skippedReason:
|
|
237
|
-
error: (res.stderr || res.stdout ||
|
|
238
|
-
stdout: res.stdout ||
|
|
239
|
-
stderr: res.stderr ||
|
|
257
|
+
skippedReason: "openclaw-hooks-install-failed",
|
|
258
|
+
error: (res.stderr || res.stdout || "").trim() || "openclaw hooks install failed",
|
|
259
|
+
stdout: res.stdout || "",
|
|
260
|
+
stderr: res.stderr || "",
|
|
240
261
|
};
|
|
241
262
|
}
|
|
242
263
|
|
|
243
264
|
return {
|
|
244
265
|
code: 0,
|
|
245
|
-
stdout: res.stdout ||
|
|
246
|
-
stderr: res.stderr ||
|
|
266
|
+
stdout: res.stdout || "",
|
|
267
|
+
stderr: res.stderr || "",
|
|
247
268
|
};
|
|
248
269
|
}
|
|
249
270
|
|
|
@@ -261,12 +282,13 @@ Triggers non-blocking 'vibeusage sync --auto --from-openclaw' runs when OpenClaw
|
|
|
261
282
|
`;
|
|
262
283
|
}
|
|
263
284
|
|
|
264
|
-
function buildHookHandler({ trackerDir, packageName =
|
|
265
|
-
const trackerBinPath = path.join(trackerDir,
|
|
266
|
-
const fallbackPkg = packageName ||
|
|
267
|
-
const safeOpenclawHome = openclawHome || path.join(os.homedir(),
|
|
285
|
+
function buildHookHandler({ trackerDir, packageName = "vibeusage", openclawHome }) {
|
|
286
|
+
const trackerBinPath = path.join(trackerDir, "app", "bin", "tracker.js");
|
|
287
|
+
const fallbackPkg = packageName || "vibeusage";
|
|
288
|
+
const safeOpenclawHome = openclawHome || path.join(os.homedir(), ".openclaw");
|
|
268
289
|
|
|
269
|
-
return
|
|
290
|
+
return (
|
|
291
|
+
`'use strict';\n` +
|
|
270
292
|
`const fs = require('node:fs');\n` +
|
|
271
293
|
`const path = require('node:path');\n` +
|
|
272
294
|
`const cp = require('node:child_process');\n` +
|
|
@@ -377,11 +399,12 @@ function buildHookHandler({ trackerDir, packageName = 'vibeusage', openclawHome
|
|
|
377
399
|
` normalize(ctx.sessionEntry && ctx.sessionEntry.sessionId) ||\n` +
|
|
378
400
|
` normalize(ctx.sessionId)\n` +
|
|
379
401
|
` );\n` +
|
|
380
|
-
`}\n
|
|
402
|
+
`}\n`
|
|
403
|
+
);
|
|
381
404
|
}
|
|
382
405
|
|
|
383
406
|
function normalizeString(value) {
|
|
384
|
-
if (typeof value !==
|
|
407
|
+
if (typeof value !== "string") return null;
|
|
385
408
|
const trimmed = value.trim();
|
|
386
409
|
return trimmed.length > 0 ? trimmed : null;
|
|
387
410
|
}
|
|
@@ -393,5 +416,5 @@ module.exports = {
|
|
|
393
416
|
ensureOpenclawHookFiles,
|
|
394
417
|
installOpenclawHook,
|
|
395
418
|
probeOpenclawHookState,
|
|
396
|
-
removeOpenclawHookConfig
|
|
419
|
+
removeOpenclawHookConfig,
|
|
397
420
|
};
|