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/codex-config.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
1
|
+
const fs = require("node:fs/promises");
|
|
2
|
+
const path = require("node:path");
|
|
3
3
|
|
|
4
|
-
const { ensureDir, readJson, writeJson } = require(
|
|
4
|
+
const { ensureDir, readJson, writeJson } = require("./fs");
|
|
5
5
|
|
|
6
6
|
async function upsertNotify({ configPath, notifyCmd, notifyOriginalPath, configLabel }) {
|
|
7
|
-
const originalText = await fs.readFile(configPath,
|
|
7
|
+
const originalText = await fs.readFile(configPath, "utf8").catch(() => null);
|
|
8
8
|
if (originalText == null) {
|
|
9
|
-
const label =
|
|
9
|
+
const label =
|
|
10
|
+
typeof configLabel === "string" && configLabel.length > 0 ? configLabel : "Config";
|
|
10
11
|
throw new Error(`${label} not found: ${configPath}`);
|
|
11
12
|
}
|
|
12
13
|
|
|
@@ -19,14 +20,17 @@ async function upsertNotify({ configPath, notifyCmd, notifyOriginalPath, configL
|
|
|
19
20
|
await ensureDir(path.dirname(notifyOriginalPath));
|
|
20
21
|
const existing = await readJson(notifyOriginalPath);
|
|
21
22
|
if (!existing) {
|
|
22
|
-
await writeJson(notifyOriginalPath, {
|
|
23
|
+
await writeJson(notifyOriginalPath, {
|
|
24
|
+
notify: existingNotify,
|
|
25
|
+
capturedAt: new Date().toISOString(),
|
|
26
|
+
});
|
|
23
27
|
}
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
const updated = setNotify(originalText, notifyCmd);
|
|
27
|
-
const backupPath = `${configPath}.bak.${new Date().toISOString().replace(/[:.]/g,
|
|
31
|
+
const backupPath = `${configPath}.bak.${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
28
32
|
await fs.copyFile(configPath, backupPath);
|
|
29
|
-
await fs.writeFile(configPath, updated,
|
|
33
|
+
await fs.writeFile(configPath, updated, "utf8");
|
|
30
34
|
return { changed: true, backupPath };
|
|
31
35
|
}
|
|
32
36
|
|
|
@@ -34,23 +38,23 @@ async function upsertNotify({ configPath, notifyCmd, notifyOriginalPath, configL
|
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
async function restoreNotify({ configPath, notifyOriginalPath, expectedNotify }) {
|
|
37
|
-
const text = await fs.readFile(configPath,
|
|
38
|
-
if (text == null) return { restored: false, skippedReason:
|
|
41
|
+
const text = await fs.readFile(configPath, "utf8").catch(() => null);
|
|
42
|
+
if (text == null) return { restored: false, skippedReason: "config-missing" };
|
|
39
43
|
|
|
40
44
|
const original = await readJson(notifyOriginalPath);
|
|
41
45
|
const originalNotify = Array.isArray(original?.notify) ? original.notify : null;
|
|
42
46
|
const currentNotify = extractNotify(text);
|
|
43
47
|
|
|
44
48
|
if (!originalNotify && expectedNotify && !arraysEqual(currentNotify, expectedNotify)) {
|
|
45
|
-
return { restored: false, skippedReason:
|
|
49
|
+
return { restored: false, skippedReason: "no-backup-not-installed" };
|
|
46
50
|
}
|
|
47
51
|
|
|
48
52
|
const updated = originalNotify ? setNotify(text, originalNotify) : removeNotify(text);
|
|
49
|
-
if (updated === text) return { restored: false, skippedReason:
|
|
53
|
+
if (updated === text) return { restored: false, skippedReason: "no-change" };
|
|
50
54
|
|
|
51
|
-
const backupPath = `${configPath}.bak.${new Date().toISOString().replace(/[:.]/g,
|
|
55
|
+
const backupPath = `${configPath}.bak.${new Date().toISOString().replace(/[:.]/g, "-")}`;
|
|
52
56
|
await fs.copyFile(configPath, backupPath).catch(() => {});
|
|
53
|
-
await fs.writeFile(configPath, updated,
|
|
57
|
+
await fs.writeFile(configPath, updated, "utf8");
|
|
54
58
|
return { restored: true, skippedReason: null };
|
|
55
59
|
}
|
|
56
60
|
|
|
@@ -60,7 +64,7 @@ async function loadNotifyOriginal(notifyOriginalPath) {
|
|
|
60
64
|
}
|
|
61
65
|
|
|
62
66
|
async function readNotify(configPath) {
|
|
63
|
-
const text = await fs.readFile(configPath,
|
|
67
|
+
const text = await fs.readFile(configPath, "utf8").catch(() => null);
|
|
64
68
|
if (text == null) return null;
|
|
65
69
|
return extractNotify(text);
|
|
66
70
|
}
|
|
@@ -70,7 +74,7 @@ async function upsertCodexNotify({ codexConfigPath, notifyCmd, notifyOriginalPat
|
|
|
70
74
|
configPath: codexConfigPath,
|
|
71
75
|
notifyCmd,
|
|
72
76
|
notifyOriginalPath,
|
|
73
|
-
configLabel:
|
|
77
|
+
configLabel: "Codex config",
|
|
74
78
|
});
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -78,7 +82,7 @@ async function restoreCodexNotify({ codexConfigPath, notifyOriginalPath, notifyC
|
|
|
78
82
|
return restoreNotify({
|
|
79
83
|
configPath: codexConfigPath,
|
|
80
84
|
notifyOriginalPath,
|
|
81
|
-
expectedNotify: notifyCmd
|
|
85
|
+
expectedNotify: notifyCmd,
|
|
82
86
|
});
|
|
83
87
|
}
|
|
84
88
|
|
|
@@ -95,7 +99,7 @@ async function upsertEveryCodeNotify({ codeConfigPath, notifyCmd, notifyOriginal
|
|
|
95
99
|
configPath: codeConfigPath,
|
|
96
100
|
notifyCmd,
|
|
97
101
|
notifyOriginalPath,
|
|
98
|
-
configLabel:
|
|
102
|
+
configLabel: "Every Code config",
|
|
99
103
|
});
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -103,7 +107,7 @@ async function restoreEveryCodeNotify({ codeConfigPath, notifyOriginalPath, noti
|
|
|
103
107
|
return restoreNotify({
|
|
104
108
|
configPath: codeConfigPath,
|
|
105
109
|
notifyOriginalPath,
|
|
106
|
-
expectedNotify: notifyCmd
|
|
110
|
+
expectedNotify: notifyCmd,
|
|
107
111
|
});
|
|
108
112
|
}
|
|
109
113
|
|
|
@@ -130,7 +134,7 @@ function extractNotify(text) {
|
|
|
130
134
|
const m = line.match(/^\s*notify\s*=\s*(.*)\s*$/);
|
|
131
135
|
if (!m) continue;
|
|
132
136
|
|
|
133
|
-
const rhs = (m[1] ||
|
|
137
|
+
const rhs = (m[1] || "").trim();
|
|
134
138
|
const literal = readTomlArrayLiteral(lines, i, rhs);
|
|
135
139
|
if (!literal) continue;
|
|
136
140
|
|
|
@@ -155,7 +159,7 @@ function setNotify(text, notifyCmd) {
|
|
|
155
159
|
replaced = true;
|
|
156
160
|
}
|
|
157
161
|
|
|
158
|
-
const rhs = (m[1] ||
|
|
162
|
+
const rhs = (m[1] || "").trim();
|
|
159
163
|
i = findTomlArrayBlockEnd(lines, i, rhs);
|
|
160
164
|
continue;
|
|
161
165
|
}
|
|
@@ -169,7 +173,7 @@ function setNotify(text, notifyCmd) {
|
|
|
169
173
|
out.splice(headerIdx, 0, notifyLine);
|
|
170
174
|
}
|
|
171
175
|
|
|
172
|
-
return out.join(
|
|
176
|
+
return out.join("\n").replace(/\n+$/, "\n");
|
|
173
177
|
}
|
|
174
178
|
|
|
175
179
|
function removeNotify(text) {
|
|
@@ -179,24 +183,24 @@ function removeNotify(text) {
|
|
|
179
183
|
const line = lines[i];
|
|
180
184
|
const m = line.match(/^\s*notify\s*=\s*(.*)\s*$/);
|
|
181
185
|
if (m) {
|
|
182
|
-
const rhs = (m[1] ||
|
|
186
|
+
const rhs = (m[1] || "").trim();
|
|
183
187
|
i = findTomlArrayBlockEnd(lines, i, rhs);
|
|
184
188
|
continue;
|
|
185
189
|
}
|
|
186
190
|
out.push(line);
|
|
187
191
|
}
|
|
188
|
-
return out.join(
|
|
192
|
+
return out.join("\n").replace(/\n+$/, "\n");
|
|
189
193
|
}
|
|
190
194
|
|
|
191
195
|
function parseTomlStringArray(rhs) {
|
|
192
196
|
// Minimal parser for ["a", "b"] string arrays.
|
|
193
197
|
// Assumes there are no escapes in strings (good enough for our usage).
|
|
194
|
-
if (!rhs.startsWith(
|
|
198
|
+
if (!rhs.startsWith("[") || !rhs.endsWith("]")) return null;
|
|
195
199
|
const inner = rhs.slice(1, -1).trim();
|
|
196
200
|
if (!inner) return [];
|
|
197
201
|
|
|
198
202
|
const parts = [];
|
|
199
|
-
let current =
|
|
203
|
+
let current = "";
|
|
200
204
|
let inString = false;
|
|
201
205
|
let quote = null;
|
|
202
206
|
for (let i = 0; i < inner.length; i++) {
|
|
@@ -205,7 +209,7 @@ function parseTomlStringArray(rhs) {
|
|
|
205
209
|
if (ch === '"' || ch === "'") {
|
|
206
210
|
inString = true;
|
|
207
211
|
quote = ch;
|
|
208
|
-
current =
|
|
212
|
+
current = "";
|
|
209
213
|
}
|
|
210
214
|
continue;
|
|
211
215
|
}
|
|
@@ -222,12 +226,12 @@ function parseTomlStringArray(rhs) {
|
|
|
222
226
|
}
|
|
223
227
|
|
|
224
228
|
function formatTomlStringArray(arr) {
|
|
225
|
-
return `[${arr.map((s) => JSON.stringify(String(s))).join(
|
|
229
|
+
return `[${arr.map((s) => JSON.stringify(String(s))).join(", ")}]`;
|
|
226
230
|
}
|
|
227
231
|
|
|
228
232
|
function readTomlArrayLiteral(lines, startIndex, rhs) {
|
|
229
233
|
const first = rhs.trim();
|
|
230
|
-
if (!first.startsWith(
|
|
234
|
+
if (!first.startsWith("[")) return null;
|
|
231
235
|
|
|
232
236
|
let inString = false;
|
|
233
237
|
let quote = null;
|
|
@@ -243,12 +247,12 @@ function readTomlArrayLiteral(lines, startIndex, rhs) {
|
|
|
243
247
|
quote = ch;
|
|
244
248
|
continue;
|
|
245
249
|
}
|
|
246
|
-
if (ch ===
|
|
250
|
+
if (ch === "[") {
|
|
247
251
|
depth += 1;
|
|
248
252
|
sawOpen = true;
|
|
249
253
|
continue;
|
|
250
254
|
}
|
|
251
|
-
if (ch ===
|
|
255
|
+
if (ch === "]") {
|
|
252
256
|
depth -= 1;
|
|
253
257
|
if (sawOpen && depth === 0) return i;
|
|
254
258
|
}
|
|
@@ -271,7 +275,7 @@ function readTomlArrayLiteral(lines, startIndex, rhs) {
|
|
|
271
275
|
endPos = scanChunk(line);
|
|
272
276
|
if (endPos !== -1) {
|
|
273
277
|
parts.push(line.slice(0, endPos + 1));
|
|
274
|
-
return parts.join(
|
|
278
|
+
return parts.join("\n").trim();
|
|
275
279
|
}
|
|
276
280
|
parts.push(line);
|
|
277
281
|
}
|
|
@@ -281,7 +285,7 @@ function readTomlArrayLiteral(lines, startIndex, rhs) {
|
|
|
281
285
|
|
|
282
286
|
function findTomlArrayBlockEnd(lines, startIndex, rhs) {
|
|
283
287
|
const first = rhs.trim();
|
|
284
|
-
if (!first.startsWith(
|
|
288
|
+
if (!first.startsWith("[")) return startIndex;
|
|
285
289
|
|
|
286
290
|
let inString = false;
|
|
287
291
|
let quote = null;
|
|
@@ -297,12 +301,12 @@ function findTomlArrayBlockEnd(lines, startIndex, rhs) {
|
|
|
297
301
|
quote = ch;
|
|
298
302
|
continue;
|
|
299
303
|
}
|
|
300
|
-
if (ch ===
|
|
304
|
+
if (ch === "[") {
|
|
301
305
|
depth += 1;
|
|
302
306
|
sawOpen = true;
|
|
303
307
|
continue;
|
|
304
308
|
}
|
|
305
|
-
if (ch ===
|
|
309
|
+
if (ch === "]") {
|
|
306
310
|
depth -= 1;
|
|
307
311
|
if (sawOpen && depth === 0) return true;
|
|
308
312
|
}
|
|
@@ -342,5 +346,5 @@ module.exports = {
|
|
|
342
346
|
upsertEveryCodeNotify,
|
|
343
347
|
restoreEveryCodeNotify,
|
|
344
348
|
loadEveryCodeNotifyOriginal,
|
|
345
|
-
readEveryCodeNotify
|
|
349
|
+
readEveryCodeNotify,
|
|
346
350
|
};
|
package/src/lib/debug-flags.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
function stripDebugFlag(argv, env = process.env) {
|
|
2
|
-
const filtered = Array.isArray(argv) ? argv.filter((arg) => arg !==
|
|
3
|
-
const debugEnv = String(env?.VIBEUSAGE_DEBUG ||
|
|
2
|
+
const filtered = Array.isArray(argv) ? argv.filter((arg) => arg !== "--debug") : [];
|
|
3
|
+
const debugEnv = String(env?.VIBEUSAGE_DEBUG || "") === "1";
|
|
4
4
|
return { argv: filtered, debug: filtered.length !== (argv || []).length || debugEnv };
|
|
5
5
|
}
|
|
6
6
|
|
package/src/lib/diagnostics.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
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 { readJson } = require(
|
|
6
|
-
const { readCodexNotify, readEveryCodeNotify } = require(
|
|
7
|
-
const { isClaudeHookConfigured, buildClaudeHookCommand } = require(
|
|
5
|
+
const { readJson } = require("./fs");
|
|
6
|
+
const { readCodexNotify, readEveryCodeNotify } = require("./codex-config");
|
|
7
|
+
const { isClaudeHookConfigured, buildClaudeHookCommand } = require("./claude-config");
|
|
8
8
|
const {
|
|
9
9
|
resolveGeminiConfigDir,
|
|
10
10
|
resolveGeminiSettingsPath,
|
|
11
11
|
buildGeminiHookCommand,
|
|
12
|
-
isGeminiHookConfigured
|
|
13
|
-
} = require(
|
|
14
|
-
const { resolveOpencodeConfigDir, isOpencodePluginInstalled } = require(
|
|
15
|
-
const { normalizeState: normalizeUploadState } = require(
|
|
16
|
-
const { probeOpenclawHookState } = require(
|
|
17
|
-
const { probeOpenclawSessionPluginState } = require(
|
|
18
|
-
const { resolveTrackerPaths } = require(
|
|
12
|
+
isGeminiHookConfigured,
|
|
13
|
+
} = require("./gemini-config");
|
|
14
|
+
const { resolveOpencodeConfigDir, isOpencodePluginInstalled } = require("./opencode-config");
|
|
15
|
+
const { normalizeState: normalizeUploadState } = require("./upload-throttle");
|
|
16
|
+
const { probeOpenclawHookState } = require("./openclaw-hook");
|
|
17
|
+
const { probeOpenclawSessionPluginState } = require("./openclaw-session-plugin");
|
|
18
|
+
const { resolveTrackerPaths } = require("./tracker-paths");
|
|
19
19
|
|
|
20
20
|
async function collectTrackerDiagnostics({
|
|
21
21
|
home = os.homedir(),
|
|
22
|
-
codexHome = process.env.CODEX_HOME || path.join(home,
|
|
23
|
-
codeHome = process.env.CODE_HOME || path.join(home,
|
|
22
|
+
codexHome = process.env.CODEX_HOME || path.join(home, ".codex"),
|
|
23
|
+
codeHome = process.env.CODE_HOME || path.join(home, ".code"),
|
|
24
24
|
} = {}) {
|
|
25
25
|
const { trackerDir, binDir } = await resolveTrackerPaths({ home });
|
|
26
|
-
const configPath = path.join(trackerDir,
|
|
27
|
-
const queuePath = path.join(trackerDir,
|
|
28
|
-
const queueStatePath = path.join(trackerDir,
|
|
29
|
-
const cursorsPath = path.join(trackerDir,
|
|
30
|
-
const notifySignalPath = path.join(trackerDir,
|
|
31
|
-
const openclawSignalPath = path.join(trackerDir,
|
|
32
|
-
const throttlePath = path.join(trackerDir,
|
|
33
|
-
const uploadThrottlePath = path.join(trackerDir,
|
|
34
|
-
const autoRetryPath = path.join(trackerDir,
|
|
35
|
-
const codexConfigPath = path.join(codexHome,
|
|
36
|
-
const codeConfigPath = path.join(codeHome,
|
|
37
|
-
const claudeConfigPath = path.join(home,
|
|
26
|
+
const configPath = path.join(trackerDir, "config.json");
|
|
27
|
+
const queuePath = path.join(trackerDir, "queue.jsonl");
|
|
28
|
+
const queueStatePath = path.join(trackerDir, "queue.state.json");
|
|
29
|
+
const cursorsPath = path.join(trackerDir, "cursors.json");
|
|
30
|
+
const notifySignalPath = path.join(trackerDir, "notify.signal");
|
|
31
|
+
const openclawSignalPath = path.join(trackerDir, "openclaw.signal");
|
|
32
|
+
const throttlePath = path.join(trackerDir, "sync.throttle");
|
|
33
|
+
const uploadThrottlePath = path.join(trackerDir, "upload.throttle.json");
|
|
34
|
+
const autoRetryPath = path.join(trackerDir, "auto.retry.json");
|
|
35
|
+
const codexConfigPath = path.join(codexHome, "config.toml");
|
|
36
|
+
const codeConfigPath = path.join(codeHome, "config.toml");
|
|
37
|
+
const claudeConfigPath = path.join(home, ".claude", "settings.json");
|
|
38
38
|
const geminiConfigDir = resolveGeminiConfigDir({ home, env: process.env });
|
|
39
39
|
const geminiSettingsPath = resolveGeminiSettingsPath({ configDir: geminiConfigDir });
|
|
40
40
|
const opencodeConfigDir = resolveOpencodeConfigDir({ home, env: process.env });
|
|
@@ -58,22 +58,32 @@ async function collectTrackerDiagnostics({
|
|
|
58
58
|
const codexNotify = notifyConfigured ? codexNotifyRaw.map((v) => redactValue(v, home)) : null;
|
|
59
59
|
const everyCodeNotifyRaw = await readEveryCodeNotify(codeConfigPath);
|
|
60
60
|
const everyCodeConfigured = Array.isArray(everyCodeNotifyRaw) && everyCodeNotifyRaw.length > 0;
|
|
61
|
-
const everyCodeNotify = everyCodeConfigured
|
|
62
|
-
|
|
61
|
+
const everyCodeNotify = everyCodeConfigured
|
|
62
|
+
? everyCodeNotifyRaw.map((v) => redactValue(v, home))
|
|
63
|
+
: null;
|
|
64
|
+
const claudeHookCommand = buildClaudeHookCommand(path.join(binDir, "notify.cjs"));
|
|
63
65
|
const claudeHookConfigured = await isClaudeHookConfigured({
|
|
64
66
|
settingsPath: claudeConfigPath,
|
|
65
|
-
hookCommand: claudeHookCommand
|
|
67
|
+
hookCommand: claudeHookCommand,
|
|
66
68
|
});
|
|
67
|
-
const geminiHookCommand = buildGeminiHookCommand(path.join(binDir,
|
|
69
|
+
const geminiHookCommand = buildGeminiHookCommand(path.join(binDir, "notify.cjs"));
|
|
68
70
|
const geminiHookConfigured = await isGeminiHookConfigured({
|
|
69
71
|
settingsPath: geminiSettingsPath,
|
|
70
|
-
hookCommand: geminiHookCommand
|
|
72
|
+
hookCommand: geminiHookCommand,
|
|
73
|
+
});
|
|
74
|
+
const opencodePluginConfigured = await isOpencodePluginInstalled({
|
|
75
|
+
configDir: opencodeConfigDir,
|
|
76
|
+
});
|
|
77
|
+
const openclawSessionPluginState = await probeOpenclawSessionPluginState({
|
|
78
|
+
home,
|
|
79
|
+
trackerDir,
|
|
80
|
+
env: process.env,
|
|
71
81
|
});
|
|
72
|
-
const opencodePluginConfigured = await isOpencodePluginInstalled({ configDir: opencodeConfigDir });
|
|
73
|
-
const openclawSessionPluginState = await probeOpenclawSessionPluginState({ home, trackerDir, env: process.env });
|
|
74
82
|
const openclawHookState = await probeOpenclawHookState({ home, trackerDir, env: process.env });
|
|
75
83
|
|
|
76
|
-
const lastSuccessAt = uploadThrottle.lastSuccessMs
|
|
84
|
+
const lastSuccessAt = uploadThrottle.lastSuccessMs
|
|
85
|
+
? new Date(uploadThrottle.lastSuccessMs).toISOString()
|
|
86
|
+
: null;
|
|
77
87
|
const autoRetryAt = parseEpochMsToIso(autoRetry?.retryAtMs);
|
|
78
88
|
|
|
79
89
|
return {
|
|
@@ -83,7 +93,7 @@ async function collectTrackerDiagnostics({
|
|
|
83
93
|
env: {
|
|
84
94
|
node: process.version,
|
|
85
95
|
platform: process.platform,
|
|
86
|
-
arch: process.arch
|
|
96
|
+
arch: process.arch,
|
|
87
97
|
},
|
|
88
98
|
paths: {
|
|
89
99
|
tracker_dir: redactValue(trackerDir, home),
|
|
@@ -93,23 +103,26 @@ async function collectTrackerDiagnostics({
|
|
|
93
103
|
code_config: redactValue(codeConfigPath, home),
|
|
94
104
|
claude_config: redactValue(claudeConfigPath, home),
|
|
95
105
|
gemini_config: redactValue(geminiSettingsPath, home),
|
|
96
|
-
opencode_config: redactValue(opencodeConfigDir, home)
|
|
106
|
+
opencode_config: redactValue(opencodeConfigDir, home),
|
|
97
107
|
},
|
|
98
108
|
config: {
|
|
99
|
-
base_url: typeof config?.baseUrl ===
|
|
100
|
-
device_token: config?.deviceToken ?
|
|
109
|
+
base_url: typeof config?.baseUrl === "string" ? config.baseUrl : null,
|
|
110
|
+
device_token: config?.deviceToken ? "set" : "unset",
|
|
101
111
|
device_id: maskId(config?.deviceId),
|
|
102
|
-
installed_at: typeof config?.installedAt ===
|
|
112
|
+
installed_at: typeof config?.installedAt === "string" ? config.installedAt : null,
|
|
103
113
|
},
|
|
104
114
|
parse: {
|
|
105
|
-
updated_at: typeof cursors?.updatedAt ===
|
|
106
|
-
file_count:
|
|
115
|
+
updated_at: typeof cursors?.updatedAt === "string" ? cursors.updatedAt : null,
|
|
116
|
+
file_count:
|
|
117
|
+
cursors?.files && typeof cursors.files === "object"
|
|
118
|
+
? Object.keys(cursors.files).length
|
|
119
|
+
: null,
|
|
107
120
|
},
|
|
108
121
|
queue: {
|
|
109
122
|
size_bytes: queueSize,
|
|
110
123
|
offset_bytes: offsetBytes,
|
|
111
124
|
pending_bytes: pendingBytes,
|
|
112
|
-
updated_at: typeof queueState.updatedAt ===
|
|
125
|
+
updated_at: typeof queueState.updatedAt === "string" ? queueState.updatedAt : null,
|
|
113
126
|
},
|
|
114
127
|
notify: {
|
|
115
128
|
last_notify: lastNotify,
|
|
@@ -127,7 +140,7 @@ async function collectTrackerDiagnostics({
|
|
|
127
140
|
openclaw_session_plugin_enabled: Boolean(openclawSessionPluginState?.enabled),
|
|
128
141
|
openclaw_hook_configured: Boolean(openclawHookState?.configured),
|
|
129
142
|
openclaw_hook_linked: Boolean(openclawHookState?.linked),
|
|
130
|
-
openclaw_hook_enabled: Boolean(openclawHookState?.enabled)
|
|
143
|
+
openclaw_hook_enabled: Boolean(openclawHookState?.enabled),
|
|
131
144
|
},
|
|
132
145
|
upload: {
|
|
133
146
|
last_success_at: lastSuccessAt,
|
|
@@ -136,43 +149,43 @@ async function collectTrackerDiagnostics({
|
|
|
136
149
|
last_error: uploadThrottle.lastError
|
|
137
150
|
? {
|
|
138
151
|
at: uploadThrottle.lastErrorAt || null,
|
|
139
|
-
message: redactError(String(uploadThrottle.lastError), home)
|
|
152
|
+
message: redactError(String(uploadThrottle.lastError), home),
|
|
140
153
|
}
|
|
141
|
-
: null
|
|
154
|
+
: null,
|
|
142
155
|
},
|
|
143
156
|
auto_retry: autoRetryAt
|
|
144
157
|
? {
|
|
145
158
|
next_retry_at: autoRetryAt,
|
|
146
|
-
reason: typeof autoRetry?.reason ===
|
|
159
|
+
reason: typeof autoRetry?.reason === "string" ? autoRetry.reason : null,
|
|
147
160
|
pending_bytes: Number.isFinite(Number(autoRetry?.pendingBytes))
|
|
148
161
|
? Math.max(0, Number(autoRetry.pendingBytes))
|
|
149
162
|
: null,
|
|
150
|
-
scheduled_at: typeof autoRetry?.scheduledAt ===
|
|
151
|
-
source: typeof autoRetry?.source ===
|
|
163
|
+
scheduled_at: typeof autoRetry?.scheduledAt === "string" ? autoRetry.scheduledAt : null,
|
|
164
|
+
source: typeof autoRetry?.source === "string" ? autoRetry.source : null,
|
|
152
165
|
}
|
|
153
|
-
: null
|
|
166
|
+
: null,
|
|
154
167
|
};
|
|
155
168
|
}
|
|
156
169
|
|
|
157
170
|
function maskId(v) {
|
|
158
|
-
if (typeof v !==
|
|
171
|
+
if (typeof v !== "string") return null;
|
|
159
172
|
const s = v.trim();
|
|
160
173
|
if (s.length < 12) return null;
|
|
161
174
|
return `${s.slice(0, 8)}…${s.slice(-4)}`;
|
|
162
175
|
}
|
|
163
176
|
|
|
164
177
|
function redactValue(value, home) {
|
|
165
|
-
if (typeof value !==
|
|
166
|
-
if (typeof home !==
|
|
178
|
+
if (typeof value !== "string") return value;
|
|
179
|
+
if (typeof home !== "string" || home.length === 0) return value;
|
|
167
180
|
const homeNorm = home.endsWith(path.sep) ? home.slice(0, -1) : home;
|
|
168
181
|
return value.startsWith(homeNorm) ? `~${value.slice(homeNorm.length)}` : value;
|
|
169
182
|
}
|
|
170
183
|
|
|
171
184
|
function redactError(message, home) {
|
|
172
|
-
if (typeof message !==
|
|
173
|
-
if (typeof home !==
|
|
185
|
+
if (typeof message !== "string") return message;
|
|
186
|
+
if (typeof home !== "string" || home.length === 0) return message;
|
|
174
187
|
const homeNorm = home.endsWith(path.sep) ? home.slice(0, -1) : home;
|
|
175
|
-
return message.split(homeNorm).join(
|
|
188
|
+
return message.split(homeNorm).join("~");
|
|
176
189
|
}
|
|
177
190
|
|
|
178
191
|
async function safeStatSize(p) {
|
|
@@ -186,7 +199,7 @@ async function safeStatSize(p) {
|
|
|
186
199
|
|
|
187
200
|
async function safeReadText(p) {
|
|
188
201
|
try {
|
|
189
|
-
return await fs.readFile(p,
|
|
202
|
+
return await fs.readFile(p, "utf8");
|
|
190
203
|
} catch (_e) {
|
|
191
204
|
return null;
|
|
192
205
|
}
|