@imisbahk/hive 0.1.2 → 0.1.3
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/FEATURES.md +10 -2
- package/dist/cli/commands/doctor.d.ts +8 -0
- package/dist/cli/commands/doctor.d.ts.map +1 -0
- package/dist/cli/commands/doctor.js +503 -0
- package/dist/cli/commands/doctor.js.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
- package/releases/v1/v0.1/RELEASE-NOTES.md +55 -0
- package/src/cli/commands/doctor.ts +655 -0
- package/src/cli/index.ts +2 -0
package/FEATURES.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
• v0.1.
|
|
1
|
+
• v0.1.3 command surface:
|
|
2
2
|
|
|
3
3
|
CLI commands
|
|
4
4
|
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
- hive config provider → interactive provider/model/key update
|
|
10
10
|
- hive config model → interactive model update
|
|
11
11
|
- hive config key → interactive API key update
|
|
12
|
+
- hive config theme → interactive accent theme picker (live preview)
|
|
12
13
|
- hive config show → show provider/model/agent/key status
|
|
14
|
+
- hive doctor → run full health check diagnostics
|
|
13
15
|
- hive status → full local status report
|
|
14
16
|
- hive nuke → permanently delete local Hive data + keys
|
|
15
17
|
- hive help [command]
|
|
@@ -40,11 +42,17 @@
|
|
|
40
42
|
- /hive config provider (interactive in chat)
|
|
41
43
|
- /hive config model (interactive in chat)
|
|
42
44
|
- /hive config key (interactive in chat)
|
|
45
|
+
- /hive config theme (interactive in chat)
|
|
43
46
|
- /hive init and /hive nuke are shell-only safety commands
|
|
44
47
|
|
|
45
48
|
Current features
|
|
46
49
|
|
|
47
50
|
- Centered “HIVE / Command Centre” UI across command pages
|
|
51
|
+
- Unified CLI accent theme system stored in `~/.hive/hive.db`
|
|
52
|
+
- Built-in themes: amber (default), cyan, rose, slate, green
|
|
53
|
+
- Custom theme hex support with validation (`^#[0-9A-Fa-f]{6}$`)
|
|
54
|
+
- Live, real-time theme preview while navigating `hive config theme`
|
|
55
|
+
- ASCII HIVE wordmark, separators, prompts, step/success indicators now share one accent color
|
|
48
56
|
- Chat-first CLI (hive opens chat)
|
|
49
57
|
- Deprecated hive chat messaging
|
|
50
58
|
- Live / autocomplete with scrollable suggestion viewport
|
|
@@ -52,4 +60,4 @@
|
|
|
52
60
|
- Slash-command hardening (unknown slash commands handled locally, not sent to model)
|
|
53
61
|
- Browser-augmented chat flow for search/browse prompts
|
|
54
62
|
- In-chat provider/model switching without dropping back to shell
|
|
55
|
-
- Local-first storage (~/.hive), prompt loading from .hive/prompts, keychain-backed keys
|
|
63
|
+
- Local-first storage (~/.hive), prompt loading from .hive/prompts, keychain-backed keys
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
interface DoctorOptions {
|
|
3
|
+
showHeader?: boolean;
|
|
4
|
+
}
|
|
5
|
+
export declare function registerDoctorCommand(program: Command): void;
|
|
6
|
+
export declare function runDoctorCommand(options?: DoctorOptions): Promise<void>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AA8BpC,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAYD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAO5D;AAED,wBAAsB,gBAAgB,CAAC,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwKjF"}
|
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
import Database from "better-sqlite3";
|
|
6
|
+
import fetch from "node-fetch";
|
|
7
|
+
import keytar from "keytar";
|
|
8
|
+
import { normalizeProviderName } from "../../providers/base.js";
|
|
9
|
+
import { closeHiveDatabase, getHiveDatabasePath, getHiveHomeDir, getMetaValue, getPrimaryAgent, } from "../../storage/db.js";
|
|
10
|
+
import { BUILT_IN_THEMES, DEFAULT_THEME_HEX, DEFAULT_THEME_NAME, isValidHexColor, } from "../theme.js";
|
|
11
|
+
import { renderError, renderHiveHeader, renderInfo, renderSuccess } from "../ui.js";
|
|
12
|
+
const KEYCHAIN_SERVICE = "hive";
|
|
13
|
+
const PROMPTS_DIRECTORY = "prompts";
|
|
14
|
+
const PROVIDER_PING_TIMEOUT_MS = 5_000;
|
|
15
|
+
const OLLAMA_PING_TIMEOUT_MS = 5_000;
|
|
16
|
+
const DB_SIZE_WARNING_BYTES = 100 * 1024 * 1024;
|
|
17
|
+
const NODE_MAJOR_WARNING_VERSION = 20;
|
|
18
|
+
const CHECK_LABEL_WIDTH = 22;
|
|
19
|
+
export function registerDoctorCommand(program) {
|
|
20
|
+
program
|
|
21
|
+
.command("doctor")
|
|
22
|
+
.description("Run a full Hive health check")
|
|
23
|
+
.action(async () => {
|
|
24
|
+
await runDoctorCommand();
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export async function runDoctorCommand(options = {}) {
|
|
28
|
+
const showHeader = options.showHeader ?? true;
|
|
29
|
+
if (showHeader) {
|
|
30
|
+
renderHiveHeader("Doctor");
|
|
31
|
+
}
|
|
32
|
+
renderInfo("");
|
|
33
|
+
renderInfo("Running diagnostics...");
|
|
34
|
+
renderInfo("");
|
|
35
|
+
const counters = { warnings: 0, errors: 0 };
|
|
36
|
+
const dbPath = getHiveDatabasePath();
|
|
37
|
+
const promptsPath = join(getHiveHomeDir(), PROMPTS_DIRECTORY);
|
|
38
|
+
let dbSizeBytes = 0;
|
|
39
|
+
let db = null;
|
|
40
|
+
let providerName = null;
|
|
41
|
+
let providerLookupError = null;
|
|
42
|
+
let keychainApiKey = null;
|
|
43
|
+
const databaseCheck = checkDatabase(dbPath);
|
|
44
|
+
dbSizeBytes = databaseCheck.sizeBytes;
|
|
45
|
+
db = databaseCheck.ok ? databaseCheck.db : null;
|
|
46
|
+
let agentName = "missing";
|
|
47
|
+
if (db) {
|
|
48
|
+
const agent = getPrimaryAgent(db);
|
|
49
|
+
if (agent) {
|
|
50
|
+
agentName = agent.agent_name?.trim() ? agent.agent_name : agent.name;
|
|
51
|
+
renderSuccess(formatCheckLine("Agent initialized", agentName));
|
|
52
|
+
try {
|
|
53
|
+
providerName = normalizeProviderName(agent.provider);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
providerName = null;
|
|
57
|
+
providerLookupError = `unsupported (${agent.provider})`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
renderFailure("Agent initialized", "not initialized", counters);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
renderFailure("Agent initialized", "not checked (database unavailable)", counters);
|
|
66
|
+
}
|
|
67
|
+
if (databaseCheck.ok) {
|
|
68
|
+
renderSuccess(formatCheckLine("Database", `${displayPath(dbPath)} (${formatBytes(dbSizeBytes)})`));
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
renderFailure("Database", databaseCheck.message, counters);
|
|
72
|
+
}
|
|
73
|
+
if (dbSizeBytes > DB_SIZE_WARNING_BYTES) {
|
|
74
|
+
renderWarning("Database size", `${formatBytes(dbSizeBytes)} exceeds ${formatBytes(DB_SIZE_WARNING_BYTES)}`, counters);
|
|
75
|
+
}
|
|
76
|
+
if (providerName) {
|
|
77
|
+
if (providerName === "ollama") {
|
|
78
|
+
renderSuccess(formatCheckLine("API Key", "not required (ollama)"));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
keychainApiKey = await readKeychainApiKey(providerName);
|
|
82
|
+
if (keychainApiKey && keychainApiKey.trim().length > 0) {
|
|
83
|
+
renderSuccess(formatCheckLine("API Key", "set"));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
renderFailure("API Key", "missing in keychain", counters);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (providerLookupError) {
|
|
91
|
+
renderFailure("API Key", "not checked (provider unsupported)", counters);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
renderFailure("API Key", "not checked (provider unavailable)", counters);
|
|
95
|
+
}
|
|
96
|
+
if (providerName) {
|
|
97
|
+
const providerReachable = await checkProviderReachable(providerName, keychainApiKey);
|
|
98
|
+
if (providerReachable.ok) {
|
|
99
|
+
renderSuccess(formatCheckLine("Provider", `${providerName} — reachable`));
|
|
100
|
+
}
|
|
101
|
+
else if (providerReachable.warning) {
|
|
102
|
+
renderWarning("Provider", `${providerName} — ${providerReachable.message}`, counters);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
renderFailure("Provider", `${providerName} — ${providerReachable.message}`, counters);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else if (providerLookupError) {
|
|
109
|
+
renderFailure("Provider", providerLookupError, counters);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
renderFailure("Provider", "not checked (provider unavailable)", counters);
|
|
113
|
+
}
|
|
114
|
+
const promptsCheck = checkPromptsDirectory(promptsPath);
|
|
115
|
+
if (promptsCheck.ok) {
|
|
116
|
+
renderSuccess(formatCheckLine("Prompts", `${promptsCheck.fileCount} files loaded`));
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
renderFailure("Prompts", promptsCheck.message, counters);
|
|
120
|
+
}
|
|
121
|
+
if (db) {
|
|
122
|
+
const theme = resolveThemeDetails(db);
|
|
123
|
+
renderSuccess(formatCheckLine("Theme", `${theme.name} ${theme.hex}`));
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
renderFailure("Theme", "not checked (database unavailable)", counters);
|
|
127
|
+
}
|
|
128
|
+
const nodeCheck = checkNodeVersion(process.version);
|
|
129
|
+
if (nodeCheck.ok) {
|
|
130
|
+
renderSuccess(formatCheckLine("Node version", process.version));
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
renderWarning("Node version", nodeCheck.message, counters);
|
|
134
|
+
}
|
|
135
|
+
const playwrightCheck = await checkPlaywrightInstallation();
|
|
136
|
+
if (playwrightCheck.ok) {
|
|
137
|
+
renderSuccess(formatCheckLine("Playwright", "chromium installed"));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
renderFailure("Playwright", playwrightCheck.message, counters);
|
|
141
|
+
}
|
|
142
|
+
if (providerName === "ollama") {
|
|
143
|
+
const ollamaCheck = await checkOllamaRunning();
|
|
144
|
+
if (ollamaCheck.ok) {
|
|
145
|
+
renderSuccess(formatCheckLine("Ollama", "running"));
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
renderWarning("Ollama", "not running", counters);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (db) {
|
|
152
|
+
const messageCount = countRowsIfTableExists(db, "messages");
|
|
153
|
+
const conversationCount = countRowsIfTableExists(db, "conversations");
|
|
154
|
+
const episodeCount = countRowsIfTableExists(db, "episodes");
|
|
155
|
+
if (episodeCount === null) {
|
|
156
|
+
renderInfo(formatInfoLine("Memory", "episodes table not found"));
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
renderInfo(formatInfoLine("Memory", `${episodeCount} episodes stored`));
|
|
160
|
+
}
|
|
161
|
+
if (messageCount === null) {
|
|
162
|
+
renderInfo(formatInfoLine("Messages", "messages table not found"));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
renderInfo(formatInfoLine("Messages", `${messageCount} total`));
|
|
166
|
+
}
|
|
167
|
+
if (conversationCount === null) {
|
|
168
|
+
renderInfo(formatInfoLine("Conversations", "conversations table not found"));
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
renderInfo(formatInfoLine("Conversations", `${conversationCount} total`));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
renderInfo(formatInfoLine("Memory", "not checked"));
|
|
176
|
+
renderInfo(formatInfoLine("Conversations", "not checked"));
|
|
177
|
+
}
|
|
178
|
+
renderInfo("");
|
|
179
|
+
renderSummary(counters);
|
|
180
|
+
if (db) {
|
|
181
|
+
closeHiveDatabase(db);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
function checkDatabase(databasePath) {
|
|
185
|
+
if (!fs.existsSync(databasePath)) {
|
|
186
|
+
return {
|
|
187
|
+
ok: false,
|
|
188
|
+
message: `${displayPath(databasePath)} not found`,
|
|
189
|
+
sizeBytes: 0,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const stats = fs.statSync(databasePath);
|
|
193
|
+
if (!stats.isFile()) {
|
|
194
|
+
return {
|
|
195
|
+
ok: false,
|
|
196
|
+
message: `${displayPath(databasePath)} is not a file`,
|
|
197
|
+
sizeBytes: 0,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
const db = new Database(databasePath, {
|
|
202
|
+
readonly: true,
|
|
203
|
+
fileMustExist: true,
|
|
204
|
+
});
|
|
205
|
+
const integrity = db.pragma("integrity_check", { simple: true });
|
|
206
|
+
if (integrity !== "ok") {
|
|
207
|
+
db.close();
|
|
208
|
+
return {
|
|
209
|
+
ok: false,
|
|
210
|
+
message: `integrity check failed (${String(integrity)})`,
|
|
211
|
+
sizeBytes: stats.size,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
return { ok: true, db, sizeBytes: stats.size };
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
return {
|
|
218
|
+
ok: false,
|
|
219
|
+
message: `unreadable (${formatError(error)})`,
|
|
220
|
+
sizeBytes: stats.size,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
async function readKeychainApiKey(providerName) {
|
|
225
|
+
try {
|
|
226
|
+
return await keytar.getPassword(KEYCHAIN_SERVICE, providerName);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return null;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function checkProviderReachable(providerName, apiKey) {
|
|
233
|
+
const target = buildProviderPingTarget(providerName, apiKey);
|
|
234
|
+
if (!target) {
|
|
235
|
+
return {
|
|
236
|
+
ok: false,
|
|
237
|
+
message: "missing API key",
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
try {
|
|
241
|
+
const response = await fetchWithTimeout(target.url, {
|
|
242
|
+
method: "GET",
|
|
243
|
+
headers: target.headers,
|
|
244
|
+
}, PROVIDER_PING_TIMEOUT_MS);
|
|
245
|
+
if (response.ok) {
|
|
246
|
+
return { ok: true };
|
|
247
|
+
}
|
|
248
|
+
if (response.status === 401 || response.status === 403) {
|
|
249
|
+
return { ok: false, message: `auth failed (${response.status})` };
|
|
250
|
+
}
|
|
251
|
+
if (response.status >= 500) {
|
|
252
|
+
return { ok: false, warning: true, message: `service unavailable (${response.status})` };
|
|
253
|
+
}
|
|
254
|
+
return { ok: false, message: `HTTP ${response.status}` };
|
|
255
|
+
}
|
|
256
|
+
catch (error) {
|
|
257
|
+
return {
|
|
258
|
+
ok: false,
|
|
259
|
+
warning: isTimeoutError(error),
|
|
260
|
+
message: isTimeoutError(error) ? "timeout after 5s" : formatError(error),
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function buildProviderPingTarget(providerName, apiKey) {
|
|
265
|
+
switch (providerName) {
|
|
266
|
+
case "openai":
|
|
267
|
+
return buildOpenAICompatiblePing(process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1", apiKey);
|
|
268
|
+
case "google":
|
|
269
|
+
return buildOpenAICompatiblePing(process.env.GOOGLE_BASE_URL ?? "https://generativelanguage.googleapis.com/v1beta/openai", apiKey);
|
|
270
|
+
case "groq":
|
|
271
|
+
return buildOpenAICompatiblePing(process.env.GROQ_BASE_URL ?? "https://api.groq.com/openai/v1", apiKey);
|
|
272
|
+
case "mistral":
|
|
273
|
+
return buildOpenAICompatiblePing(process.env.MISTRAL_BASE_URL ?? "https://api.mistral.ai/v1", apiKey);
|
|
274
|
+
case "openrouter":
|
|
275
|
+
return buildOpenAICompatiblePing(process.env.OPENROUTER_BASE_URL ?? "https://openrouter.ai/api/v1", apiKey);
|
|
276
|
+
case "together":
|
|
277
|
+
return buildOpenAICompatiblePing(process.env.TOGETHER_BASE_URL ?? "https://api.together.xyz/v1", apiKey);
|
|
278
|
+
case "ollama":
|
|
279
|
+
return {
|
|
280
|
+
url: "http://localhost:11434/api/tags",
|
|
281
|
+
};
|
|
282
|
+
case "anthropic":
|
|
283
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
return {
|
|
287
|
+
url: "https://api.anthropic.com/v1/models",
|
|
288
|
+
headers: {
|
|
289
|
+
"x-api-key": apiKey,
|
|
290
|
+
"anthropic-version": "2023-06-01",
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
default:
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
function buildOpenAICompatiblePing(baseUrl, apiKey) {
|
|
298
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
299
|
+
return null;
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
url: `${baseUrl.replace(/\/$/, "")}/models`,
|
|
303
|
+
headers: {
|
|
304
|
+
authorization: `Bearer ${apiKey}`,
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
function checkPromptsDirectory(promptsPath) {
|
|
309
|
+
if (!fs.existsSync(promptsPath)) {
|
|
310
|
+
return {
|
|
311
|
+
ok: false,
|
|
312
|
+
message: `${ensureTrailingSlash(displayPath(promptsPath))} missing`,
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const stats = fs.statSync(promptsPath);
|
|
316
|
+
if (!stats.isDirectory()) {
|
|
317
|
+
return {
|
|
318
|
+
ok: false,
|
|
319
|
+
message: `${displayPath(promptsPath)} is not a directory`,
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
const fileCount = countFilesRecursively(promptsPath);
|
|
323
|
+
if (fileCount <= 0) {
|
|
324
|
+
return {
|
|
325
|
+
ok: false,
|
|
326
|
+
message: `${ensureTrailingSlash(displayPath(promptsPath))} has no files`,
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return {
|
|
330
|
+
ok: true,
|
|
331
|
+
fileCount,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
function checkNodeVersion(version) {
|
|
335
|
+
const major = parseNodeMajorVersion(version);
|
|
336
|
+
if (major !== null && major >= NODE_MAJOR_WARNING_VERSION) {
|
|
337
|
+
return {
|
|
338
|
+
ok: true,
|
|
339
|
+
message: version,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
return {
|
|
343
|
+
ok: false,
|
|
344
|
+
message: `${version} (recommended v20+)`,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
function parseNodeMajorVersion(version) {
|
|
348
|
+
const match = /^v(\d+)/.exec(version.trim());
|
|
349
|
+
if (!match) {
|
|
350
|
+
return null;
|
|
351
|
+
}
|
|
352
|
+
const major = Number.parseInt(match[1] ?? "", 10);
|
|
353
|
+
return Number.isNaN(major) ? null : major;
|
|
354
|
+
}
|
|
355
|
+
async function checkPlaywrightInstallation() {
|
|
356
|
+
try {
|
|
357
|
+
const playwright = (await import("playwright"));
|
|
358
|
+
const executablePath = playwright.chromium?.executablePath();
|
|
359
|
+
if (!executablePath || !fs.existsSync(executablePath)) {
|
|
360
|
+
return { ok: false, message: "chromium not installed" };
|
|
361
|
+
}
|
|
362
|
+
return { ok: true };
|
|
363
|
+
}
|
|
364
|
+
catch {
|
|
365
|
+
return { ok: false, message: "playwright not installed" };
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
async function checkOllamaRunning() {
|
|
369
|
+
try {
|
|
370
|
+
const response = await fetchWithTimeout("http://localhost:11434", { method: "GET" }, OLLAMA_PING_TIMEOUT_MS);
|
|
371
|
+
return { ok: response.ok };
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
return { ok: false };
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
function resolveThemeDetails(db) {
|
|
378
|
+
const rawThemeName = getMetaValue(db, "theme");
|
|
379
|
+
const rawThemeHex = getMetaValue(db, "theme_hex");
|
|
380
|
+
if (rawThemeName && rawThemeName in BUILT_IN_THEMES) {
|
|
381
|
+
const name = rawThemeName;
|
|
382
|
+
return { name, hex: BUILT_IN_THEMES[name] };
|
|
383
|
+
}
|
|
384
|
+
if (rawThemeName === "custom" && rawThemeHex && isValidHexColor(rawThemeHex)) {
|
|
385
|
+
return { name: "custom", hex: rawThemeHex.toUpperCase() };
|
|
386
|
+
}
|
|
387
|
+
return {
|
|
388
|
+
name: DEFAULT_THEME_NAME,
|
|
389
|
+
hex: DEFAULT_THEME_HEX,
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function countRowsIfTableExists(db, tableName) {
|
|
393
|
+
if (!tableExists(db, tableName)) {
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
const row = db
|
|
397
|
+
.prepare(`SELECT COUNT(1) AS count FROM ${tableName}`)
|
|
398
|
+
.get();
|
|
399
|
+
return row.count;
|
|
400
|
+
}
|
|
401
|
+
function tableExists(db, tableName) {
|
|
402
|
+
const row = db
|
|
403
|
+
.prepare("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1")
|
|
404
|
+
.get(tableName);
|
|
405
|
+
return Boolean(row);
|
|
406
|
+
}
|
|
407
|
+
function countFilesRecursively(path) {
|
|
408
|
+
let total = 0;
|
|
409
|
+
const entries = fs.readdirSync(path, { withFileTypes: true });
|
|
410
|
+
for (const entry of entries) {
|
|
411
|
+
const absolutePath = join(path, entry.name);
|
|
412
|
+
if (entry.isDirectory()) {
|
|
413
|
+
total += countFilesRecursively(absolutePath);
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (entry.isFile()) {
|
|
417
|
+
total += 1;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return total;
|
|
421
|
+
}
|
|
422
|
+
function formatCheckLine(label, value) {
|
|
423
|
+
return `${label.padEnd(CHECK_LABEL_WIDTH, " ")} ${value}`;
|
|
424
|
+
}
|
|
425
|
+
function formatInfoLine(label, value) {
|
|
426
|
+
return `· ${formatCheckLine(label, value)}`;
|
|
427
|
+
}
|
|
428
|
+
function renderFailure(label, value, counters) {
|
|
429
|
+
counters.errors += 1;
|
|
430
|
+
renderError(`✗ ${formatCheckLine(label, value)}`);
|
|
431
|
+
}
|
|
432
|
+
function renderWarning(label, value, counters) {
|
|
433
|
+
counters.warnings += 1;
|
|
434
|
+
renderError(`✗ ${formatCheckLine(label, value)}`);
|
|
435
|
+
}
|
|
436
|
+
function renderSummary(counters) {
|
|
437
|
+
if (counters.errors === 0 && counters.warnings === 0) {
|
|
438
|
+
renderSuccess("All checks passed.");
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
const warningWord = counters.warnings === 1 ? "warning" : "warnings";
|
|
442
|
+
const errorWord = counters.errors === 1 ? "error" : "errors";
|
|
443
|
+
const summary = `${counters.warnings} ${warningWord}, ${counters.errors} ${errorWord}.`;
|
|
444
|
+
if (counters.errors > 0) {
|
|
445
|
+
renderError(summary);
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
renderInfo(summary);
|
|
449
|
+
}
|
|
450
|
+
async function fetchWithTimeout(url, init, timeoutMs) {
|
|
451
|
+
const controller = new AbortController();
|
|
452
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
453
|
+
try {
|
|
454
|
+
return await fetch(url, {
|
|
455
|
+
method: init.method,
|
|
456
|
+
headers: init.headers,
|
|
457
|
+
signal: controller.signal,
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
finally {
|
|
461
|
+
clearTimeout(timer);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
function isTimeoutError(error) {
|
|
465
|
+
if (!error || typeof error !== "object") {
|
|
466
|
+
return false;
|
|
467
|
+
}
|
|
468
|
+
const maybeError = error;
|
|
469
|
+
return maybeError.name === "AbortError" || maybeError.type === "aborted";
|
|
470
|
+
}
|
|
471
|
+
function formatBytes(bytes) {
|
|
472
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
473
|
+
let unitIndex = 0;
|
|
474
|
+
let value = bytes;
|
|
475
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
476
|
+
value /= 1024;
|
|
477
|
+
unitIndex += 1;
|
|
478
|
+
}
|
|
479
|
+
if (unitIndex === 0) {
|
|
480
|
+
return `${value} ${units[unitIndex]}`;
|
|
481
|
+
}
|
|
482
|
+
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
483
|
+
}
|
|
484
|
+
function displayPath(path) {
|
|
485
|
+
const home = homedir();
|
|
486
|
+
if (path === home) {
|
|
487
|
+
return "~";
|
|
488
|
+
}
|
|
489
|
+
if (path.startsWith(`${home}/`)) {
|
|
490
|
+
return `~/${path.slice(home.length + 1)}`;
|
|
491
|
+
}
|
|
492
|
+
return path;
|
|
493
|
+
}
|
|
494
|
+
function ensureTrailingSlash(path) {
|
|
495
|
+
return path.endsWith("/") ? path : `${path}/`;
|
|
496
|
+
}
|
|
497
|
+
function formatError(error) {
|
|
498
|
+
if (error instanceof Error) {
|
|
499
|
+
return error.message;
|
|
500
|
+
}
|
|
501
|
+
return String(error);
|
|
502
|
+
}
|
|
503
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../../src/cli/commands/doctor.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import "dotenv/config";
|
|
|
3
3
|
import { Command } from "commander";
|
|
4
4
|
import { registerChatCommand, runChatCommand } from "./commands/chat.js";
|
|
5
5
|
import { registerConfigCommand } from "./commands/config.js";
|
|
6
|
+
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
6
7
|
import { registerInitCommand } from "./commands/init.js";
|
|
7
8
|
import { registerNukeCommand } from "./commands/nuke.js";
|
|
8
9
|
import { registerStatusCommand } from "./commands/status.js";
|
|
@@ -15,6 +16,7 @@ program
|
|
|
15
16
|
registerInitCommand(program);
|
|
16
17
|
registerChatCommand(program);
|
|
17
18
|
registerConfigCommand(program);
|
|
19
|
+
registerDoctorCommand(program);
|
|
18
20
|
registerStatusCommand(program);
|
|
19
21
|
registerNukeCommand(program);
|
|
20
22
|
const argv = process.argv.slice(2);
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAExD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,MAAM,CAAC;KACZ,WAAW,CAAC,8DAA8D,CAAC;KAC3E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,KAAK,IAAI,EAAE,CAAC;AAEZ,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,cAAc,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,IAAI,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAc;IAC5C,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAc;IACtC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IAC3B,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,OAAO,WAAW,IAAI,MAAM,CAAC;AAC/B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";AAEA,OAAO,eAAe,CAAC;AAEvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAExD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,MAAM,CAAC;KACZ,WAAW,CAAC,8DAA8D,CAAC;KAC3E,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAC7B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAC/B,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAE7B,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,KAAK,IAAI,EAAE,CAAC;AAEZ,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,cAAc,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,IAAI,sBAAsB,CAAC,IAAI,CAAC,EAAE,CAAC;YACjC,gBAAgB,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC3B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;YACrB,OAAO;QACT,CAAC;QAED,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3B,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB,CAAC,IAAc;IAC5C,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAC9E,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAc;IACtC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC;IAC3B,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7D,OAAO,WAAW,IAAI,MAAM,CAAC;AAC/B,CAAC"}
|
package/package.json
CHANGED
|
@@ -44,3 +44,58 @@ hive init
|
|
|
44
44
|
```bash
|
|
45
45
|
npm install -g @imisbahk/hive@0.1.1
|
|
46
46
|
```
|
|
47
|
+
|
|
48
|
+
## 🐝 v0.1.2 — Themes + Live Accent Preview
|
|
49
|
+
|
|
50
|
+
### What's in v0.1.2
|
|
51
|
+
|
|
52
|
+
- New `hive config theme` command to set the CLI accent theme.
|
|
53
|
+
- Built-in theme options:
|
|
54
|
+
- `amber` (`#FFA500`) default beehive accent
|
|
55
|
+
- `cyan` (`#00BCD4`)
|
|
56
|
+
- `rose` (`#FF4081`)
|
|
57
|
+
- `slate` (`#90A4AE`)
|
|
58
|
+
- `green` (`#00E676`)
|
|
59
|
+
- `custom` (user-provided hex)
|
|
60
|
+
- Live theme preview: moving through the picker updates the UI accent in real time before selection.
|
|
61
|
+
- Theme persistence in local DB metadata (`~/.hive/hive.db`):
|
|
62
|
+
- `theme`
|
|
63
|
+
- `theme_hex`
|
|
64
|
+
- Accent color is now consistent across the command centre UI:
|
|
65
|
+
- ASCII HIVE wordmark
|
|
66
|
+
- separators
|
|
67
|
+
- prompt symbol (`›`)
|
|
68
|
+
- agent name prefix in chat
|
|
69
|
+
- success indicator (`✓`)
|
|
70
|
+
- step indicator (`›`)
|
|
71
|
+
- New in-chat shortcut: `/hive config theme`
|
|
72
|
+
|
|
73
|
+
### Upgrade
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
npm install -g @imisbahk/hive@0.1.2
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## 🐝 v0.2.0 — Doctor (Health Checks)
|
|
80
|
+
|
|
81
|
+
### What's in v0.2.0
|
|
82
|
+
|
|
83
|
+
- New `hive doctor` command runs a full diagnostic pass across local Hive setup.
|
|
84
|
+
- Live, sequential output (no spinner) so checks feel immediate as they complete.
|
|
85
|
+
- Checks include:
|
|
86
|
+
- Agent initialized (DB record exists)
|
|
87
|
+
- Database readable + integrity check + size warning when large
|
|
88
|
+
- API key present in OS keychain
|
|
89
|
+
- Provider reachability (5s timeout)
|
|
90
|
+
- Prompts folder present with files
|
|
91
|
+
- Theme selection from local DB metadata
|
|
92
|
+
- Node version warning if < v20
|
|
93
|
+
- Playwright + Chromium installed
|
|
94
|
+
- Ollama running when provider is `ollama`
|
|
95
|
+
- Basic DB stats (messages, conversations, episodes when table exists)
|
|
96
|
+
|
|
97
|
+
### Upgrade
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
npm install -g @imisbahk/hive@0.2.0
|
|
101
|
+
```
|
|
@@ -0,0 +1,655 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import process from "node:process";
|
|
5
|
+
|
|
6
|
+
import Database from "better-sqlite3";
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import fetch from "node-fetch";
|
|
9
|
+
import keytar from "keytar";
|
|
10
|
+
|
|
11
|
+
import { normalizeProviderName, type ProviderName } from "../../providers/base.js";
|
|
12
|
+
import {
|
|
13
|
+
closeHiveDatabase,
|
|
14
|
+
getHiveDatabasePath,
|
|
15
|
+
getHiveHomeDir,
|
|
16
|
+
getMetaValue,
|
|
17
|
+
getPrimaryAgent,
|
|
18
|
+
type HiveDatabase,
|
|
19
|
+
} from "../../storage/db.js";
|
|
20
|
+
import {
|
|
21
|
+
BUILT_IN_THEMES,
|
|
22
|
+
DEFAULT_THEME_HEX,
|
|
23
|
+
DEFAULT_THEME_NAME,
|
|
24
|
+
isValidHexColor,
|
|
25
|
+
type ThemeName,
|
|
26
|
+
} from "../theme.js";
|
|
27
|
+
import { renderError, renderHiveHeader, renderInfo, renderSuccess } from "../ui.js";
|
|
28
|
+
|
|
29
|
+
const KEYCHAIN_SERVICE = "hive";
|
|
30
|
+
const PROMPTS_DIRECTORY = "prompts";
|
|
31
|
+
const PROVIDER_PING_TIMEOUT_MS = 5_000;
|
|
32
|
+
const OLLAMA_PING_TIMEOUT_MS = 5_000;
|
|
33
|
+
const DB_SIZE_WARNING_BYTES = 100 * 1024 * 1024;
|
|
34
|
+
const NODE_MAJOR_WARNING_VERSION = 20;
|
|
35
|
+
const CHECK_LABEL_WIDTH = 22;
|
|
36
|
+
|
|
37
|
+
interface DoctorOptions {
|
|
38
|
+
showHeader?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface ThemeDetails {
|
|
42
|
+
name: ThemeName;
|
|
43
|
+
hex: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
interface CheckCounter {
|
|
47
|
+
warnings: number;
|
|
48
|
+
errors: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function registerDoctorCommand(program: Command): void {
|
|
52
|
+
program
|
|
53
|
+
.command("doctor")
|
|
54
|
+
.description("Run a full Hive health check")
|
|
55
|
+
.action(async () => {
|
|
56
|
+
await runDoctorCommand();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export async function runDoctorCommand(options: DoctorOptions = {}): Promise<void> {
|
|
61
|
+
const showHeader = options.showHeader ?? true;
|
|
62
|
+
if (showHeader) {
|
|
63
|
+
renderHiveHeader("Doctor");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
renderInfo("");
|
|
67
|
+
renderInfo("Running diagnostics...");
|
|
68
|
+
renderInfo("");
|
|
69
|
+
|
|
70
|
+
const counters: CheckCounter = { warnings: 0, errors: 0 };
|
|
71
|
+
const dbPath = getHiveDatabasePath();
|
|
72
|
+
const promptsPath = join(getHiveHomeDir(), PROMPTS_DIRECTORY);
|
|
73
|
+
|
|
74
|
+
let dbSizeBytes = 0;
|
|
75
|
+
let db: HiveDatabase | null = null;
|
|
76
|
+
let providerName: ProviderName | null = null;
|
|
77
|
+
let providerLookupError: string | null = null;
|
|
78
|
+
let keychainApiKey: string | null = null;
|
|
79
|
+
|
|
80
|
+
const databaseCheck = checkDatabase(dbPath);
|
|
81
|
+
dbSizeBytes = databaseCheck.sizeBytes;
|
|
82
|
+
db = databaseCheck.ok ? databaseCheck.db : null;
|
|
83
|
+
|
|
84
|
+
let agentName = "missing";
|
|
85
|
+
if (db) {
|
|
86
|
+
const agent = getPrimaryAgent(db);
|
|
87
|
+
if (agent) {
|
|
88
|
+
agentName = agent.agent_name?.trim() ? agent.agent_name : agent.name;
|
|
89
|
+
renderSuccess(formatCheckLine("Agent initialized", agentName));
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
providerName = normalizeProviderName(agent.provider);
|
|
93
|
+
} catch {
|
|
94
|
+
providerName = null;
|
|
95
|
+
providerLookupError = `unsupported (${agent.provider})`;
|
|
96
|
+
}
|
|
97
|
+
} else {
|
|
98
|
+
renderFailure("Agent initialized", "not initialized", counters);
|
|
99
|
+
}
|
|
100
|
+
} else {
|
|
101
|
+
renderFailure("Agent initialized", "not checked (database unavailable)", counters);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (databaseCheck.ok) {
|
|
105
|
+
renderSuccess(formatCheckLine("Database", `${displayPath(dbPath)} (${formatBytes(dbSizeBytes)})`));
|
|
106
|
+
} else {
|
|
107
|
+
renderFailure("Database", databaseCheck.message, counters);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (dbSizeBytes > DB_SIZE_WARNING_BYTES) {
|
|
111
|
+
renderWarning(
|
|
112
|
+
"Database size",
|
|
113
|
+
`${formatBytes(dbSizeBytes)} exceeds ${formatBytes(DB_SIZE_WARNING_BYTES)}`,
|
|
114
|
+
counters,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (providerName) {
|
|
119
|
+
if (providerName === "ollama") {
|
|
120
|
+
renderSuccess(formatCheckLine("API Key", "not required (ollama)"));
|
|
121
|
+
} else {
|
|
122
|
+
keychainApiKey = await readKeychainApiKey(providerName);
|
|
123
|
+
if (keychainApiKey && keychainApiKey.trim().length > 0) {
|
|
124
|
+
renderSuccess(formatCheckLine("API Key", "set"));
|
|
125
|
+
} else {
|
|
126
|
+
renderFailure("API Key", "missing in keychain", counters);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
} else if (providerLookupError) {
|
|
130
|
+
renderFailure("API Key", "not checked (provider unsupported)", counters);
|
|
131
|
+
} else {
|
|
132
|
+
renderFailure("API Key", "not checked (provider unavailable)", counters);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (providerName) {
|
|
136
|
+
const providerReachable = await checkProviderReachable(providerName, keychainApiKey);
|
|
137
|
+
if (providerReachable.ok) {
|
|
138
|
+
renderSuccess(
|
|
139
|
+
formatCheckLine("Provider", `${providerName} — reachable`),
|
|
140
|
+
);
|
|
141
|
+
} else if (providerReachable.warning) {
|
|
142
|
+
renderWarning(
|
|
143
|
+
"Provider",
|
|
144
|
+
`${providerName} — ${providerReachable.message}`,
|
|
145
|
+
counters,
|
|
146
|
+
);
|
|
147
|
+
} else {
|
|
148
|
+
renderFailure("Provider", `${providerName} — ${providerReachable.message}`, counters);
|
|
149
|
+
}
|
|
150
|
+
} else if (providerLookupError) {
|
|
151
|
+
renderFailure("Provider", providerLookupError, counters);
|
|
152
|
+
} else {
|
|
153
|
+
renderFailure("Provider", "not checked (provider unavailable)", counters);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const promptsCheck = checkPromptsDirectory(promptsPath);
|
|
157
|
+
if (promptsCheck.ok) {
|
|
158
|
+
renderSuccess(
|
|
159
|
+
formatCheckLine("Prompts", `${promptsCheck.fileCount} files loaded`),
|
|
160
|
+
);
|
|
161
|
+
} else {
|
|
162
|
+
renderFailure("Prompts", promptsCheck.message, counters);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (db) {
|
|
166
|
+
const theme = resolveThemeDetails(db);
|
|
167
|
+
renderSuccess(formatCheckLine("Theme", `${theme.name} ${theme.hex}`));
|
|
168
|
+
} else {
|
|
169
|
+
renderFailure("Theme", "not checked (database unavailable)", counters);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const nodeCheck = checkNodeVersion(process.version);
|
|
173
|
+
if (nodeCheck.ok) {
|
|
174
|
+
renderSuccess(formatCheckLine("Node version", process.version));
|
|
175
|
+
} else {
|
|
176
|
+
renderWarning("Node version", nodeCheck.message, counters);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const playwrightCheck = await checkPlaywrightInstallation();
|
|
180
|
+
if (playwrightCheck.ok) {
|
|
181
|
+
renderSuccess(formatCheckLine("Playwright", "chromium installed"));
|
|
182
|
+
} else {
|
|
183
|
+
renderFailure("Playwright", playwrightCheck.message, counters);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (providerName === "ollama") {
|
|
187
|
+
const ollamaCheck = await checkOllamaRunning();
|
|
188
|
+
if (ollamaCheck.ok) {
|
|
189
|
+
renderSuccess(formatCheckLine("Ollama", "running"));
|
|
190
|
+
} else {
|
|
191
|
+
renderWarning("Ollama", "not running", counters);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (db) {
|
|
196
|
+
const messageCount = countRowsIfTableExists(db, "messages");
|
|
197
|
+
const conversationCount = countRowsIfTableExists(db, "conversations");
|
|
198
|
+
const episodeCount = countRowsIfTableExists(db, "episodes");
|
|
199
|
+
|
|
200
|
+
if (episodeCount === null) {
|
|
201
|
+
renderInfo(formatInfoLine("Memory", "episodes table not found"));
|
|
202
|
+
} else {
|
|
203
|
+
renderInfo(formatInfoLine("Memory", `${episodeCount} episodes stored`));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (messageCount === null) {
|
|
207
|
+
renderInfo(formatInfoLine("Messages", "messages table not found"));
|
|
208
|
+
} else {
|
|
209
|
+
renderInfo(formatInfoLine("Messages", `${messageCount} total`));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (conversationCount === null) {
|
|
213
|
+
renderInfo(formatInfoLine("Conversations", "conversations table not found"));
|
|
214
|
+
} else {
|
|
215
|
+
renderInfo(formatInfoLine("Conversations", `${conversationCount} total`));
|
|
216
|
+
}
|
|
217
|
+
} else {
|
|
218
|
+
renderInfo(formatInfoLine("Memory", "not checked"));
|
|
219
|
+
renderInfo(formatInfoLine("Conversations", "not checked"));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
renderInfo("");
|
|
223
|
+
renderSummary(counters);
|
|
224
|
+
|
|
225
|
+
if (db) {
|
|
226
|
+
closeHiveDatabase(db);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function checkDatabase(
|
|
231
|
+
databasePath: string,
|
|
232
|
+
): { ok: true; db: HiveDatabase; sizeBytes: number } | { ok: false; message: string; sizeBytes: number } {
|
|
233
|
+
if (!fs.existsSync(databasePath)) {
|
|
234
|
+
return {
|
|
235
|
+
ok: false,
|
|
236
|
+
message: `${displayPath(databasePath)} not found`,
|
|
237
|
+
sizeBytes: 0,
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const stats = fs.statSync(databasePath);
|
|
242
|
+
if (!stats.isFile()) {
|
|
243
|
+
return {
|
|
244
|
+
ok: false,
|
|
245
|
+
message: `${displayPath(databasePath)} is not a file`,
|
|
246
|
+
sizeBytes: 0,
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
const db = new Database(databasePath, {
|
|
252
|
+
readonly: true,
|
|
253
|
+
fileMustExist: true,
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
const integrity = db.pragma("integrity_check", { simple: true });
|
|
257
|
+
if (integrity !== "ok") {
|
|
258
|
+
db.close();
|
|
259
|
+
return {
|
|
260
|
+
ok: false,
|
|
261
|
+
message: `integrity check failed (${String(integrity)})`,
|
|
262
|
+
sizeBytes: stats.size,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return { ok: true, db, sizeBytes: stats.size };
|
|
267
|
+
} catch (error) {
|
|
268
|
+
return {
|
|
269
|
+
ok: false,
|
|
270
|
+
message: `unreadable (${formatError(error)})`,
|
|
271
|
+
sizeBytes: stats.size,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
async function readKeychainApiKey(providerName: ProviderName): Promise<string | null> {
|
|
277
|
+
try {
|
|
278
|
+
return await keytar.getPassword(KEYCHAIN_SERVICE, providerName);
|
|
279
|
+
} catch {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async function checkProviderReachable(
|
|
285
|
+
providerName: ProviderName,
|
|
286
|
+
apiKey: string | null,
|
|
287
|
+
): Promise<{ ok: boolean; warning?: boolean; message?: string }> {
|
|
288
|
+
const target = buildProviderPingTarget(providerName, apiKey);
|
|
289
|
+
if (!target) {
|
|
290
|
+
return {
|
|
291
|
+
ok: false,
|
|
292
|
+
message: "missing API key",
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
try {
|
|
297
|
+
const response = await fetchWithTimeout(target.url, {
|
|
298
|
+
method: "GET",
|
|
299
|
+
headers: target.headers,
|
|
300
|
+
}, PROVIDER_PING_TIMEOUT_MS);
|
|
301
|
+
|
|
302
|
+
if (response.ok) {
|
|
303
|
+
return { ok: true };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (response.status === 401 || response.status === 403) {
|
|
307
|
+
return { ok: false, message: `auth failed (${response.status})` };
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (response.status >= 500) {
|
|
311
|
+
return { ok: false, warning: true, message: `service unavailable (${response.status})` };
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
return { ok: false, message: `HTTP ${response.status}` };
|
|
315
|
+
} catch (error) {
|
|
316
|
+
return {
|
|
317
|
+
ok: false,
|
|
318
|
+
warning: isTimeoutError(error),
|
|
319
|
+
message: isTimeoutError(error) ? "timeout after 5s" : formatError(error),
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
function buildProviderPingTarget(
|
|
325
|
+
providerName: ProviderName,
|
|
326
|
+
apiKey: string | null,
|
|
327
|
+
): { url: string; headers?: Record<string, string> } | null {
|
|
328
|
+
switch (providerName) {
|
|
329
|
+
case "openai":
|
|
330
|
+
return buildOpenAICompatiblePing(
|
|
331
|
+
process.env.OPENAI_BASE_URL ?? "https://api.openai.com/v1",
|
|
332
|
+
apiKey,
|
|
333
|
+
);
|
|
334
|
+
case "google":
|
|
335
|
+
return buildOpenAICompatiblePing(
|
|
336
|
+
process.env.GOOGLE_BASE_URL ?? "https://generativelanguage.googleapis.com/v1beta/openai",
|
|
337
|
+
apiKey,
|
|
338
|
+
);
|
|
339
|
+
case "groq":
|
|
340
|
+
return buildOpenAICompatiblePing(
|
|
341
|
+
process.env.GROQ_BASE_URL ?? "https://api.groq.com/openai/v1",
|
|
342
|
+
apiKey,
|
|
343
|
+
);
|
|
344
|
+
case "mistral":
|
|
345
|
+
return buildOpenAICompatiblePing(
|
|
346
|
+
process.env.MISTRAL_BASE_URL ?? "https://api.mistral.ai/v1",
|
|
347
|
+
apiKey,
|
|
348
|
+
);
|
|
349
|
+
case "openrouter":
|
|
350
|
+
return buildOpenAICompatiblePing(
|
|
351
|
+
process.env.OPENROUTER_BASE_URL ?? "https://openrouter.ai/api/v1",
|
|
352
|
+
apiKey,
|
|
353
|
+
);
|
|
354
|
+
case "together":
|
|
355
|
+
return buildOpenAICompatiblePing(
|
|
356
|
+
process.env.TOGETHER_BASE_URL ?? "https://api.together.xyz/v1",
|
|
357
|
+
apiKey,
|
|
358
|
+
);
|
|
359
|
+
case "ollama":
|
|
360
|
+
return {
|
|
361
|
+
url: "http://localhost:11434/api/tags",
|
|
362
|
+
};
|
|
363
|
+
case "anthropic":
|
|
364
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return {
|
|
369
|
+
url: "https://api.anthropic.com/v1/models",
|
|
370
|
+
headers: {
|
|
371
|
+
"x-api-key": apiKey,
|
|
372
|
+
"anthropic-version": "2023-06-01",
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
default:
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
function buildOpenAICompatiblePing(
|
|
381
|
+
baseUrl: string,
|
|
382
|
+
apiKey: string | null,
|
|
383
|
+
): { url: string; headers?: Record<string, string> } | null {
|
|
384
|
+
if (!apiKey || apiKey.trim().length === 0) {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return {
|
|
389
|
+
url: `${baseUrl.replace(/\/$/, "")}/models`,
|
|
390
|
+
headers: {
|
|
391
|
+
authorization: `Bearer ${apiKey}`,
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function checkPromptsDirectory(
|
|
397
|
+
promptsPath: string,
|
|
398
|
+
): { ok: true; fileCount: number } | { ok: false; message: string } {
|
|
399
|
+
if (!fs.existsSync(promptsPath)) {
|
|
400
|
+
return {
|
|
401
|
+
ok: false,
|
|
402
|
+
message: `${ensureTrailingSlash(displayPath(promptsPath))} missing`,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const stats = fs.statSync(promptsPath);
|
|
407
|
+
if (!stats.isDirectory()) {
|
|
408
|
+
return {
|
|
409
|
+
ok: false,
|
|
410
|
+
message: `${displayPath(promptsPath)} is not a directory`,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
const fileCount = countFilesRecursively(promptsPath);
|
|
415
|
+
if (fileCount <= 0) {
|
|
416
|
+
return {
|
|
417
|
+
ok: false,
|
|
418
|
+
message: `${ensureTrailingSlash(displayPath(promptsPath))} has no files`,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
return {
|
|
423
|
+
ok: true,
|
|
424
|
+
fileCount,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function checkNodeVersion(version: string): { ok: boolean; message: string } {
|
|
429
|
+
const major = parseNodeMajorVersion(version);
|
|
430
|
+
if (major !== null && major >= NODE_MAJOR_WARNING_VERSION) {
|
|
431
|
+
return {
|
|
432
|
+
ok: true,
|
|
433
|
+
message: version,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
return {
|
|
438
|
+
ok: false,
|
|
439
|
+
message: `${version} (recommended v20+)`,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function parseNodeMajorVersion(version: string): number | null {
|
|
444
|
+
const match = /^v(\d+)/.exec(version.trim());
|
|
445
|
+
if (!match) {
|
|
446
|
+
return null;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const major = Number.parseInt(match[1] ?? "", 10);
|
|
450
|
+
return Number.isNaN(major) ? null : major;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
async function checkPlaywrightInstallation(): Promise<
|
|
454
|
+
{ ok: true } | { ok: false; message: string }
|
|
455
|
+
> {
|
|
456
|
+
try {
|
|
457
|
+
const playwright = (await import("playwright")) as {
|
|
458
|
+
chromium?: {
|
|
459
|
+
executablePath: () => string;
|
|
460
|
+
};
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
const executablePath = playwright.chromium?.executablePath();
|
|
464
|
+
if (!executablePath || !fs.existsSync(executablePath)) {
|
|
465
|
+
return { ok: false, message: "chromium not installed" };
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return { ok: true };
|
|
469
|
+
} catch {
|
|
470
|
+
return { ok: false, message: "playwright not installed" };
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function checkOllamaRunning(): Promise<{ ok: boolean }> {
|
|
475
|
+
try {
|
|
476
|
+
const response = await fetchWithTimeout(
|
|
477
|
+
"http://localhost:11434",
|
|
478
|
+
{ method: "GET" },
|
|
479
|
+
OLLAMA_PING_TIMEOUT_MS,
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
return { ok: response.ok };
|
|
483
|
+
} catch {
|
|
484
|
+
return { ok: false };
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function resolveThemeDetails(db: HiveDatabase): ThemeDetails {
|
|
489
|
+
const rawThemeName = getMetaValue(db, "theme");
|
|
490
|
+
const rawThemeHex = getMetaValue(db, "theme_hex");
|
|
491
|
+
|
|
492
|
+
if (rawThemeName && rawThemeName in BUILT_IN_THEMES) {
|
|
493
|
+
const name = rawThemeName as keyof typeof BUILT_IN_THEMES;
|
|
494
|
+
return { name, hex: BUILT_IN_THEMES[name] };
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
if (rawThemeName === "custom" && rawThemeHex && isValidHexColor(rawThemeHex)) {
|
|
498
|
+
return { name: "custom", hex: rawThemeHex.toUpperCase() };
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return {
|
|
502
|
+
name: DEFAULT_THEME_NAME,
|
|
503
|
+
hex: DEFAULT_THEME_HEX,
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function countRowsIfTableExists(db: HiveDatabase, tableName: string): number | null {
|
|
508
|
+
if (!tableExists(db, tableName)) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
const row = db
|
|
513
|
+
.prepare(`SELECT COUNT(1) AS count FROM ${tableName}`)
|
|
514
|
+
.get() as { count: number };
|
|
515
|
+
|
|
516
|
+
return row.count;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function tableExists(db: HiveDatabase, tableName: string): boolean {
|
|
520
|
+
const row = db
|
|
521
|
+
.prepare(
|
|
522
|
+
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ? LIMIT 1",
|
|
523
|
+
)
|
|
524
|
+
.get(tableName) as { name: string } | undefined;
|
|
525
|
+
|
|
526
|
+
return Boolean(row);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function countFilesRecursively(path: string): number {
|
|
530
|
+
let total = 0;
|
|
531
|
+
const entries = fs.readdirSync(path, { withFileTypes: true });
|
|
532
|
+
|
|
533
|
+
for (const entry of entries) {
|
|
534
|
+
const absolutePath = join(path, entry.name);
|
|
535
|
+
if (entry.isDirectory()) {
|
|
536
|
+
total += countFilesRecursively(absolutePath);
|
|
537
|
+
continue;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (entry.isFile()) {
|
|
541
|
+
total += 1;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
return total;
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
function formatCheckLine(label: string, value: string): string {
|
|
549
|
+
return `${label.padEnd(CHECK_LABEL_WIDTH, " ")} ${value}`;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
function formatInfoLine(label: string, value: string): string {
|
|
553
|
+
return `· ${formatCheckLine(label, value)}`;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
function renderFailure(label: string, value: string, counters: CheckCounter): void {
|
|
557
|
+
counters.errors += 1;
|
|
558
|
+
renderError(`✗ ${formatCheckLine(label, value)}`);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function renderWarning(label: string, value: string, counters: CheckCounter): void {
|
|
562
|
+
counters.warnings += 1;
|
|
563
|
+
renderError(`✗ ${formatCheckLine(label, value)}`);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function renderSummary(counters: CheckCounter): void {
|
|
567
|
+
if (counters.errors === 0 && counters.warnings === 0) {
|
|
568
|
+
renderSuccess("All checks passed.");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const warningWord = counters.warnings === 1 ? "warning" : "warnings";
|
|
573
|
+
const errorWord = counters.errors === 1 ? "error" : "errors";
|
|
574
|
+
const summary = `${counters.warnings} ${warningWord}, ${counters.errors} ${errorWord}.`;
|
|
575
|
+
|
|
576
|
+
if (counters.errors > 0) {
|
|
577
|
+
renderError(summary);
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
renderInfo(summary);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
async function fetchWithTimeout(
|
|
585
|
+
url: string,
|
|
586
|
+
init: {
|
|
587
|
+
method: "GET";
|
|
588
|
+
headers?: Record<string, string>;
|
|
589
|
+
},
|
|
590
|
+
timeoutMs: number,
|
|
591
|
+
): Promise<Awaited<ReturnType<typeof fetch>>> {
|
|
592
|
+
const controller = new AbortController();
|
|
593
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
594
|
+
|
|
595
|
+
try {
|
|
596
|
+
return await fetch(url, {
|
|
597
|
+
method: init.method,
|
|
598
|
+
headers: init.headers,
|
|
599
|
+
signal: controller.signal,
|
|
600
|
+
});
|
|
601
|
+
} finally {
|
|
602
|
+
clearTimeout(timer);
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function isTimeoutError(error: unknown): boolean {
|
|
607
|
+
if (!error || typeof error !== "object") {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
const maybeError = error as { name?: string; type?: string };
|
|
612
|
+
return maybeError.name === "AbortError" || maybeError.type === "aborted";
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function formatBytes(bytes: number): string {
|
|
616
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
617
|
+
let unitIndex = 0;
|
|
618
|
+
let value = bytes;
|
|
619
|
+
|
|
620
|
+
while (value >= 1024 && unitIndex < units.length - 1) {
|
|
621
|
+
value /= 1024;
|
|
622
|
+
unitIndex += 1;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
if (unitIndex === 0) {
|
|
626
|
+
return `${value} ${units[unitIndex]}`;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
return `${value.toFixed(1)} ${units[unitIndex]}`;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
function displayPath(path: string): string {
|
|
633
|
+
const home = homedir();
|
|
634
|
+
if (path === home) {
|
|
635
|
+
return "~";
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
if (path.startsWith(`${home}/`)) {
|
|
639
|
+
return `~/${path.slice(home.length + 1)}`;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
return path;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function ensureTrailingSlash(path: string): string {
|
|
646
|
+
return path.endsWith("/") ? path : `${path}/`;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function formatError(error: unknown): string {
|
|
650
|
+
if (error instanceof Error) {
|
|
651
|
+
return error.message;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
return String(error);
|
|
655
|
+
}
|
package/src/cli/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
|
|
7
7
|
import { registerChatCommand, runChatCommand } from "./commands/chat.js";
|
|
8
8
|
import { registerConfigCommand } from "./commands/config.js";
|
|
9
|
+
import { registerDoctorCommand } from "./commands/doctor.js";
|
|
9
10
|
import { registerInitCommand } from "./commands/init.js";
|
|
10
11
|
import { registerNukeCommand } from "./commands/nuke.js";
|
|
11
12
|
import { registerStatusCommand } from "./commands/status.js";
|
|
@@ -21,6 +22,7 @@ program
|
|
|
21
22
|
registerInitCommand(program);
|
|
22
23
|
registerChatCommand(program);
|
|
23
24
|
registerConfigCommand(program);
|
|
25
|
+
registerDoctorCommand(program);
|
|
24
26
|
registerStatusCommand(program);
|
|
25
27
|
registerNukeCommand(program);
|
|
26
28
|
|