jinzd-ai-cli 0.4.118 → 0.4.119
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/dist/{batch-AZ55FS2U.js → batch-7QSGU2F3.js} +2 -2
- package/dist/chunk-2OMBLQP6.js +186 -0
- package/dist/{chunk-W622HYR2.js → chunk-4QMGQO4P.js} +1 -1
- package/dist/{chunk-LO4MSGGO.js → chunk-BS7EUZSO.js} +1 -1
- package/dist/chunk-DIOSW6R4.js +1728 -0
- package/dist/{chunk-Z6BSKKF6.js → chunk-M36KRNHE.js} +14 -1728
- package/dist/{chunk-3YUH5CCS.js → chunk-SWOK6GDC.js} +1 -1
- package/dist/chunk-SYF3BPZK.js +166 -0
- package/dist/{chunk-267DHNV2.js → chunk-UVKO7T4I.js} +1 -1
- package/dist/{chunk-7QZOLJ2R.js → chunk-VYTV2VP7.js} +13 -64
- package/dist/{constants-H7Q7BTHA.js → constants-QVAT2SUA.js} +1 -1
- package/dist/doctor-cli-RZC5PSDE.js +171 -0
- package/dist/electron-server.js +309 -192
- package/dist/{hub-TGZYNCE6.js → hub-SODOTZRX.js} +1 -1
- package/dist/index.js +99 -26
- package/dist/{run-tests-CQME3NR3.js → run-tests-3ZLFXP7F.js} +1 -1
- package/dist/{run-tests-G22G7UMT.js → run-tests-CDY775VC.js} +2 -2
- package/dist/{server-KLAINAR5.js → server-C6Z6TVTZ.js} +47 -21
- package/dist/{server-7NVVJSNQ.js → server-HMJTQCKI.js} +10 -7
- package/dist/{task-orchestrator-BZS2GA2Q.js → task-orchestrator-SDFF4CQ7.js} +8 -5
- package/package.json +1 -1
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
CONFIG_DIR_NAME,
|
|
4
|
+
VERSION
|
|
5
|
+
} from "./chunk-BS7EUZSO.js";
|
|
6
|
+
|
|
7
|
+
// src/diagnostics/crash-log.ts
|
|
8
|
+
import {
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
readdirSync,
|
|
12
|
+
readFileSync,
|
|
13
|
+
statSync,
|
|
14
|
+
unlinkSync,
|
|
15
|
+
writeFileSync
|
|
16
|
+
} from "fs";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
import { homedir, platform, release, arch } from "os";
|
|
19
|
+
var LOGS_DIR_NAME = "logs";
|
|
20
|
+
var CRASH_LOG_PREFIX = "crash-";
|
|
21
|
+
var MAX_CRASH_FILES = 20;
|
|
22
|
+
function getLogsDir(configDir) {
|
|
23
|
+
const base = configDir ?? join(homedir(), CONFIG_DIR_NAME);
|
|
24
|
+
return join(base, LOGS_DIR_NAME);
|
|
25
|
+
}
|
|
26
|
+
function ensureLogsDir(configDir) {
|
|
27
|
+
const dir = getLogsDir(configDir);
|
|
28
|
+
if (!existsSync(dir)) {
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
}
|
|
31
|
+
return dir;
|
|
32
|
+
}
|
|
33
|
+
function isoTimestampForFilename(d = /* @__PURE__ */ new Date()) {
|
|
34
|
+
return d.toISOString().replace(/[:.]/g, "-");
|
|
35
|
+
}
|
|
36
|
+
function writeCrashLog(err, context, configDir) {
|
|
37
|
+
try {
|
|
38
|
+
const dir = ensureLogsDir(configDir);
|
|
39
|
+
const ts = /* @__PURE__ */ new Date();
|
|
40
|
+
const file = join(dir, `${CRASH_LOG_PREFIX}${isoTimestampForFilename(ts)}.log`);
|
|
41
|
+
const errMessage = err instanceof Error ? err.message : String(err);
|
|
42
|
+
const errStack = err instanceof Error ? err.stack ?? "(no stack)" : "(non-Error thrown)";
|
|
43
|
+
const lines = [
|
|
44
|
+
`# ai-cli crash log`,
|
|
45
|
+
`timestamp: ${ts.toISOString()}`,
|
|
46
|
+
`kind: ${context.kind}`,
|
|
47
|
+
`version: ${VERSION}`,
|
|
48
|
+
`node: ${process.version}`,
|
|
49
|
+
`platform: ${platform()} ${release()} (${arch()})`,
|
|
50
|
+
`pid: ${process.pid}`,
|
|
51
|
+
context.provider ? `provider: ${context.provider}` : "",
|
|
52
|
+
context.model ? `model: ${context.model}` : "",
|
|
53
|
+
context.lastAction ? `lastAction: ${truncate(context.lastAction, 500)}` : "",
|
|
54
|
+
"",
|
|
55
|
+
`## message`,
|
|
56
|
+
errMessage,
|
|
57
|
+
"",
|
|
58
|
+
`## stack`,
|
|
59
|
+
errStack,
|
|
60
|
+
""
|
|
61
|
+
].filter(Boolean);
|
|
62
|
+
writeFileSync(file, lines.join("\n"), "utf-8");
|
|
63
|
+
pruneOldCrashes(configDir);
|
|
64
|
+
return file;
|
|
65
|
+
} catch {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function truncate(s, n) {
|
|
70
|
+
return s.length <= n ? s : s.slice(0, n) + "\u2026";
|
|
71
|
+
}
|
|
72
|
+
function pruneOldCrashes(configDir) {
|
|
73
|
+
try {
|
|
74
|
+
const dir = getLogsDir(configDir);
|
|
75
|
+
if (!existsSync(dir)) return;
|
|
76
|
+
const entries = readdirSync(dir).filter((f) => f.startsWith(CRASH_LOG_PREFIX) && f.endsWith(".log")).map((f) => {
|
|
77
|
+
const full = join(dir, f);
|
|
78
|
+
let mtime = 0;
|
|
79
|
+
try {
|
|
80
|
+
mtime = statSync(full).mtimeMs;
|
|
81
|
+
} catch {
|
|
82
|
+
}
|
|
83
|
+
return { file: full, mtime };
|
|
84
|
+
}).sort((a, b) => b.mtime - a.mtime);
|
|
85
|
+
for (const e of entries.slice(MAX_CRASH_FILES)) {
|
|
86
|
+
try {
|
|
87
|
+
unlinkSync(e.file);
|
|
88
|
+
} catch {
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function listRecentCrashes(limit = 10, configDir) {
|
|
95
|
+
const dir = getLogsDir(configDir);
|
|
96
|
+
if (!existsSync(dir)) return [];
|
|
97
|
+
const files = [];
|
|
98
|
+
let names;
|
|
99
|
+
try {
|
|
100
|
+
names = readdirSync(dir);
|
|
101
|
+
} catch {
|
|
102
|
+
return [];
|
|
103
|
+
}
|
|
104
|
+
for (const name of names) {
|
|
105
|
+
if (!name.startsWith(CRASH_LOG_PREFIX) || !name.endsWith(".log")) continue;
|
|
106
|
+
const full = join(dir, name);
|
|
107
|
+
try {
|
|
108
|
+
const st = statSync(full);
|
|
109
|
+
const head = readFileSync(full, "utf-8").split("\n").slice(0, 20);
|
|
110
|
+
const tsLine = head.find((l) => l.startsWith("timestamp:"));
|
|
111
|
+
const kindLine = head.find((l) => l.startsWith("kind:"));
|
|
112
|
+
const msgIdx = head.findIndex((l) => l.trim() === "## message");
|
|
113
|
+
const message = msgIdx >= 0 && head[msgIdx + 1] ? head[msgIdx + 1] : "";
|
|
114
|
+
files.push({
|
|
115
|
+
file: full,
|
|
116
|
+
timestamp: tsLine ? tsLine.replace("timestamp:", "").trim() : st.mtime.toISOString(),
|
|
117
|
+
kind: kindLine ? kindLine.replace("kind:", "").trim() : "unknown",
|
|
118
|
+
message: truncate(message, 200),
|
|
119
|
+
sizeBytes: st.size
|
|
120
|
+
});
|
|
121
|
+
} catch {
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
files.sort((a, b) => a.timestamp < b.timestamp ? 1 : -1);
|
|
125
|
+
return files.slice(0, limit);
|
|
126
|
+
}
|
|
127
|
+
function getConfigDirUsage(configDir) {
|
|
128
|
+
const base = configDir ?? join(homedir(), CONFIG_DIR_NAME);
|
|
129
|
+
if (!existsSync(base)) return { totalBytes: 0, entries: [] };
|
|
130
|
+
const entries = [];
|
|
131
|
+
let total = 0;
|
|
132
|
+
let names;
|
|
133
|
+
try {
|
|
134
|
+
names = readdirSync(base);
|
|
135
|
+
} catch {
|
|
136
|
+
return { totalBytes: 0, entries: [] };
|
|
137
|
+
}
|
|
138
|
+
for (const name of names) {
|
|
139
|
+
const full = join(base, name);
|
|
140
|
+
const bytes = dirSize(full);
|
|
141
|
+
entries.push({ name, bytes });
|
|
142
|
+
total += bytes;
|
|
143
|
+
}
|
|
144
|
+
entries.sort((a, b) => b.bytes - a.bytes);
|
|
145
|
+
return { totalBytes: total, entries };
|
|
146
|
+
}
|
|
147
|
+
function dirSize(path) {
|
|
148
|
+
try {
|
|
149
|
+
const st = statSync(path);
|
|
150
|
+
if (st.isFile()) return st.size;
|
|
151
|
+
if (!st.isDirectory()) return 0;
|
|
152
|
+
let sum = 0;
|
|
153
|
+
for (const name of readdirSync(path)) {
|
|
154
|
+
sum += dirSize(join(path, name));
|
|
155
|
+
}
|
|
156
|
+
return sum;
|
|
157
|
+
} catch {
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export {
|
|
163
|
+
writeCrashLog,
|
|
164
|
+
listRecentCrashes,
|
|
165
|
+
getConfigDirUsage
|
|
166
|
+
};
|
|
@@ -5,7 +5,12 @@ import {
|
|
|
5
5
|
} from "./chunk-3BICTI5M.js";
|
|
6
6
|
import {
|
|
7
7
|
runTestsTool
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4QMGQO4P.js";
|
|
9
|
+
import {
|
|
10
|
+
getDangerLevel,
|
|
11
|
+
isFileWriteTool,
|
|
12
|
+
runTool
|
|
13
|
+
} from "./chunk-2OMBLQP6.js";
|
|
9
14
|
import {
|
|
10
15
|
EnvLoader,
|
|
11
16
|
NetworkError,
|
|
@@ -18,7 +23,7 @@ import {
|
|
|
18
23
|
SUBAGENT_ALLOWED_TOOLS,
|
|
19
24
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
20
25
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
21
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-BS7EUZSO.js";
|
|
22
27
|
import {
|
|
23
28
|
fileCheckpoints
|
|
24
29
|
} from "./chunk-4BKXL7SM.js";
|
|
@@ -33,61 +38,6 @@ import {
|
|
|
33
38
|
loadIndex
|
|
34
39
|
} from "./chunk-6VRJGH25.js";
|
|
35
40
|
|
|
36
|
-
// src/tools/types.ts
|
|
37
|
-
function isFileWriteTool(name) {
|
|
38
|
-
return name === "write_file" || name === "edit_file" || name === "notebook_edit";
|
|
39
|
-
}
|
|
40
|
-
function getDangerLevel(toolName, args) {
|
|
41
|
-
if (toolName.startsWith("mcp__")) return "safe";
|
|
42
|
-
if (toolName === "bash") {
|
|
43
|
-
const cmd = String(args["command"] ?? "");
|
|
44
|
-
if (/\brm\s+[^\n]*(?:-\w*[rRfF]\w*|--recursive|--force)\b/.test(cmd)) return "destructive";
|
|
45
|
-
if (/\brm\s+\S/.test(cmd)) return "destructive";
|
|
46
|
-
if (/\brmdir\b|\bformat\b|\bmkfs\b|\bdd\s+if=|\bshred\b|\bfdisk\b|\bparted\b/.test(cmd)) return "destructive";
|
|
47
|
-
if (/\bshutdown\b|\breboot\b|\bhalt\b|\bpoweroff\b/.test(cmd)) return "destructive";
|
|
48
|
-
if (/\bkill\s+-9\b|\bkillall\b/.test(cmd)) return "destructive";
|
|
49
|
-
if (/\bRemove-Item\b|\bri\s+\S/i.test(cmd)) return "destructive";
|
|
50
|
-
if (/\brd\s+\/s\b|\brmdir\s+\/s\b/i.test(cmd)) return "destructive";
|
|
51
|
-
if (/\bdel\s+\S/.test(cmd)) return "destructive";
|
|
52
|
-
if (/\bShutdown(-Computer)?\b|\bRestart-Computer\b/i.test(cmd)) return "destructive";
|
|
53
|
-
if (/(?:^|[\s|;&])>>?\s*\S/.test(cmd)) return "write";
|
|
54
|
-
if (/\btee\b|\bcp\b|\bmv\b|\bln\s+-s/.test(cmd)) return "write";
|
|
55
|
-
if (/\bchmod\b|\bchown\b/.test(cmd)) return "write";
|
|
56
|
-
if (/\bSet-Content\b|\bOut-File\b|\bAdd-Content\b|\bCopy-Item\b|\bMove-Item\b|\bSet-ItemProperty\b|\bNew-ItemProperty\b/i.test(cmd)) return "write";
|
|
57
|
-
return "safe";
|
|
58
|
-
}
|
|
59
|
-
if (toolName === "write_file") return "write";
|
|
60
|
-
if (toolName === "edit_file") return "write";
|
|
61
|
-
if (toolName === "save_last_response") return "write";
|
|
62
|
-
if (toolName === "run_interactive") {
|
|
63
|
-
const exe = String(args["executable"] ?? "").toLowerCase();
|
|
64
|
-
if (/\b(rm|rmdir|del|format|mkfs|Remove-Item)\b/i.test(exe)) return "destructive";
|
|
65
|
-
if (/\b(bash|sh|zsh|cmd|powershell|pwsh|python|node|ruby|perl)\b/i.test(exe)) return "write";
|
|
66
|
-
return "write";
|
|
67
|
-
}
|
|
68
|
-
if (toolName === "task_create" || toolName === "task_stop") return "write";
|
|
69
|
-
if (toolName === "task_list") return "safe";
|
|
70
|
-
if (toolName === "git_commit") return "write";
|
|
71
|
-
if (toolName === "git_status" || toolName === "git_diff" || toolName === "git_log") return "safe";
|
|
72
|
-
if (toolName === "notebook_edit") return "write";
|
|
73
|
-
if (toolName === "read_file" || toolName === "list_dir" || toolName === "grep_files" || toolName === "glob_files" || toolName === "web_fetch" || toolName === "save_memory" || toolName === "ask_user" || toolName === "write_todos" || toolName === "google_search" || toolName === "spawn_agent" || toolName === "run_tests") return "safe";
|
|
74
|
-
return "write";
|
|
75
|
-
}
|
|
76
|
-
function schemaToJsonSchema(schema) {
|
|
77
|
-
const result = {
|
|
78
|
-
type: schema.type,
|
|
79
|
-
description: schema.description
|
|
80
|
-
};
|
|
81
|
-
if (schema.enum) result["enum"] = schema.enum;
|
|
82
|
-
if (schema.items) result["items"] = schemaToJsonSchema(schema.items);
|
|
83
|
-
if (schema.properties) {
|
|
84
|
-
result["properties"] = Object.fromEntries(
|
|
85
|
-
Object.entries(schema.properties).map(([k, v]) => [k, schemaToJsonSchema(v)])
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
return result;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
41
|
// src/tools/builtin/bash.ts
|
|
92
42
|
import { spawn } from "child_process";
|
|
93
43
|
import { existsSync as existsSync2, readdirSync, statSync } from "fs";
|
|
@@ -1548,7 +1498,7 @@ var ToolExecutor = class {
|
|
|
1548
1498
|
if (action === "auto-approve") {
|
|
1549
1499
|
this.printToolCall(call);
|
|
1550
1500
|
try {
|
|
1551
|
-
const rawContent = await tool.
|
|
1501
|
+
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
1552
1502
|
const content = truncateOutput(rawContent, call.name);
|
|
1553
1503
|
const wasTruncated = content !== rawContent;
|
|
1554
1504
|
this.printToolResult(call.name, rawContent, false, wasTruncated);
|
|
@@ -1567,7 +1517,7 @@ var ToolExecutor = class {
|
|
|
1567
1517
|
if (dangerLevel === "write") this.printDiffPreview(call);
|
|
1568
1518
|
console.log(theme.warning(" \u26A1 Auto-approved (session /yolo mode)"));
|
|
1569
1519
|
try {
|
|
1570
|
-
const rawContent = await tool.
|
|
1520
|
+
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
1571
1521
|
const content = truncateOutput(rawContent, call.name);
|
|
1572
1522
|
const wasTruncated = content !== rawContent;
|
|
1573
1523
|
this.printToolResult(call.name, rawContent, false, wasTruncated);
|
|
@@ -1605,7 +1555,7 @@ var ToolExecutor = class {
|
|
|
1605
1555
|
this.printToolCall(call);
|
|
1606
1556
|
}
|
|
1607
1557
|
try {
|
|
1608
|
-
const rawContent = await tool.
|
|
1558
|
+
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
1609
1559
|
const content = truncateOutput(rawContent, call.name);
|
|
1610
1560
|
const wasTruncated = content !== rawContent;
|
|
1611
1561
|
this.printToolResult(call.name, rawContent, false, wasTruncated);
|
|
@@ -1688,7 +1638,7 @@ var ToolExecutor = class {
|
|
|
1688
1638
|
return;
|
|
1689
1639
|
}
|
|
1690
1640
|
try {
|
|
1691
|
-
const rawContent = await tool.
|
|
1641
|
+
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
1692
1642
|
const content = truncateOutput(rawContent, call.name);
|
|
1693
1643
|
const wasTruncated = content !== rawContent;
|
|
1694
1644
|
this.printToolResult(call.name, rawContent, false, wasTruncated);
|
|
@@ -3849,7 +3799,7 @@ var SubAgentExecutor = class {
|
|
|
3849
3799
|
}
|
|
3850
3800
|
this.printToolCall(call, dangerLevel);
|
|
3851
3801
|
try {
|
|
3852
|
-
const rawContent = await tool.
|
|
3802
|
+
const rawContent = await runTool(tool, call.arguments, call.name);
|
|
3853
3803
|
const content = truncateOutput(rawContent, call.name);
|
|
3854
3804
|
const wasTruncated = content !== rawContent;
|
|
3855
3805
|
this.printToolResult(call.name, rawContent, false, wasTruncated);
|
|
@@ -5263,8 +5213,6 @@ var ToolRegistry = class {
|
|
|
5263
5213
|
};
|
|
5264
5214
|
|
|
5265
5215
|
export {
|
|
5266
|
-
getDangerLevel,
|
|
5267
|
-
schemaToJsonSchema,
|
|
5268
5216
|
initTheme,
|
|
5269
5217
|
theme,
|
|
5270
5218
|
undoStack,
|
|
@@ -5272,6 +5220,7 @@ export {
|
|
|
5272
5220
|
isInterrupted,
|
|
5273
5221
|
requestInterrupt,
|
|
5274
5222
|
resetInterrupt,
|
|
5223
|
+
runWithSessionKey,
|
|
5275
5224
|
rlInternal,
|
|
5276
5225
|
groupCallsByPhase,
|
|
5277
5226
|
runSafePhases,
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getConfigDirUsage,
|
|
4
|
+
listRecentCrashes
|
|
5
|
+
} from "./chunk-SYF3BPZK.js";
|
|
6
|
+
import {
|
|
7
|
+
ProviderRegistry
|
|
8
|
+
} from "./chunk-M36KRNHE.js";
|
|
9
|
+
import {
|
|
10
|
+
ConfigManager
|
|
11
|
+
} from "./chunk-UVKO7T4I.js";
|
|
12
|
+
import {
|
|
13
|
+
getStatsSnapshot,
|
|
14
|
+
getTopFailingTools,
|
|
15
|
+
getTopUsedTools,
|
|
16
|
+
resetStats
|
|
17
|
+
} from "./chunk-2OMBLQP6.js";
|
|
18
|
+
import "./chunk-2ZD3YTVM.js";
|
|
19
|
+
import {
|
|
20
|
+
DEV_STATE_FILE_NAME,
|
|
21
|
+
MEMORY_FILE_NAME,
|
|
22
|
+
VERSION
|
|
23
|
+
} from "./chunk-BS7EUZSO.js";
|
|
24
|
+
import "./chunk-PDX44BCA.js";
|
|
25
|
+
|
|
26
|
+
// src/diagnostics/doctor-cli.ts
|
|
27
|
+
import { existsSync, statSync } from "fs";
|
|
28
|
+
import { join } from "path";
|
|
29
|
+
import { platform, release, arch } from "os";
|
|
30
|
+
async function collect() {
|
|
31
|
+
const config = new ConfigManager();
|
|
32
|
+
const registry = new ProviderRegistry();
|
|
33
|
+
await registry.initialize(
|
|
34
|
+
(id) => config.getApiKey(id),
|
|
35
|
+
(id) => ({
|
|
36
|
+
baseUrl: config.get("customBaseUrls")[id],
|
|
37
|
+
timeout: config.get("timeouts")[id]
|
|
38
|
+
}),
|
|
39
|
+
config.get("customProviders")
|
|
40
|
+
);
|
|
41
|
+
const providers = registry.listAll().map((p) => ({
|
|
42
|
+
id: p.id,
|
|
43
|
+
displayName: p.displayName,
|
|
44
|
+
configured: p.configured
|
|
45
|
+
}));
|
|
46
|
+
const configDir = config.getConfigDir();
|
|
47
|
+
const checkFile = (label, path) => {
|
|
48
|
+
const exists = existsSync(path);
|
|
49
|
+
let sizeBytes = null;
|
|
50
|
+
if (exists) {
|
|
51
|
+
try {
|
|
52
|
+
sizeBytes = statSync(path).size;
|
|
53
|
+
} catch {
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { label, path, exists, sizeBytes };
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
version: VERSION,
|
|
60
|
+
node: process.version,
|
|
61
|
+
platform: `${platform()} ${release()} (${arch()})`,
|
|
62
|
+
configDir,
|
|
63
|
+
providers,
|
|
64
|
+
files: [
|
|
65
|
+
checkFile("config.json", join(configDir, "config.json")),
|
|
66
|
+
checkFile("memory.md", join(configDir, MEMORY_FILE_NAME)),
|
|
67
|
+
checkFile("dev-state.md", join(configDir, DEV_STATE_FILE_NAME))
|
|
68
|
+
],
|
|
69
|
+
recentCrashes: listRecentCrashes(5, configDir),
|
|
70
|
+
diskUsage: getConfigDirUsage(configDir),
|
|
71
|
+
toolStats: (() => {
|
|
72
|
+
const all = getStatsSnapshot();
|
|
73
|
+
const totalCalls = all.reduce((a, b) => a + b.calls, 0);
|
|
74
|
+
const totalFailures = all.reduce((a, b) => a + b.failures, 0);
|
|
75
|
+
return {
|
|
76
|
+
totalCalls,
|
|
77
|
+
totalFailures,
|
|
78
|
+
topUsed: getTopUsedTools(5),
|
|
79
|
+
topFailing: getTopFailingTools(5)
|
|
80
|
+
};
|
|
81
|
+
})()
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function formatBytes(n) {
|
|
85
|
+
if (n < 1024) return `${n} B`;
|
|
86
|
+
if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`;
|
|
87
|
+
if (n < 1024 * 1024 * 1024) return `${(n / 1024 / 1024).toFixed(1)} MB`;
|
|
88
|
+
return `${(n / 1024 / 1024 / 1024).toFixed(2)} GB`;
|
|
89
|
+
}
|
|
90
|
+
async function runDoctorCli(options = {}) {
|
|
91
|
+
if (options.resetStats) {
|
|
92
|
+
resetStats();
|
|
93
|
+
process.stdout.write("Tool stats reset.\n");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
const report = await collect();
|
|
97
|
+
if (options.json) {
|
|
98
|
+
process.stdout.write(JSON.stringify(report, null, 2) + "\n");
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const B = "\x1B[1m";
|
|
102
|
+
const D = "\x1B[2m";
|
|
103
|
+
const G = "\x1B[32m";
|
|
104
|
+
const Y = "\x1B[33m";
|
|
105
|
+
const R_ = "\x1B[31m";
|
|
106
|
+
const RESET = "\x1B[0m";
|
|
107
|
+
const out = [];
|
|
108
|
+
out.push("");
|
|
109
|
+
out.push(`${B}\u{1FA7A} AI-CLI Health Check${RESET}`);
|
|
110
|
+
out.push("");
|
|
111
|
+
out.push(` version: ${report.version}`);
|
|
112
|
+
out.push(` node: ${report.node}`);
|
|
113
|
+
out.push(` platform: ${report.platform}`);
|
|
114
|
+
out.push(` config: ${report.configDir}`);
|
|
115
|
+
out.push("");
|
|
116
|
+
out.push(`${B}API Keys:${RESET}`);
|
|
117
|
+
for (const p of report.providers) {
|
|
118
|
+
const icon = p.configured ? `${G}\u2713${RESET}` : `${D}\u25CB${RESET}`;
|
|
119
|
+
const status = p.configured ? `${G}configured${RESET}` : `${D}not configured${RESET}`;
|
|
120
|
+
out.push(` ${icon} ${p.id.padEnd(14)} ${status}`);
|
|
121
|
+
}
|
|
122
|
+
out.push("");
|
|
123
|
+
out.push(`${B}Config Files:${RESET}`);
|
|
124
|
+
for (const f of report.files) {
|
|
125
|
+
const icon = f.exists ? `${G}\u2713${RESET}` : `${D}\u2013${RESET}`;
|
|
126
|
+
const extra = f.exists && f.sizeBytes !== null ? ` ${D}(${formatBytes(f.sizeBytes)})${RESET}` : "";
|
|
127
|
+
out.push(` ${icon} ${f.label.padEnd(14)} ${f.exists ? f.path : `${D}(not found)${RESET}`}${extra}`);
|
|
128
|
+
}
|
|
129
|
+
out.push("");
|
|
130
|
+
out.push(`${B}Recent Crashes (last 5):${RESET}`);
|
|
131
|
+
if (report.recentCrashes.length === 0) {
|
|
132
|
+
out.push(` ${D}(none \u2014 clean!)${RESET}`);
|
|
133
|
+
} else {
|
|
134
|
+
for (const c of report.recentCrashes) {
|
|
135
|
+
out.push(` ${R_}\u2717${RESET} ${c.timestamp} ${Y}${c.kind}${RESET}`);
|
|
136
|
+
out.push(` ${D}${c.message}${RESET}`);
|
|
137
|
+
out.push(` ${D}${c.file}${RESET}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
out.push("");
|
|
141
|
+
out.push(`${B}Tool Usage:${RESET}`);
|
|
142
|
+
const ts = report.toolStats;
|
|
143
|
+
out.push(` total: ${ts.totalCalls} calls \xB7 ${ts.totalFailures} failures (${ts.totalCalls ? (ts.totalFailures * 100 / ts.totalCalls).toFixed(1) : "0.0"}%)`);
|
|
144
|
+
if (ts.topUsed.length > 0) {
|
|
145
|
+
out.push(` ${D}top used:${RESET}`);
|
|
146
|
+
for (const t of ts.topUsed) {
|
|
147
|
+
const avg = t.calls ? (t.totalDurationMs / t.calls).toFixed(0) : "0";
|
|
148
|
+
out.push(` ${t.name.padEnd(20)} ${String(t.calls).padStart(6)} calls \xB7 avg ${avg}ms`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (ts.topFailing.length > 0) {
|
|
152
|
+
out.push(` ${D}top failing:${RESET}`);
|
|
153
|
+
for (const t of ts.topFailing) {
|
|
154
|
+
const rate = (t.failures * 100 / Math.max(1, t.calls)).toFixed(1);
|
|
155
|
+
out.push(` ${R_}${t.name.padEnd(20)}${RESET} ${t.failures}/${t.calls} (${rate}%) ${D}${t.lastFailureMessage ?? ""}${RESET}`.slice(0, 200));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
out.push("");
|
|
159
|
+
out.push(`${B}Disk Usage (~/.aicli):${RESET}`);
|
|
160
|
+
out.push(` total: ${formatBytes(report.diskUsage.totalBytes)}`);
|
|
161
|
+
for (const e of report.diskUsage.entries.slice(0, 8)) {
|
|
162
|
+
out.push(` ${e.name.padEnd(18)} ${formatBytes(e.bytes)}`);
|
|
163
|
+
}
|
|
164
|
+
out.push("");
|
|
165
|
+
out.push(`${G}\u2713 Health check complete${RESET}`);
|
|
166
|
+
out.push("");
|
|
167
|
+
process.stdout.write(out.join("\n"));
|
|
168
|
+
}
|
|
169
|
+
export {
|
|
170
|
+
runDoctorCli
|
|
171
|
+
};
|