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
package/src/lib/gemini-config.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
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 { ensureDir, readJson, writeJson } = require(
|
|
5
|
+
const { ensureDir, readJson, writeJson } = require("./fs");
|
|
6
6
|
|
|
7
|
-
const DEFAULT_EVENT =
|
|
8
|
-
const DEFAULT_HOOK_NAME =
|
|
9
|
-
const DEFAULT_MATCHER =
|
|
7
|
+
const DEFAULT_EVENT = "SessionEnd";
|
|
8
|
+
const DEFAULT_HOOK_NAME = "vibeusage-tracker";
|
|
9
|
+
const DEFAULT_MATCHER = "exit|clear|logout|prompt_input_exit|other";
|
|
10
10
|
|
|
11
11
|
function resolveGeminiConfigDir({ home = os.homedir(), env = process.env } = {}) {
|
|
12
|
-
const explicit = typeof env.GEMINI_HOME ===
|
|
12
|
+
const explicit = typeof env.GEMINI_HOME === "string" ? env.GEMINI_HOME.trim() : "";
|
|
13
13
|
if (explicit) return path.resolve(explicit);
|
|
14
|
-
return path.join(home,
|
|
14
|
+
return path.join(home, ".gemini");
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function resolveGeminiSettingsPath({ configDir }) {
|
|
18
|
-
return path.join(configDir,
|
|
18
|
+
return path.join(configDir, "settings.json");
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
async function upsertGeminiHook({
|
|
@@ -23,7 +23,7 @@ async function upsertGeminiHook({
|
|
|
23
23
|
hookCommand,
|
|
24
24
|
hookName = DEFAULT_HOOK_NAME,
|
|
25
25
|
matcher = DEFAULT_MATCHER,
|
|
26
|
-
event = DEFAULT_EVENT
|
|
26
|
+
event = DEFAULT_EVENT,
|
|
27
27
|
}) {
|
|
28
28
|
const existing = await readJson(settingsPath);
|
|
29
29
|
const settings = normalizeSettings(existing);
|
|
@@ -53,15 +53,15 @@ async function removeGeminiHook({
|
|
|
53
53
|
settingsPath,
|
|
54
54
|
hookCommand,
|
|
55
55
|
hookName = DEFAULT_HOOK_NAME,
|
|
56
|
-
event = DEFAULT_EVENT
|
|
56
|
+
event = DEFAULT_EVENT,
|
|
57
57
|
}) {
|
|
58
58
|
const existing = await readJson(settingsPath);
|
|
59
|
-
if (!existing) return { removed: false, skippedReason:
|
|
59
|
+
if (!existing) return { removed: false, skippedReason: "settings-missing" };
|
|
60
60
|
|
|
61
61
|
const settings = normalizeSettings(existing);
|
|
62
62
|
const hooks = normalizeHooks(settings.hooks);
|
|
63
63
|
const entries = normalizeEntries(hooks[event]);
|
|
64
|
-
if (entries.length === 0) return { removed: false, skippedReason:
|
|
64
|
+
if (entries.length === 0) return { removed: false, skippedReason: "hook-missing" };
|
|
65
65
|
|
|
66
66
|
let removed = false;
|
|
67
67
|
const nextEntries = [];
|
|
@@ -71,7 +71,7 @@ async function removeGeminiHook({
|
|
|
71
71
|
if (res.entry) nextEntries.push(res.entry);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if (!removed) return { removed: false, skippedReason:
|
|
74
|
+
if (!removed) return { removed: false, skippedReason: "hook-missing" };
|
|
75
75
|
|
|
76
76
|
const nextHooks = { ...hooks };
|
|
77
77
|
if (nextEntries.length > 0) nextHooks[event] = nextEntries;
|
|
@@ -89,42 +89,42 @@ async function isGeminiHookConfigured({
|
|
|
89
89
|
settingsPath,
|
|
90
90
|
hookCommand,
|
|
91
91
|
hookName = DEFAULT_HOOK_NAME,
|
|
92
|
-
event = DEFAULT_EVENT
|
|
92
|
+
event = DEFAULT_EVENT,
|
|
93
93
|
}) {
|
|
94
94
|
const settings = await readJson(settingsPath);
|
|
95
|
-
if (!settings || typeof settings !==
|
|
95
|
+
if (!settings || typeof settings !== "object") return false;
|
|
96
96
|
const hooks = settings.hooks;
|
|
97
|
-
if (!hooks || typeof hooks !==
|
|
97
|
+
if (!hooks || typeof hooks !== "object") return false;
|
|
98
98
|
const entries = normalizeEntries(hooks[event]);
|
|
99
99
|
return hasHook(entries, { hookCommand, hookName });
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
function buildGeminiHookCommand(notifyPath) {
|
|
103
|
-
const cmd = typeof notifyPath ===
|
|
103
|
+
const cmd = typeof notifyPath === "string" ? notifyPath : "";
|
|
104
104
|
return `/usr/bin/env node ${quoteArg(cmd)} --source=gemini`;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function buildHookEntry({ hookCommand, hookName, matcher }) {
|
|
108
108
|
const hook = {
|
|
109
109
|
name: hookName,
|
|
110
|
-
type:
|
|
111
|
-
command: hookCommand
|
|
110
|
+
type: "command",
|
|
111
|
+
command: hookCommand,
|
|
112
112
|
};
|
|
113
113
|
const entry = { hooks: [hook] };
|
|
114
|
-
if (typeof matcher ===
|
|
114
|
+
if (typeof matcher === "string" && matcher.length > 0) entry.matcher = matcher;
|
|
115
115
|
return entry;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
function normalizeSettings(raw) {
|
|
119
|
-
return raw && typeof raw ===
|
|
119
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
120
120
|
}
|
|
121
121
|
|
|
122
122
|
function normalizeHooks(raw) {
|
|
123
|
-
return raw && typeof raw ===
|
|
123
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
function normalizeTools(raw) {
|
|
127
|
-
return raw && typeof raw ===
|
|
127
|
+
return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : {};
|
|
128
128
|
}
|
|
129
129
|
|
|
130
130
|
function normalizeEntries(raw) {
|
|
@@ -132,13 +132,13 @@ function normalizeEntries(raw) {
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
function normalizeCommand(cmd) {
|
|
135
|
-
if (Array.isArray(cmd)) return cmd.map((v) => String(v)).join(
|
|
136
|
-
if (typeof cmd ===
|
|
135
|
+
if (Array.isArray(cmd)) return cmd.map((v) => String(v)).join("\u0000");
|
|
136
|
+
if (typeof cmd === "string") return cmd.trim();
|
|
137
137
|
return null;
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
function normalizeName(name) {
|
|
141
|
-
if (typeof name !==
|
|
141
|
+
if (typeof name !== "string") return null;
|
|
142
142
|
const trimmed = name.trim();
|
|
143
143
|
return trimmed.length > 0 ? trimmed : null;
|
|
144
144
|
}
|
|
@@ -151,7 +151,7 @@ function ensureHooksEnabled(settings) {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
function hookMatches(hook, { hookCommand, hookName, requireCommand = false }) {
|
|
154
|
-
if (!hook || typeof hook !==
|
|
154
|
+
if (!hook || typeof hook !== "object") return false;
|
|
155
155
|
const name = normalizeName(hook.name);
|
|
156
156
|
const targetName = normalizeName(hookName);
|
|
157
157
|
const cmd = normalizeCommand(hook.command);
|
|
@@ -163,8 +163,9 @@ function hookMatches(hook, { hookCommand, hookName, requireCommand = false }) {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
function entryMatches(entry, { hookCommand, hookName, requireCommand = false }) {
|
|
166
|
-
if (!entry || typeof entry !==
|
|
167
|
-
if (entry.command || entry.name)
|
|
166
|
+
if (!entry || typeof entry !== "object") return false;
|
|
167
|
+
if (entry.command || entry.name)
|
|
168
|
+
return hookMatches(entry, { hookCommand, hookName, requireCommand });
|
|
168
169
|
if (!Array.isArray(entry.hooks)) return false;
|
|
169
170
|
return entry.hooks.some((hook) => hookMatches(hook, { hookCommand, hookName, requireCommand }));
|
|
170
171
|
}
|
|
@@ -176,7 +177,7 @@ function hasHook(entries, { hookCommand, hookName }) {
|
|
|
176
177
|
function normalizeEntriesForHook(entries, { hookCommand, hookName }) {
|
|
177
178
|
let changed = false;
|
|
178
179
|
const nextEntries = entries.map((entry) => {
|
|
179
|
-
if (!entry || typeof entry !==
|
|
180
|
+
if (!entry || typeof entry !== "object") return entry;
|
|
180
181
|
|
|
181
182
|
if (entry.command || entry.name) {
|
|
182
183
|
if (hookMatches(entry, { hookCommand, hookName })) {
|
|
@@ -212,8 +213,8 @@ function normalizeHookObject(hook, { hookCommand, hookName }) {
|
|
|
212
213
|
const next = { ...hook };
|
|
213
214
|
let changed = false;
|
|
214
215
|
|
|
215
|
-
if (next.type !==
|
|
216
|
-
next.type =
|
|
216
|
+
if (next.type !== "command") {
|
|
217
|
+
next.type = "command";
|
|
217
218
|
changed = true;
|
|
218
219
|
}
|
|
219
220
|
|
|
@@ -231,17 +232,20 @@ function normalizeHookObject(hook, { hookCommand, hookName }) {
|
|
|
231
232
|
}
|
|
232
233
|
|
|
233
234
|
function stripHookFromEntry(entry, { hookCommand, hookName }) {
|
|
234
|
-
if (!entry || typeof entry !==
|
|
235
|
+
if (!entry || typeof entry !== "object") return { entry, removed: false };
|
|
235
236
|
|
|
236
237
|
if (entry.command || entry.name) {
|
|
237
|
-
if (hookMatches(entry, { hookCommand, hookName, requireCommand: true }))
|
|
238
|
+
if (hookMatches(entry, { hookCommand, hookName, requireCommand: true }))
|
|
239
|
+
return { entry: null, removed: true };
|
|
238
240
|
return { entry, removed: false };
|
|
239
241
|
}
|
|
240
242
|
|
|
241
243
|
const hooks = Array.isArray(entry.hooks) ? entry.hooks : null;
|
|
242
244
|
if (!hooks) return { entry, removed: false };
|
|
243
245
|
|
|
244
|
-
const nextHooks = hooks.filter(
|
|
246
|
+
const nextHooks = hooks.filter(
|
|
247
|
+
(hook) => !hookMatches(hook, { hookCommand, hookName, requireCommand: true }),
|
|
248
|
+
);
|
|
245
249
|
if (nextHooks.length === hooks.length) return { entry, removed: false };
|
|
246
250
|
if (nextHooks.length === 0) return { entry: null, removed: true };
|
|
247
251
|
|
|
@@ -249,7 +253,7 @@ function stripHookFromEntry(entry, { hookCommand, hookName }) {
|
|
|
249
253
|
}
|
|
250
254
|
|
|
251
255
|
function quoteArg(value) {
|
|
252
|
-
const v = typeof value ===
|
|
256
|
+
const v = typeof value === "string" ? value : "";
|
|
253
257
|
if (!v) return '""';
|
|
254
258
|
if (/^[A-Za-z0-9_\-./:@]+$/.test(v)) return v;
|
|
255
259
|
return `"${v.replace(/"/g, '\\"')}"`;
|
|
@@ -261,7 +265,7 @@ async function writeGeminiSettings({ settingsPath, settings }) {
|
|
|
261
265
|
try {
|
|
262
266
|
const st = await fs.stat(settingsPath);
|
|
263
267
|
if (st && st.isFile()) {
|
|
264
|
-
backupPath = `${settingsPath}.bak.${new Date().toISOString().replace(/[:.]/g,
|
|
268
|
+
backupPath = `${settingsPath}.bak.${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
265
269
|
await fs.copyFile(settingsPath, backupPath);
|
|
266
270
|
}
|
|
267
271
|
} catch (_e) {
|
|
@@ -280,5 +284,5 @@ module.exports = {
|
|
|
280
284
|
buildGeminiHookCommand,
|
|
281
285
|
upsertGeminiHook,
|
|
282
286
|
removeGeminiHook,
|
|
283
|
-
isGeminiHookConfigured
|
|
287
|
+
isGeminiHookConfigured,
|
|
284
288
|
};
|
package/src/lib/init-flow.js
CHANGED
|
@@ -1,42 +1,36 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
const { formatSummaryLine, renderBox, underline } = require(
|
|
3
|
+
const { formatSummaryLine, renderBox, underline } = require("./cli-ui");
|
|
4
4
|
|
|
5
|
-
const DIVIDER =
|
|
5
|
+
const DIVIDER = "----------------------------------------------";
|
|
6
6
|
|
|
7
7
|
function renderLocalReport({ summary, isDryRun }) {
|
|
8
8
|
const header = isDryRun
|
|
9
|
-
?
|
|
10
|
-
:
|
|
11
|
-
const lines = [header,
|
|
9
|
+
? "Dry run complete. Preview only; no changes were applied."
|
|
10
|
+
: "Local configuration complete.";
|
|
11
|
+
const lines = [header, "", "Integration Status:"];
|
|
12
12
|
for (const item of summary || []) lines.push(formatSummaryLine(item));
|
|
13
|
-
process.stdout.write(`${lines.join(
|
|
13
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function renderAuthTransition({ authUrl, canAutoOpen }) {
|
|
17
|
-
const lines = [
|
|
17
|
+
const lines = ["", DIVIDER, "", "Next: Registering device..."];
|
|
18
18
|
if (canAutoOpen) {
|
|
19
|
-
lines.push(
|
|
19
|
+
lines.push("Opening your browser to link account...");
|
|
20
20
|
if (authUrl) lines.push(`If it does not open, visit: ${underline(authUrl)}`);
|
|
21
21
|
} else {
|
|
22
|
-
lines.push(
|
|
22
|
+
lines.push("Open the link below to sign in.");
|
|
23
23
|
if (authUrl) lines.push(`Visit: ${underline(authUrl)}`);
|
|
24
24
|
}
|
|
25
|
-
lines.push(
|
|
26
|
-
process.stdout.write(`${lines.join(
|
|
25
|
+
lines.push("");
|
|
26
|
+
process.stdout.write(`${lines.join("\n")}\n`);
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function renderSuccessBox({ configPath, dashboardUrl }) {
|
|
30
|
-
const identityLine =
|
|
31
|
-
const lines = [
|
|
32
|
-
'You are all set!',
|
|
33
|
-
'',
|
|
34
|
-
identityLine,
|
|
35
|
-
`Token saved to: ${configPath}`,
|
|
36
|
-
''
|
|
37
|
-
];
|
|
30
|
+
const identityLine = "Account linked.";
|
|
31
|
+
const lines = ["You are all set!", "", identityLine, `Token saved to: ${configPath}`, ""];
|
|
38
32
|
if (dashboardUrl) lines.push(`View your stats at: ${dashboardUrl}`);
|
|
39
|
-
lines.push(
|
|
33
|
+
lines.push("You can close this terminal window.");
|
|
40
34
|
process.stdout.write(`${renderBox(lines)}\n`);
|
|
41
35
|
}
|
|
42
36
|
|
|
@@ -44,5 +38,5 @@ module.exports = {
|
|
|
44
38
|
DIVIDER,
|
|
45
39
|
renderLocalReport,
|
|
46
40
|
renderAuthTransition,
|
|
47
|
-
renderSuccessBox
|
|
41
|
+
renderSuccessBox,
|
|
48
42
|
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
3
|
function loadInsforgeSdk() {
|
|
4
4
|
try {
|
|
5
|
-
return require(
|
|
5
|
+
return require("@insforge/sdk");
|
|
6
6
|
} catch (err) {
|
|
7
|
-
const wrapped = new Error(
|
|
7
|
+
const wrapped = new Error("Missing dependency @insforge/sdk. Please reinstall vibeusage.");
|
|
8
8
|
wrapped.cause = err;
|
|
9
9
|
throw wrapped;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function getAnonKey({ env = process.env } = {}) {
|
|
14
|
-
return env.VIBEUSAGE_INSFORGE_ANON_KEY ||
|
|
14
|
+
return env.VIBEUSAGE_INSFORGE_ANON_KEY || "";
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
function getHttpTimeoutMs({ env = process.env } = {}) {
|
|
18
|
-
const raw = readEnvValue(env, [
|
|
19
|
-
if (raw == null || raw ===
|
|
18
|
+
const raw = readEnvValue(env, ["VIBEUSAGE_HTTP_TIMEOUT_MS"]);
|
|
19
|
+
if (raw == null || raw === "") return 20_000;
|
|
20
20
|
const n = Number(raw);
|
|
21
21
|
if (!Number.isFinite(n)) return 20_000;
|
|
22
22
|
if (n <= 0) return 0;
|
|
@@ -46,14 +46,14 @@ function createTimeoutFetch(baseFetch) {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
function createInsforgeClient({ baseUrl, accessToken } = {}) {
|
|
49
|
-
if (!baseUrl) throw new Error(
|
|
49
|
+
if (!baseUrl) throw new Error("Missing baseUrl");
|
|
50
50
|
const { createClient } = loadInsforgeSdk();
|
|
51
51
|
const anonKey = getAnonKey();
|
|
52
52
|
return createClient({
|
|
53
53
|
baseUrl,
|
|
54
54
|
anonKey: anonKey || undefined,
|
|
55
55
|
edgeFunctionToken: accessToken || undefined,
|
|
56
|
-
fetch: createTimeoutFetch(globalThis.fetch)
|
|
56
|
+
fetch: createTimeoutFetch(globalThis.fetch),
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -67,7 +67,7 @@ function readEnvValue(env, keys) {
|
|
|
67
67
|
if (!env || !Array.isArray(keys)) return undefined;
|
|
68
68
|
for (const key of keys) {
|
|
69
69
|
const value = env?.[key];
|
|
70
|
-
if (value != null && value !==
|
|
70
|
+
if (value != null && value !== "") return value;
|
|
71
71
|
}
|
|
72
72
|
return undefined;
|
|
73
73
|
}
|
|
@@ -75,5 +75,5 @@ function readEnvValue(env, keys) {
|
|
|
75
75
|
module.exports = {
|
|
76
76
|
createInsforgeClient,
|
|
77
77
|
getAnonKey,
|
|
78
|
-
getHttpTimeoutMs
|
|
78
|
+
getHttpTimeoutMs,
|
|
79
79
|
};
|
package/src/lib/insforge.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const { exchangeLinkCode, issueDeviceToken, signInWithPassword } = require(
|
|
1
|
+
const { exchangeLinkCode, issueDeviceToken, signInWithPassword } = require("./vibeusage-api");
|
|
2
2
|
|
|
3
3
|
async function issueDeviceTokenWithPassword({ baseUrl, email, password, deviceName }) {
|
|
4
4
|
const accessToken = await signInWithPassword({ baseUrl, email, password });
|
|
@@ -11,7 +11,13 @@ async function issueDeviceTokenWithAccessToken({ baseUrl, accessToken, deviceNam
|
|
|
11
11
|
return issued;
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
async function issueDeviceTokenWithLinkCode({
|
|
14
|
+
async function issueDeviceTokenWithLinkCode({
|
|
15
|
+
baseUrl,
|
|
16
|
+
linkCode,
|
|
17
|
+
requestId,
|
|
18
|
+
deviceName,
|
|
19
|
+
platform,
|
|
20
|
+
}) {
|
|
15
21
|
const issued = await exchangeLinkCode({ baseUrl, linkCode, requestId, deviceName, platform });
|
|
16
22
|
return { token: issued.token, deviceId: issued.deviceId };
|
|
17
23
|
}
|
|
@@ -19,5 +25,5 @@ async function issueDeviceTokenWithLinkCode({ baseUrl, linkCode, requestId, devi
|
|
|
19
25
|
module.exports = {
|
|
20
26
|
issueDeviceTokenWithPassword,
|
|
21
27
|
issueDeviceTokenWithAccessToken,
|
|
22
|
-
issueDeviceTokenWithLinkCode
|
|
28
|
+
issueDeviceTokenWithLinkCode,
|
|
23
29
|
};
|