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.
Files changed (40) hide show
  1. package/README.md +306 -173
  2. package/README.old.md +324 -0
  3. package/README.zh-CN.md +304 -188
  4. package/package.json +32 -32
  5. package/src/cli.js +41 -37
  6. package/src/commands/activate-if-needed.js +41 -0
  7. package/src/commands/diagnostics.js +8 -9
  8. package/src/commands/doctor.js +31 -26
  9. package/src/commands/init.js +285 -218
  10. package/src/commands/status.js +86 -83
  11. package/src/commands/sync.js +182 -130
  12. package/src/commands/uninstall.js +66 -62
  13. package/src/lib/activation-check.js +290 -0
  14. package/src/lib/browser-auth.js +52 -54
  15. package/src/lib/claude-config.js +25 -25
  16. package/src/lib/cli-ui.js +35 -35
  17. package/src/lib/codex-config.js +40 -36
  18. package/src/lib/debug-flags.js +2 -2
  19. package/src/lib/diagnostics.js +70 -57
  20. package/src/lib/doctor.js +139 -132
  21. package/src/lib/fs.js +17 -17
  22. package/src/lib/gemini-config.js +44 -40
  23. package/src/lib/init-flow.js +16 -22
  24. package/src/lib/insforge-client.js +10 -10
  25. package/src/lib/insforge.js +9 -3
  26. package/src/lib/openclaw-hook.js +89 -66
  27. package/src/lib/openclaw-session-plugin.js +116 -92
  28. package/src/lib/opencode-config.js +31 -32
  29. package/src/lib/opencode-usage-audit.js +34 -31
  30. package/src/lib/progress.js +12 -13
  31. package/src/lib/project-usage-purge.js +23 -17
  32. package/src/lib/prompt.js +8 -4
  33. package/src/lib/rollout.js +342 -241
  34. package/src/lib/runtime-config.js +34 -22
  35. package/src/lib/subscriptions.js +94 -92
  36. package/src/lib/tracker-paths.js +6 -6
  37. package/src/lib/upload-throttle.js +35 -16
  38. package/src/lib/uploader.js +33 -29
  39. package/src/lib/vibeusage-api.js +72 -56
  40. package/src/lib/vibeusage-public-repo.js +41 -24
@@ -1,24 +1,24 @@
1
- const fs = require('node:fs/promises');
2
- const os = require('node:os');
3
- const path = require('node:path');
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('./rollout');
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, '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`;
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 === 'bigint') return value;
20
- if (typeof value === 'number') return BigInt(value);
21
- if (typeof value === 'string' && value.trim()) return BigInt(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 = 'opencode' }) {
33
+ async function buildLocalHourlyTotals({ storageDir, source = "opencode" }) {
34
34
  const messageFiles = await listOpencodeMessageFiles(storageDir);
35
- const queuePath = path.join(os.tmpdir(), `vibeusage-opencode-audit-${process.pid}-${Date.now()}.jsonl`);
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, '0')}-${String(
125
- dt.getUTCDate()
126
- ).padStart(2, '0')}`;
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: 'opencode' });
136
+ const local = await buildLocalHourlyTotals({ storageDir, source: "opencode" });
134
137
  if (!local.minDay || !local.maxDay) {
135
- throw new Error('No local opencode data found');
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('Invalid date range for audit');
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, '0')}:${String(m).padStart(2, '0')}:00`;
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
 
@@ -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 ? ' '.repeat(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${' '.repeat(lastLen)}\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 `[${'='.repeat(filled)}${'-'.repeat(empty)}] ${Math.round(p * 100)}%`;
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 '0';
54
- return Math.trunc(v).toLocaleString('en-US');
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 '0 B';
60
- const units = ['B', 'KB', 'MB', 'GB', 'TB'];
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('node:fs');
2
- const fsp = require('node:fs/promises');
3
- const path = require('node:path');
4
- const readline = require('node:readline');
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({ projectKey, projectQueuePath, projectQueueStatePath, projectState }) {
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, 'utf8').catch(() => null);
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 = projectState.buckets && typeof projectState.buckets === 'object' ? projectState.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, 'utf8');
42
- const output = fs.createWriteStream(tmpPath, { encoding: 'utf8' });
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, 'utf8') + 1;
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 + '\n';
62
+ const entry = trimmed + "\n";
57
63
  output.write(entry);
58
- outputOffset += Buffer.byteLength(entry, 'utf8');
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) + '\n';
78
+ const entry = JSON.stringify(parsed) + "\n";
73
79
  output.write(entry);
74
- outputOffset += Buffer.byteLength(entry, 'utf8');
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('error', reject);
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, '', 'utf8');
97
+ await fsp.writeFile(projectQueuePath, "", "utf8");
92
98
  nextOffset = 0;
93
99
  }
94
100
 
95
- await fsp.writeFile(projectQueueStatePath, JSON.stringify({ offset: nextOffset }), 'utf8');
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('node:readline');
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 || '').trim();
7
+ return String(value || "").trim();
8
8
  }
9
9
 
10
10
  async function promptHidden(label) {
11
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
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 || '').trim();
21
+ return String(value || "").trim();
18
22
  }
19
23
 
20
24
  module.exports = { prompt, promptHidden };