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,24 +1,24 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const os = require(
|
|
3
|
-
const path = require(
|
|
1
|
+
const fs = require("node:fs/promises");
|
|
2
|
+
const os = require("node:os");
|
|
3
|
+
const path = require("node:path");
|
|
4
4
|
|
|
5
|
-
const { listOpencodeMessageFiles, parseOpencodeIncremental } = require(
|
|
5
|
+
const { listOpencodeMessageFiles, parseOpencodeIncremental } = require("./rollout");
|
|
6
6
|
|
|
7
|
-
const BUCKET_SEPARATOR =
|
|
7
|
+
const BUCKET_SEPARATOR = "|";
|
|
8
8
|
const DAY_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
9
9
|
|
|
10
10
|
function formatHourKey(date) {
|
|
11
|
-
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2,
|
|
12
|
-
date.getUTCDate()
|
|
13
|
-
).padStart(2,
|
|
14
|
-
date.getUTCMinutes() >= 30 ? 30 : 0
|
|
15
|
-
).padStart(2,
|
|
11
|
+
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}-${String(
|
|
12
|
+
date.getUTCDate(),
|
|
13
|
+
).padStart(2, "0")}T${String(date.getUTCHours()).padStart(2, "0")}:${String(
|
|
14
|
+
date.getUTCMinutes() >= 30 ? 30 : 0,
|
|
15
|
+
).padStart(2, "0")}:00`;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function toBig(value) {
|
|
19
|
-
if (typeof value ===
|
|
20
|
-
if (typeof value ===
|
|
21
|
-
if (typeof value ===
|
|
19
|
+
if (typeof value === "bigint") return value;
|
|
20
|
+
if (typeof value === "number") return BigInt(value);
|
|
21
|
+
if (typeof value === "string" && value.trim()) return BigInt(value);
|
|
22
22
|
return 0n;
|
|
23
23
|
}
|
|
24
24
|
|
|
@@ -30,9 +30,12 @@ function addTotals(target, delta) {
|
|
|
30
30
|
target.total_tokens += toBig(delta.total_tokens);
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
async function buildLocalHourlyTotals({ storageDir, source =
|
|
33
|
+
async function buildLocalHourlyTotals({ storageDir, source = "opencode" }) {
|
|
34
34
|
const messageFiles = await listOpencodeMessageFiles(storageDir);
|
|
35
|
-
const queuePath = path.join(
|
|
35
|
+
const queuePath = path.join(
|
|
36
|
+
os.tmpdir(),
|
|
37
|
+
`vibeusage-opencode-audit-${process.pid}-${Date.now()}.jsonl`,
|
|
38
|
+
);
|
|
36
39
|
const cursors = { version: 1, files: {}, hourly: null, opencode: null };
|
|
37
40
|
|
|
38
41
|
await parseOpencodeIncremental({ messageFiles, cursors, queuePath, source });
|
|
@@ -58,7 +61,7 @@ async function buildLocalHourlyTotals({ storageDir, source = 'opencode' }) {
|
|
|
58
61
|
cached_input_tokens: 0n,
|
|
59
62
|
output_tokens: 0n,
|
|
60
63
|
reasoning_output_tokens: 0n,
|
|
61
|
-
total_tokens: 0n
|
|
64
|
+
total_tokens: 0n,
|
|
62
65
|
};
|
|
63
66
|
addTotals(totals, bucket.totals || {});
|
|
64
67
|
byHour.set(hourKey, totals);
|
|
@@ -78,8 +81,8 @@ function normalizeServerRows(rows) {
|
|
|
78
81
|
cached_input_tokens: toBig(row.cached_input_tokens),
|
|
79
82
|
output_tokens: toBig(row.output_tokens),
|
|
80
83
|
reasoning_output_tokens: toBig(row.reasoning_output_tokens),
|
|
81
|
-
total_tokens: toBig(row.total_tokens)
|
|
82
|
-
}
|
|
84
|
+
total_tokens: toBig(row.total_tokens),
|
|
85
|
+
},
|
|
83
86
|
});
|
|
84
87
|
}
|
|
85
88
|
return map;
|
|
@@ -91,7 +94,7 @@ function diffTotals(local, server) {
|
|
|
91
94
|
cached_input_tokens: local.cached_input_tokens - server.cached_input_tokens,
|
|
92
95
|
output_tokens: local.output_tokens - server.output_tokens,
|
|
93
96
|
reasoning_output_tokens: local.reasoning_output_tokens - server.reasoning_output_tokens,
|
|
94
|
-
total_tokens: local.total_tokens - server.total_tokens
|
|
97
|
+
total_tokens: local.total_tokens - server.total_tokens,
|
|
95
98
|
};
|
|
96
99
|
}
|
|
97
100
|
|
|
@@ -101,7 +104,7 @@ function maxAbsDelta(delta) {
|
|
|
101
104
|
delta.cached_input_tokens,
|
|
102
105
|
delta.output_tokens,
|
|
103
106
|
delta.reasoning_output_tokens,
|
|
104
|
-
delta.total_tokens
|
|
107
|
+
delta.total_tokens,
|
|
105
108
|
].reduce((acc, value) => {
|
|
106
109
|
const abs = value < 0n ? -value : value;
|
|
107
110
|
return abs > acc ? abs : acc;
|
|
@@ -121,25 +124,25 @@ function listDays(from, to) {
|
|
|
121
124
|
const start = new Date(`${from}T00:00:00.000Z`);
|
|
122
125
|
const end = new Date(`${to}T00:00:00.000Z`);
|
|
123
126
|
for (let dt = start; dt <= end; dt = new Date(dt.getTime() + 24 * 60 * 60 * 1000)) {
|
|
124
|
-
const day = `${dt.getUTCFullYear()}-${String(dt.getUTCMonth() + 1).padStart(2,
|
|
125
|
-
dt.getUTCDate()
|
|
126
|
-
).padStart(2,
|
|
127
|
+
const day = `${dt.getUTCFullYear()}-${String(dt.getUTCMonth() + 1).padStart(2, "0")}-${String(
|
|
128
|
+
dt.getUTCDate(),
|
|
129
|
+
).padStart(2, "0")}`;
|
|
127
130
|
out.push(day);
|
|
128
131
|
}
|
|
129
132
|
return out;
|
|
130
133
|
}
|
|
131
134
|
|
|
132
135
|
async function auditOpencodeUsage({ storageDir, from, to, fetchHourly, includeMissing = false }) {
|
|
133
|
-
const local = await buildLocalHourlyTotals({ storageDir, source:
|
|
136
|
+
const local = await buildLocalHourlyTotals({ storageDir, source: "opencode" });
|
|
134
137
|
if (!local.minDay || !local.maxDay) {
|
|
135
|
-
throw new Error(
|
|
138
|
+
throw new Error("No local opencode data found");
|
|
136
139
|
}
|
|
137
140
|
|
|
138
141
|
const fromDay = from || local.minDay;
|
|
139
142
|
const toDay = to || local.maxDay;
|
|
140
143
|
const days = listDays(fromDay, toDay);
|
|
141
144
|
if (days.length === 0) {
|
|
142
|
-
throw new Error(
|
|
145
|
+
throw new Error("Invalid date range for audit");
|
|
143
146
|
}
|
|
144
147
|
|
|
145
148
|
const diffs = [];
|
|
@@ -153,13 +156,13 @@ async function auditOpencodeUsage({ storageDir, from, to, fetchHourly, includeMi
|
|
|
153
156
|
const serverByHour = normalizeServerRows(server?.data || []);
|
|
154
157
|
for (let h = 0; h < 24; h++) {
|
|
155
158
|
for (const m of [0, 30]) {
|
|
156
|
-
const hourKey = `${day}T${String(h).padStart(2,
|
|
159
|
+
const hourKey = `${day}T${String(h).padStart(2, "0")}:${String(m).padStart(2, "0")}:00`;
|
|
157
160
|
const localTotals = local.byHour.get(hourKey) || {
|
|
158
161
|
input_tokens: 0n,
|
|
159
162
|
cached_input_tokens: 0n,
|
|
160
163
|
output_tokens: 0n,
|
|
161
164
|
reasoning_output_tokens: 0n,
|
|
162
|
-
total_tokens: 0n
|
|
165
|
+
total_tokens: 0n,
|
|
163
166
|
};
|
|
164
167
|
const serverEntry = serverByHour.get(hourKey) || null;
|
|
165
168
|
if (serverEntry?.missing && !includeMissing) {
|
|
@@ -171,7 +174,7 @@ async function auditOpencodeUsage({ storageDir, from, to, fetchHourly, includeMi
|
|
|
171
174
|
cached_input_tokens: 0n,
|
|
172
175
|
output_tokens: 0n,
|
|
173
176
|
reasoning_output_tokens: 0n,
|
|
174
|
-
total_tokens: 0n
|
|
177
|
+
total_tokens: 0n,
|
|
175
178
|
};
|
|
176
179
|
const delta = diffTotals(localTotals, serverTotals);
|
|
177
180
|
const deltaMax = maxAbsDelta(delta);
|
|
@@ -193,9 +196,9 @@ async function auditOpencodeUsage({ storageDir, from, to, fetchHourly, includeMi
|
|
|
193
196
|
matched,
|
|
194
197
|
mismatched,
|
|
195
198
|
incomplete,
|
|
196
|
-
maxDelta
|
|
199
|
+
maxDelta,
|
|
197
200
|
},
|
|
198
|
-
diffs
|
|
201
|
+
diffs,
|
|
199
202
|
};
|
|
200
203
|
}
|
|
201
204
|
|
package/src/lib/progress.js
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
function createProgress({ stream } = {}) {
|
|
2
2
|
const out = stream || process.stdout;
|
|
3
3
|
const enabled = Boolean(out && out.isTTY);
|
|
4
|
-
const frames = [
|
|
4
|
+
const frames = ["|", "/", "-", "\\"];
|
|
5
5
|
const intervalMs = 90;
|
|
6
6
|
|
|
7
7
|
let timer = null;
|
|
8
|
-
let text =
|
|
8
|
+
let text = "";
|
|
9
9
|
let frame = 0;
|
|
10
10
|
let lastLen = 0;
|
|
11
11
|
|
|
12
12
|
function render() {
|
|
13
13
|
if (!enabled) return;
|
|
14
14
|
const line = `${frames[frame++ % frames.length]} ${text}`;
|
|
15
|
-
const pad = lastLen > line.length ?
|
|
15
|
+
const pad = lastLen > line.length ? " ".repeat(lastLen - line.length) : "";
|
|
16
16
|
lastLen = line.length;
|
|
17
17
|
out.write(`\r${line}${pad}`);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function start(initialText) {
|
|
21
21
|
if (!enabled) return;
|
|
22
|
-
text = initialText ||
|
|
22
|
+
text = initialText || "";
|
|
23
23
|
if (timer) clearInterval(timer);
|
|
24
24
|
timer = setInterval(render, intervalMs);
|
|
25
25
|
render();
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function update(nextText) {
|
|
29
|
-
text = nextText ||
|
|
29
|
+
text = nextText || "";
|
|
30
30
|
render();
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -34,7 +34,7 @@ function createProgress({ stream } = {}) {
|
|
|
34
34
|
if (!enabled) return;
|
|
35
35
|
if (timer) clearInterval(timer);
|
|
36
36
|
timer = null;
|
|
37
|
-
out.write(`\r${
|
|
37
|
+
out.write(`\r${" ".repeat(lastLen)}\r`);
|
|
38
38
|
lastLen = 0;
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -45,19 +45,19 @@ function renderBar(progress, width = 20) {
|
|
|
45
45
|
const p = Number.isFinite(progress) ? Math.min(1, Math.max(0, progress)) : 0;
|
|
46
46
|
const filled = Math.round(p * width);
|
|
47
47
|
const empty = Math.max(0, width - filled);
|
|
48
|
-
return `[${
|
|
48
|
+
return `[${"=".repeat(filled)}${"-".repeat(empty)}] ${Math.round(p * 100)}%`;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
function formatNumber(n) {
|
|
52
52
|
const v = Number(n);
|
|
53
|
-
if (!Number.isFinite(v)) return
|
|
54
|
-
return Math.trunc(v).toLocaleString(
|
|
53
|
+
if (!Number.isFinite(v)) return "0";
|
|
54
|
+
return Math.trunc(v).toLocaleString("en-US");
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
function formatBytes(bytes) {
|
|
58
58
|
const n = Number(bytes);
|
|
59
|
-
if (!Number.isFinite(n) || n <= 0) return
|
|
60
|
-
const units = [
|
|
59
|
+
if (!Number.isFinite(n) || n <= 0) return "0 B";
|
|
60
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
61
61
|
let v = n;
|
|
62
62
|
let i = 0;
|
|
63
63
|
while (v >= 1024 && i < units.length - 1) {
|
|
@@ -72,6 +72,5 @@ module.exports = {
|
|
|
72
72
|
createProgress,
|
|
73
73
|
renderBar,
|
|
74
74
|
formatNumber,
|
|
75
|
-
formatBytes
|
|
75
|
+
formatBytes,
|
|
76
76
|
};
|
|
77
|
-
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const fsp = require(
|
|
3
|
-
const path = require(
|
|
4
|
-
const readline = require(
|
|
1
|
+
const fs = require("node:fs");
|
|
2
|
+
const fsp = require("node:fs/promises");
|
|
3
|
+
const path = require("node:path");
|
|
4
|
+
const readline = require("node:readline");
|
|
5
5
|
|
|
6
|
-
async function purgeProjectUsage({
|
|
6
|
+
async function purgeProjectUsage({
|
|
7
|
+
projectKey,
|
|
8
|
+
projectQueuePath,
|
|
9
|
+
projectQueueStatePath,
|
|
10
|
+
projectState,
|
|
11
|
+
}) {
|
|
7
12
|
if (!projectKey || !projectQueuePath || !projectQueueStatePath || !projectState) {
|
|
8
13
|
return { removed: 0, kept: 0, removedBuckets: 0 };
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
let previousOffset = 0;
|
|
12
|
-
const previousState = await fsp.readFile(projectQueueStatePath,
|
|
17
|
+
const previousState = await fsp.readFile(projectQueueStatePath, "utf8").catch(() => null);
|
|
13
18
|
if (previousState) {
|
|
14
19
|
try {
|
|
15
20
|
const parsed = JSON.parse(previousState);
|
|
@@ -20,7 +25,8 @@ async function purgeProjectUsage({ projectKey, projectQueuePath, projectQueueSta
|
|
|
20
25
|
}
|
|
21
26
|
}
|
|
22
27
|
|
|
23
|
-
const buckets =
|
|
28
|
+
const buckets =
|
|
29
|
+
projectState.buckets && typeof projectState.buckets === "object" ? projectState.buckets : {};
|
|
24
30
|
let removedBuckets = 0;
|
|
25
31
|
const prefix = `${projectKey}|`;
|
|
26
32
|
for (const key of Object.keys(buckets)) {
|
|
@@ -38,24 +44,24 @@ async function purgeProjectUsage({ projectKey, projectQueuePath, projectQueueSta
|
|
|
38
44
|
if (st && st.isFile()) {
|
|
39
45
|
const tmpPath = `${projectQueuePath}.tmp`;
|
|
40
46
|
await fsp.mkdir(path.dirname(projectQueuePath), { recursive: true });
|
|
41
|
-
const input = fs.createReadStream(projectQueuePath,
|
|
42
|
-
const output = fs.createWriteStream(tmpPath, { encoding:
|
|
47
|
+
const input = fs.createReadStream(projectQueuePath, "utf8");
|
|
48
|
+
const output = fs.createWriteStream(tmpPath, { encoding: "utf8" });
|
|
43
49
|
const rl = readline.createInterface({ input, crlfDelay: Infinity });
|
|
44
50
|
|
|
45
51
|
let inputOffset = 0;
|
|
46
52
|
let outputOffset = 0;
|
|
47
53
|
for await (const line of rl) {
|
|
48
54
|
const trimmed = line.trim();
|
|
49
|
-
const inputBytes = Buffer.byteLength(line,
|
|
55
|
+
const inputBytes = Buffer.byteLength(line, "utf8") + 1;
|
|
50
56
|
inputOffset += inputBytes;
|
|
51
57
|
if (!trimmed) continue;
|
|
52
58
|
let parsed = null;
|
|
53
59
|
try {
|
|
54
60
|
parsed = JSON.parse(trimmed);
|
|
55
61
|
} catch (_err) {
|
|
56
|
-
const entry = trimmed +
|
|
62
|
+
const entry = trimmed + "\n";
|
|
57
63
|
output.write(entry);
|
|
58
|
-
outputOffset += Buffer.byteLength(entry,
|
|
64
|
+
outputOffset += Buffer.byteLength(entry, "utf8");
|
|
59
65
|
kept += 1;
|
|
60
66
|
if (inputOffset <= previousOffset) {
|
|
61
67
|
nextOffset = outputOffset;
|
|
@@ -69,9 +75,9 @@ async function purgeProjectUsage({ projectKey, projectQueuePath, projectQueueSta
|
|
|
69
75
|
}
|
|
70
76
|
continue;
|
|
71
77
|
}
|
|
72
|
-
const entry = JSON.stringify(parsed) +
|
|
78
|
+
const entry = JSON.stringify(parsed) + "\n";
|
|
73
79
|
output.write(entry);
|
|
74
|
-
outputOffset += Buffer.byteLength(entry,
|
|
80
|
+
outputOffset += Buffer.byteLength(entry, "utf8");
|
|
75
81
|
kept += 1;
|
|
76
82
|
if (inputOffset <= previousOffset) {
|
|
77
83
|
nextOffset = outputOffset;
|
|
@@ -80,7 +86,7 @@ async function purgeProjectUsage({ projectKey, projectQueuePath, projectQueueSta
|
|
|
80
86
|
|
|
81
87
|
await new Promise((resolve, reject) => {
|
|
82
88
|
output.end(resolve);
|
|
83
|
-
output.on(
|
|
89
|
+
output.on("error", reject);
|
|
84
90
|
});
|
|
85
91
|
|
|
86
92
|
await fsp.rename(tmpPath, projectQueuePath);
|
|
@@ -88,11 +94,11 @@ async function purgeProjectUsage({ projectKey, projectQueuePath, projectQueueSta
|
|
|
88
94
|
nextOffset = outputOffset;
|
|
89
95
|
}
|
|
90
96
|
} else {
|
|
91
|
-
await fsp.writeFile(projectQueuePath,
|
|
97
|
+
await fsp.writeFile(projectQueuePath, "", "utf8");
|
|
92
98
|
nextOffset = 0;
|
|
93
99
|
}
|
|
94
100
|
|
|
95
|
-
await fsp.writeFile(projectQueueStatePath, JSON.stringify({ offset: nextOffset }),
|
|
101
|
+
await fsp.writeFile(projectQueueStatePath, JSON.stringify({ offset: nextOffset }), "utf8");
|
|
96
102
|
|
|
97
103
|
return { removed, kept, removedBuckets };
|
|
98
104
|
}
|
package/src/lib/prompt.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
const readline = require(
|
|
1
|
+
const readline = require("node:readline");
|
|
2
2
|
|
|
3
3
|
async function prompt(label) {
|
|
4
4
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
5
5
|
const value = await new Promise((resolve) => rl.question(label, resolve));
|
|
6
6
|
rl.close();
|
|
7
|
-
return String(value ||
|
|
7
|
+
return String(value || "").trim();
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
async function promptHidden(label) {
|
|
11
|
-
const rl = readline.createInterface({
|
|
11
|
+
const rl = readline.createInterface({
|
|
12
|
+
input: process.stdin,
|
|
13
|
+
output: process.stdout,
|
|
14
|
+
terminal: true,
|
|
15
|
+
});
|
|
12
16
|
const value = await new Promise((resolve) => {
|
|
13
17
|
rl._writeToOutput = function _writeToOutput() {};
|
|
14
18
|
rl.question(label, (answer) => resolve(answer));
|
|
15
19
|
});
|
|
16
20
|
rl.close();
|
|
17
|
-
return String(value ||
|
|
21
|
+
return String(value || "").trim();
|
|
18
22
|
}
|
|
19
23
|
|
|
20
24
|
module.exports = { prompt, promptHidden };
|