@prestyj/cli 4.2.77 → 4.3.34
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 +2 -2
- package/dist/cli.js +335 -85
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +33 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +42 -26
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +17 -2
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/auto-update.d.ts +16 -3
- package/dist/core/auto-update.d.ts.map +1 -1
- package/dist/core/auto-update.js +126 -56
- package/dist/core/auto-update.js.map +1 -1
- package/dist/core/auto-update.test.d.ts +2 -0
- package/dist/core/auto-update.test.d.ts.map +1 -0
- package/dist/core/auto-update.test.js +205 -0
- package/dist/core/auto-update.test.js.map +1 -0
- package/dist/core/compaction/compactor.d.ts +18 -1
- package/dist/core/compaction/compactor.d.ts.map +1 -1
- package/dist/core/compaction/compactor.js +48 -7
- package/dist/core/compaction/compactor.js.map +1 -1
- package/dist/core/compaction/compactor.test.js +39 -6
- package/dist/core/compaction/compactor.test.js.map +1 -1
- package/dist/core/compaction/token-estimator.d.ts.map +1 -1
- package/dist/core/compaction/token-estimator.js +4 -0
- package/dist/core/compaction/token-estimator.js.map +1 -1
- package/dist/core/compaction/token-estimator.test.js +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +15 -2
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +74 -5
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/oauth/types.d.ts +1 -0
- package/dist/core/oauth/types.d.ts.map +1 -1
- package/dist/core/prompt-commands.d.ts.map +1 -1
- package/dist/core/prompt-commands.js +134 -0
- package/dist/core/prompt-commands.js.map +1 -1
- package/dist/core/settings-manager.d.ts +5 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +14 -2
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/interactive.js +1 -1
- package/dist/interactive.js.map +1 -1
- package/dist/modes/agent-home-mode.d.ts +21 -0
- package/dist/modes/agent-home-mode.d.ts.map +1 -0
- package/dist/modes/agent-home-mode.js +553 -0
- package/dist/modes/agent-home-mode.js.map +1 -0
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +30 -14
- package/dist/system-prompt.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +9 -2
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +4 -2
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +48 -33
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/edit.test.js +80 -20
- package/dist/tools/edit.test.js.map +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +12 -0
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +6 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/overflow.js +1 -1
- package/dist/tools/overflow.js.map +1 -1
- package/dist/tools/plan-mode.test.js +2 -2
- package/dist/tools/plan-mode.test.js.map +1 -1
- package/dist/tools/read.d.ts.map +1 -1
- package/dist/tools/read.js +14 -1
- package/dist/tools/read.js.map +1 -1
- package/dist/tools/subagent.d.ts.map +1 -1
- package/dist/tools/subagent.js +20 -3
- package/dist/tools/subagent.js.map +1 -1
- package/dist/tools/web-search.d.ts +9 -0
- package/dist/tools/web-search.d.ts.map +1 -0
- package/dist/tools/web-search.js +241 -0
- package/dist/tools/web-search.js.map +1 -0
- package/dist/tools/write.js +2 -2
- package/dist/tools/write.js.map +1 -1
- package/dist/tools/write.test.js +7 -9
- package/dist/tools/write.test.js.map +1 -1
- package/dist/ui/App.d.ts +4 -0
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/App.js +324 -161
- package/dist/ui/App.js.map +1 -1
- package/dist/ui/activity-phrases.d.ts.map +1 -1
- package/dist/ui/activity-phrases.js +74 -129
- package/dist/ui/activity-phrases.js.map +1 -1
- package/dist/ui/components/ActivityIndicator.d.ts +3 -1
- package/dist/ui/components/ActivityIndicator.d.ts.map +1 -1
- package/dist/ui/components/ActivityIndicator.js +5 -2
- package/dist/ui/components/ActivityIndicator.js.map +1 -1
- package/dist/ui/components/AssistantMessage.d.ts +1 -0
- package/dist/ui/components/AssistantMessage.d.ts.map +1 -1
- package/dist/ui/components/AssistantMessage.js +2 -2
- package/dist/ui/components/AssistantMessage.js.map +1 -1
- package/dist/ui/components/DiffFrame.d.ts +10 -0
- package/dist/ui/components/DiffFrame.d.ts.map +1 -0
- package/dist/ui/components/DiffFrame.js +13 -0
- package/dist/ui/components/DiffFrame.js.map +1 -0
- package/dist/ui/components/Footer.d.ts +2 -3
- package/dist/ui/components/Footer.d.ts.map +1 -1
- package/dist/ui/components/Footer.js +6 -5
- package/dist/ui/components/Footer.js.map +1 -1
- package/dist/ui/components/InputArea.d.ts.map +1 -1
- package/dist/ui/components/InputArea.js +297 -8
- package/dist/ui/components/InputArea.js.map +1 -1
- package/dist/ui/components/Markdown.d.ts +1 -1
- package/dist/ui/components/Markdown.d.ts.map +1 -1
- package/dist/ui/components/Markdown.js +25 -213
- package/dist/ui/components/Markdown.js.map +1 -1
- package/dist/ui/components/ModelSelector.d.ts.map +1 -1
- package/dist/ui/components/ModelSelector.js +10 -2
- package/dist/ui/components/ModelSelector.js.map +1 -1
- package/dist/ui/components/Ratchet.d.ts.map +1 -1
- package/dist/ui/components/Ratchet.js +10 -3
- package/dist/ui/components/Ratchet.js.map +1 -1
- package/dist/ui/components/StreamingArea.d.ts.map +1 -1
- package/dist/ui/components/StreamingArea.js +2 -2
- package/dist/ui/components/StreamingArea.js.map +1 -1
- package/dist/ui/components/SubAgentPanel.d.ts.map +1 -1
- package/dist/ui/components/SubAgentPanel.js +5 -1
- package/dist/ui/components/SubAgentPanel.js.map +1 -1
- package/dist/ui/components/ThemeSelector.d.ts +9 -0
- package/dist/ui/components/ThemeSelector.d.ts.map +1 -0
- package/dist/ui/components/ThemeSelector.js +25 -0
- package/dist/ui/components/ThemeSelector.js.map +1 -0
- package/dist/ui/components/ToolExecution.d.ts +2 -0
- package/dist/ui/components/ToolExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolExecution.js +95 -30
- package/dist/ui/components/ToolExecution.js.map +1 -1
- package/dist/ui/components/ToolGroupExecution.d.ts +3 -2
- package/dist/ui/components/ToolGroupExecution.d.ts.map +1 -1
- package/dist/ui/components/ToolGroupExecution.js +68 -51
- package/dist/ui/components/ToolGroupExecution.js.map +1 -1
- package/dist/ui/hooks/stall-retry-duplicate.test.d.ts +2 -0
- package/dist/ui/hooks/stall-retry-duplicate.test.d.ts.map +1 -0
- package/dist/ui/hooks/stall-retry-duplicate.test.js +94 -0
- package/dist/ui/hooks/stall-retry-duplicate.test.js.map +1 -0
- package/dist/ui/hooks/useAgentLoop.d.ts +10 -3
- package/dist/ui/hooks/useAgentLoop.d.ts.map +1 -1
- package/dist/ui/hooks/useAgentLoop.js +131 -16
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/dist/ui/hooks/useDoublePress.d.ts +8 -0
- package/dist/ui/hooks/useDoublePress.d.ts.map +1 -0
- package/dist/ui/hooks/useDoublePress.js +44 -0
- package/dist/ui/hooks/useDoublePress.js.map +1 -0
- package/dist/ui/hooks/useTerminalProgress.d.ts +1 -1
- package/dist/ui/hooks/useTerminalProgress.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalProgress.js +18 -15
- package/dist/ui/hooks/useTerminalProgress.js.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.d.ts +3 -6
- package/dist/ui/hooks/useTerminalTitle.d.ts.map +1 -1
- package/dist/ui/hooks/useTerminalTitle.js +10 -19
- package/dist/ui/hooks/useTerminalTitle.js.map +1 -1
- package/dist/ui/live-item-flush.d.ts +4 -0
- package/dist/ui/live-item-flush.d.ts.map +1 -1
- package/dist/ui/live-item-flush.js +36 -0
- package/dist/ui/live-item-flush.js.map +1 -1
- package/dist/ui/login.d.ts.map +1 -1
- package/dist/ui/login.js +3 -0
- package/dist/ui/login.js.map +1 -1
- package/dist/ui/render.d.ts +3 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/dist/ui/render.js +9 -3
- package/dist/ui/render.js.map +1 -1
- package/dist/ui/stores/create-store.d.ts +15 -0
- package/dist/ui/stores/create-store.d.ts.map +1 -0
- package/dist/ui/stores/create-store.js +30 -0
- package/dist/ui/stores/create-store.js.map +1 -0
- package/dist/ui/stores/index.d.ts +3 -0
- package/dist/ui/stores/index.d.ts.map +1 -0
- package/dist/ui/stores/index.js +3 -0
- package/dist/ui/stores/index.js.map +1 -0
- package/dist/ui/stores/taskbar-store.d.ts +19 -0
- package/dist/ui/stores/taskbar-store.d.ts.map +1 -0
- package/dist/ui/stores/taskbar-store.js +59 -0
- package/dist/ui/stores/taskbar-store.js.map +1 -0
- package/dist/ui/theme/dark-ansi.json +48 -0
- package/dist/ui/theme/dark-daltonized.json +48 -0
- package/dist/ui/theme/dark.json +22 -1
- package/dist/ui/theme/detect-theme.d.ts +12 -8
- package/dist/ui/theme/detect-theme.d.ts.map +1 -1
- package/dist/ui/theme/detect-theme.js +58 -39
- package/dist/ui/theme/detect-theme.js.map +1 -1
- package/dist/ui/theme/light-ansi.json +48 -0
- package/dist/ui/theme/light-daltonized.json +48 -0
- package/dist/ui/theme/light.json +44 -23
- package/dist/ui/theme/theme.d.ts +27 -1
- package/dist/ui/theme/theme.d.ts.map +1 -1
- package/dist/ui/theme/theme.js +24 -1
- package/dist/ui/theme/theme.js.map +1 -1
- package/dist/ui/utils/highlight.d.ts +1 -1
- package/dist/ui/utils/highlight.d.ts.map +1 -1
- package/dist/ui/utils/highlight.js +24 -4
- package/dist/ui/utils/highlight.js.map +1 -1
- package/dist/ui/utils/hyperlink.d.ts +11 -0
- package/dist/ui/utils/hyperlink.d.ts.map +1 -0
- package/dist/ui/utils/hyperlink.js +27 -0
- package/dist/ui/utils/hyperlink.js.map +1 -0
- package/dist/ui/utils/markdown-cache.d.ts +5 -6
- package/dist/ui/utils/markdown-cache.d.ts.map +1 -1
- package/dist/ui/utils/markdown-cache.js +5 -5
- package/dist/ui/utils/markdown-cache.js.map +1 -1
- package/dist/ui/utils/signal.d.ts +21 -0
- package/dist/ui/utils/signal.d.ts.map +1 -0
- package/dist/ui/utils/signal.js +30 -0
- package/dist/ui/utils/signal.js.map +1 -0
- package/dist/ui/utils/supports-hyperlinks.d.ts +6 -0
- package/dist/ui/utils/supports-hyperlinks.d.ts.map +1 -0
- package/dist/ui/utils/supports-hyperlinks.js +52 -0
- package/dist/ui/utils/supports-hyperlinks.js.map +1 -0
- package/dist/ui/utils/token-to-ansi.d.ts +9 -0
- package/dist/ui/utils/token-to-ansi.d.ts.map +1 -0
- package/dist/ui/utils/token-to-ansi.js +305 -0
- package/dist/ui/utils/token-to-ansi.js.map +1 -0
- package/dist/utils/session-title.d.ts +19 -0
- package/dist/utils/session-title.d.ts.map +1 -0
- package/dist/utils/session-title.js +82 -0
- package/dist/utils/session-title.js.map +1 -0
- package/package.json +7 -5
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ Switch mid-conversation with `/model`. Not locked to anyone.
|
|
|
72
72
|
|
|
73
73
|
| Provider | Models | Auth |
|
|
74
74
|
|---|---|---|
|
|
75
|
-
| **Anthropic** | Claude Opus 4.
|
|
75
|
+
| **Anthropic** | Claude Opus 4.7, Sonnet 4.6, Haiku 4.5 | OAuth |
|
|
76
76
|
| **OpenAI** | GPT-4.1, o3, o4-mini | OAuth |
|
|
77
77
|
| **Z.AI (GLM)** | GLM-5.1, GLM-4.7 | API key |
|
|
78
78
|
| **Moonshot** | Kimi K2.5 | API key |
|
|
@@ -84,7 +84,7 @@ Switch mid-conversation with `/model`. Not locked to anyone.
|
|
|
84
84
|
Everything runs through slash commands inside the session. Not CLI flags.
|
|
85
85
|
|
|
86
86
|
```bash
|
|
87
|
-
/model claude-opus-4-
|
|
87
|
+
/model claude-opus-4-7 # Switch models on the fly
|
|
88
88
|
/model kimi-k2.5
|
|
89
89
|
/compact # Compress context when it gets long
|
|
90
90
|
|
package/dist/cli.js
CHANGED
|
@@ -13,16 +13,28 @@ process.on("unhandledRejection", (reason) => {
|
|
|
13
13
|
// Re-throw non-abort rejections so they still crash with a useful stack trace
|
|
14
14
|
throw reason;
|
|
15
15
|
});
|
|
16
|
-
// Drain performance entries to prevent
|
|
16
|
+
// Drain ALL performance entries to prevent unbounded memory growth.
|
|
17
|
+
// Node emits entries for marks, measures, resource timing (HTTP), DNS, net, etc.
|
|
18
|
+
// Without clearing, these accumulate across every LLM call and tool execution.
|
|
17
19
|
import { PerformanceObserver, performance } from "node:perf_hooks";
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
20
|
+
{
|
|
21
|
+
const allTypes = PerformanceObserver.supportedEntryTypes.filter((t) => t !== "gc" && t !== "function");
|
|
22
|
+
new PerformanceObserver((list) => {
|
|
23
|
+
for (const entry of list.getEntries()) {
|
|
24
|
+
switch (entry.entryType) {
|
|
25
|
+
case "measure":
|
|
26
|
+
performance.clearMeasures(entry.name);
|
|
27
|
+
break;
|
|
28
|
+
case "mark":
|
|
29
|
+
performance.clearMarks(entry.name);
|
|
30
|
+
break;
|
|
31
|
+
case "resource":
|
|
32
|
+
performance.clearResourceTimings();
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}).observe({ entryTypes: allTypes });
|
|
37
|
+
}
|
|
26
38
|
import { parseArgs } from "node:util";
|
|
27
39
|
import fs from "node:fs";
|
|
28
40
|
import readline from "node:readline/promises";
|
|
@@ -37,8 +49,9 @@ import { renderSessionSelector } from "./ui/sessions.js";
|
|
|
37
49
|
import { formatUserError } from "./utils/error-handler.js";
|
|
38
50
|
import { AuthStorage } from "./core/auth-storage.js";
|
|
39
51
|
import { SessionManager } from "./core/session-manager.js";
|
|
40
|
-
import { ensureAppDirs, getAppPaths } from "./config.js";
|
|
52
|
+
import { ensureAppDirs, getAppPaths, loadSavedSettings } from "./config.js";
|
|
41
53
|
import { initLogger, log, closeLogger } from "./core/logger.js";
|
|
54
|
+
import { setStreamDiagnostic } from "@prestyj/agent";
|
|
42
55
|
import { buildSystemPrompt } from "./system-prompt.js";
|
|
43
56
|
import { createTools } from "./tools/index.js";
|
|
44
57
|
import { shouldCompact, compact } from "./core/compaction/compactor.js";
|
|
@@ -115,6 +128,7 @@ function printHelp() {
|
|
|
115
128
|
const cmds = [
|
|
116
129
|
["login", "Log in to an AI provider (Anthropic, OpenAI)"],
|
|
117
130
|
["logout", "Log out and clear stored credentials"],
|
|
131
|
+
["doctor", "Diagnose and fix auth/config issues"],
|
|
118
132
|
["sessions", "Browse and resume previous sessions"],
|
|
119
133
|
["continue", "Resume the most recent session"],
|
|
120
134
|
["serve", "Start the HTTP/WebSocket API server"],
|
|
@@ -129,7 +143,7 @@ function printHelp() {
|
|
|
129
143
|
const opts = [
|
|
130
144
|
["-h, --help", "Show this help message"],
|
|
131
145
|
["-v, --version", "Show version number"],
|
|
132
|
-
["--provider <name>", "AI provider (anthropic, openai, glm, moonshot)"],
|
|
146
|
+
["--provider <name>", "AI provider (anthropic, xiaomi, openai, glm, moonshot)"],
|
|
133
147
|
["--model <name>", "Model to use (e.g. claude-sonnet-4-6, gpt-4.1)"],
|
|
134
148
|
["--max-turns <n>", "Maximum agent turns per prompt"],
|
|
135
149
|
["--system-prompt <text>", "Override the system prompt"],
|
|
@@ -230,6 +244,13 @@ function main() {
|
|
|
230
244
|
});
|
|
231
245
|
return;
|
|
232
246
|
}
|
|
247
|
+
if (subcommand === "doctor") {
|
|
248
|
+
runDoctor().catch((err) => {
|
|
249
|
+
process.stderr.write(formatUserError(err) + "\n");
|
|
250
|
+
process.exit(1);
|
|
251
|
+
});
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
233
254
|
if (subcommand === "continue") {
|
|
234
255
|
// Remove "continue" so parseArgs handles remaining flags
|
|
235
256
|
process.argv.splice(2, 1);
|
|
@@ -260,7 +281,7 @@ function main() {
|
|
|
260
281
|
if (values.json) {
|
|
261
282
|
const message = positionals[0] ?? "";
|
|
262
283
|
const jsonProvider = (values.provider ?? "anthropic");
|
|
263
|
-
const jsonModel = values.model ?? "claude-opus-4-
|
|
284
|
+
const jsonModel = values.model ?? "claude-opus-4-7";
|
|
264
285
|
const maxTurns = values["max-turns"] ? parseInt(values["max-turns"], 10) : undefined;
|
|
265
286
|
const systemPrompt = values["system-prompt"];
|
|
266
287
|
const cwd = process.cwd();
|
|
@@ -280,7 +301,7 @@ function main() {
|
|
|
280
301
|
// RPC mode — headless JSON-over-stdio for IDE integrations
|
|
281
302
|
if (values.rpc) {
|
|
282
303
|
const rpcProvider = (values.provider ?? "anthropic");
|
|
283
|
-
const rpcModel = values.model ?? "claude-opus-4-
|
|
304
|
+
const rpcModel = values.model ?? "claude-opus-4-7";
|
|
284
305
|
const systemPrompt = values["system-prompt"];
|
|
285
306
|
const cwd = process.cwd();
|
|
286
307
|
runRpcMode({
|
|
@@ -295,36 +316,24 @@ function main() {
|
|
|
295
316
|
return;
|
|
296
317
|
}
|
|
297
318
|
// Load saved settings for model/provider persistence
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
let savedTheme = "auto";
|
|
302
|
-
try {
|
|
303
|
-
const raw = JSON.parse(fs.readFileSync(getAppPaths().settingsFile, "utf-8"));
|
|
304
|
-
if (raw.defaultProvider)
|
|
305
|
-
savedProvider = raw.defaultProvider;
|
|
306
|
-
if (raw.defaultModel)
|
|
307
|
-
savedModel = raw.defaultModel;
|
|
308
|
-
if (raw.thinkingEnabled === true)
|
|
309
|
-
savedThinkingEnabled = true;
|
|
310
|
-
if (raw.theme === "dark" || raw.theme === "light" || raw.theme === "auto")
|
|
311
|
-
savedTheme = raw.theme;
|
|
312
|
-
}
|
|
313
|
-
catch {
|
|
314
|
-
// No settings file or invalid JSON — use defaults
|
|
315
|
-
}
|
|
316
|
-
const provider = savedProvider ?? "anthropic";
|
|
319
|
+
const saved = loadSavedSettings();
|
|
320
|
+
const savedTheme = saved.theme;
|
|
321
|
+
const provider = saved.provider ?? "anthropic";
|
|
317
322
|
function getHardcodedDefault(p) {
|
|
318
323
|
if (p === "openai")
|
|
319
|
-
return "gpt-5.
|
|
324
|
+
return "gpt-5.4";
|
|
320
325
|
if (p === "glm")
|
|
321
326
|
return "glm-5.1";
|
|
322
327
|
if (p === "moonshot")
|
|
323
328
|
return "kimi-k2.5";
|
|
324
|
-
|
|
329
|
+
if (p === "minimax")
|
|
330
|
+
return "MiniMax-M2.7";
|
|
331
|
+
if (p === "openrouter")
|
|
332
|
+
return "qwen/qwen3.6-plus";
|
|
333
|
+
return "claude-opus-4-7";
|
|
325
334
|
}
|
|
326
|
-
const model =
|
|
327
|
-
const thinkingLevel =
|
|
335
|
+
const model = saved.model ?? getHardcodedDefault(provider);
|
|
336
|
+
const thinkingLevel = saved.thinkingEnabled ? "medium" : undefined;
|
|
328
337
|
// Interactive mode (Ink TUI)
|
|
329
338
|
const cwd = process.cwd();
|
|
330
339
|
const continueRecent = subcommand === "continue";
|
|
@@ -355,11 +364,23 @@ async function runInkTUI(opts) {
|
|
|
355
364
|
model,
|
|
356
365
|
thinking: opts.thinkingLevel,
|
|
357
366
|
});
|
|
367
|
+
// Wire stream stall diagnostics into the debug log
|
|
368
|
+
setStreamDiagnostic((phase, data) => {
|
|
369
|
+
log("INFO", "stream", phase, data);
|
|
370
|
+
});
|
|
358
371
|
const authStorage = new AuthStorage(paths.authFile);
|
|
359
372
|
await authStorage.load();
|
|
360
373
|
const creds = await authStorage.resolveCredentials(provider);
|
|
361
374
|
// Detect all logged-in providers and preload their credentials
|
|
362
|
-
const allProviders = [
|
|
375
|
+
const allProviders = [
|
|
376
|
+
"anthropic",
|
|
377
|
+
"xiaomi",
|
|
378
|
+
"openai",
|
|
379
|
+
"glm",
|
|
380
|
+
"moonshot",
|
|
381
|
+
"minimax",
|
|
382
|
+
"openrouter",
|
|
383
|
+
];
|
|
363
384
|
const loggedInProviders = [];
|
|
364
385
|
const credentialsByProvider = {};
|
|
365
386
|
for (const p of allProviders) {
|
|
@@ -371,6 +392,7 @@ async function runInkTUI(opts) {
|
|
|
371
392
|
credentialsByProvider[p] = {
|
|
372
393
|
accessToken: resolved.accessToken,
|
|
373
394
|
accountId: resolved.accountId,
|
|
395
|
+
baseUrl: resolved.baseUrl,
|
|
374
396
|
};
|
|
375
397
|
}
|
|
376
398
|
catch {
|
|
@@ -548,8 +570,20 @@ async function runLogin() {
|
|
|
548
570
|
},
|
|
549
571
|
};
|
|
550
572
|
let creds;
|
|
551
|
-
if (provider === "glm" ||
|
|
552
|
-
|
|
573
|
+
if (provider === "glm" ||
|
|
574
|
+
provider === "moonshot" ||
|
|
575
|
+
provider === "xiaomi" ||
|
|
576
|
+
provider === "minimax" ||
|
|
577
|
+
provider === "openrouter") {
|
|
578
|
+
const keyLabel = provider === "glm"
|
|
579
|
+
? "Z.AI"
|
|
580
|
+
: provider === "xiaomi"
|
|
581
|
+
? "Xiaomi MiMo"
|
|
582
|
+
: provider === "minimax"
|
|
583
|
+
? "MiniMax"
|
|
584
|
+
: provider === "openrouter"
|
|
585
|
+
? "OpenRouter"
|
|
586
|
+
: "Moonshot";
|
|
553
587
|
const apiKey = await rl.question(chalk.hex("#60a5fa")(`Paste your ${keyLabel} API key: `));
|
|
554
588
|
if (!apiKey.trim()) {
|
|
555
589
|
console.log(chalk.hex("#ef4444")("No API key provided. Login cancelled."));
|
|
@@ -559,6 +593,7 @@ async function runLogin() {
|
|
|
559
593
|
accessToken: apiKey.trim(),
|
|
560
594
|
refreshToken: "",
|
|
561
595
|
expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000 * 100, // ~100 years
|
|
596
|
+
...(provider === "xiaomi" ? { baseUrl: "https://token-plan-sgp.xiaomimimo.com/v1" } : {}),
|
|
562
597
|
};
|
|
563
598
|
}
|
|
564
599
|
else {
|
|
@@ -574,6 +609,244 @@ async function runLogin() {
|
|
|
574
609
|
closeLogger();
|
|
575
610
|
}
|
|
576
611
|
}
|
|
612
|
+
// ── Doctor ─────────────────────────────────────────────────
|
|
613
|
+
async function runDoctor() {
|
|
614
|
+
process.stdout.write("\x1b[2J\x1b[3J\x1b[H");
|
|
615
|
+
const os = await import("node:os");
|
|
616
|
+
const fsP = await import("node:fs/promises");
|
|
617
|
+
const dim = chalk.hex("#6b7280");
|
|
618
|
+
const primary = chalk.hex("#60a5fa");
|
|
619
|
+
const accent = chalk.hex("#a78bfa");
|
|
620
|
+
const good = chalk.hex("#4ade80");
|
|
621
|
+
const warn = chalk.hex("#fbbf24");
|
|
622
|
+
const bad = chalk.hex("#ef4444");
|
|
623
|
+
// ── Banner ──────────────────────────────────────────────────
|
|
624
|
+
const LOGO = LOGO_LINES;
|
|
625
|
+
const GAP = " ";
|
|
626
|
+
console.log();
|
|
627
|
+
console.log(` ${gradientLine(LOGO[0])}${GAP}` +
|
|
628
|
+
primary.bold("EZ Coder") +
|
|
629
|
+
dim(` v${CLI_VERSION}`));
|
|
630
|
+
console.log(` ${gradientLine(LOGO[1])}${GAP}` + accent("Doctor"));
|
|
631
|
+
console.log(` ${gradientLine(LOGO[2])}${GAP}` + dim("Diagnose & Fix"));
|
|
632
|
+
console.log();
|
|
633
|
+
const home = os.homedir();
|
|
634
|
+
const ggDir = path.join(home, ".ezcoder");
|
|
635
|
+
const authFile = path.join(ggDir, "auth.json");
|
|
636
|
+
const lockFile = authFile + ".lock";
|
|
637
|
+
const myUid = process.getuid();
|
|
638
|
+
let fixed = 0;
|
|
639
|
+
// ── Environment ─────────────────────────────────────────────
|
|
640
|
+
console.log(accent(" Environment\n"));
|
|
641
|
+
console.log(dim(` Home: ${home}`));
|
|
642
|
+
console.log(dim(` $HOME: ${process.env.HOME ?? "(not set)"}`));
|
|
643
|
+
console.log(dim(` Node.js: ${process.version}`));
|
|
644
|
+
console.log(dim(` Platform: ${process.platform} ${process.arch}`));
|
|
645
|
+
console.log(dim(` UID: ${myUid} EUID: ${process.geteuid()}`));
|
|
646
|
+
if (process.env.HOME && process.env.HOME !== home) {
|
|
647
|
+
console.log(warn("\n ⚠ $HOME differs from os.homedir() — this can cause auth mismatches"));
|
|
648
|
+
}
|
|
649
|
+
if (myUid !== process.geteuid()) {
|
|
650
|
+
console.log(warn(" ⚠ uid ≠ euid — running with elevated privileges (sudo?)"));
|
|
651
|
+
console.log(dim(" Running ezcoder with sudo can cause ownership issues."));
|
|
652
|
+
console.log(dim(" Use without sudo, or fix after: sudo chown -R $(whoami) ~/.gg"));
|
|
653
|
+
}
|
|
654
|
+
console.log();
|
|
655
|
+
// ── Config Directory ────────────────────────────────────────
|
|
656
|
+
console.log(accent(" Config Directory\n"));
|
|
657
|
+
try {
|
|
658
|
+
const stat = await fsP.stat(ggDir);
|
|
659
|
+
const mode = stat.mode & 0o777;
|
|
660
|
+
console.log(dim(` Path: ${ggDir}`));
|
|
661
|
+
console.log(dim(` Mode: 0o${mode.toString(8)} UID: ${stat.uid}`));
|
|
662
|
+
// Fix ownership
|
|
663
|
+
if (stat.uid !== myUid) {
|
|
664
|
+
console.log(warn(` ⚠ Owned by uid ${stat.uid}, expected ${myUid}`));
|
|
665
|
+
try {
|
|
666
|
+
await fsP.chown(ggDir, myUid, process.getgid());
|
|
667
|
+
console.log(good(" ✓ Fixed directory ownership"));
|
|
668
|
+
fixed++;
|
|
669
|
+
}
|
|
670
|
+
catch {
|
|
671
|
+
console.log(bad(` ✗ Cannot fix — try: sudo chown -R $(whoami) ${ggDir}`));
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
// Fix permissions (should be 0o700)
|
|
675
|
+
if (mode !== 0o700) {
|
|
676
|
+
try {
|
|
677
|
+
await fsP.chmod(ggDir, 0o700);
|
|
678
|
+
console.log(good(" ✓ Fixed directory permissions → 0o700"));
|
|
679
|
+
fixed++;
|
|
680
|
+
}
|
|
681
|
+
catch {
|
|
682
|
+
console.log(bad(` ✗ Cannot fix — try: chmod 700 ${ggDir}`));
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
catch {
|
|
687
|
+
console.log(warn(` ${ggDir} missing — creating...`));
|
|
688
|
+
try {
|
|
689
|
+
await fsP.mkdir(ggDir, { recursive: true, mode: 0o700 });
|
|
690
|
+
console.log(good(` ✓ Created ${ggDir}`));
|
|
691
|
+
fixed++;
|
|
692
|
+
}
|
|
693
|
+
catch (mkErr) {
|
|
694
|
+
console.log(bad(` ✗ Cannot create: ${mkErr instanceof Error ? mkErr.message : String(mkErr)}`));
|
|
695
|
+
console.log();
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
console.log();
|
|
700
|
+
// ── Lock File ───────────────────────────────────────────────
|
|
701
|
+
try {
|
|
702
|
+
const lockStat = await fsP.stat(lockFile);
|
|
703
|
+
const ageMs = Date.now() - lockStat.mtimeMs;
|
|
704
|
+
console.log(accent(" Lock File\n"));
|
|
705
|
+
console.log(warn(` ⚠ Stale lock found (age: ${Math.round(ageMs / 1000)}s)`));
|
|
706
|
+
await fsP.unlink(lockFile);
|
|
707
|
+
console.log(good(" ✓ Removed"));
|
|
708
|
+
fixed++;
|
|
709
|
+
console.log();
|
|
710
|
+
}
|
|
711
|
+
catch {
|
|
712
|
+
// No lock file — good, skip section entirely
|
|
713
|
+
}
|
|
714
|
+
// ── Auth File ───────────────────────────────────────────────
|
|
715
|
+
console.log(accent(" Auth File\n"));
|
|
716
|
+
let authData = null;
|
|
717
|
+
let authNeedsRewrite = false;
|
|
718
|
+
try {
|
|
719
|
+
const stat = await fsP.stat(authFile);
|
|
720
|
+
const mode = stat.mode & 0o777;
|
|
721
|
+
console.log(dim(` Path: ${authFile}`));
|
|
722
|
+
console.log(dim(` Size: ${stat.size} bytes Mode: 0o${mode.toString(8)} UID: ${stat.uid}`));
|
|
723
|
+
// Fix ownership
|
|
724
|
+
if (stat.uid !== myUid) {
|
|
725
|
+
console.log(warn(` ⚠ Owned by uid ${stat.uid}, expected ${myUid}`));
|
|
726
|
+
try {
|
|
727
|
+
await fsP.chown(authFile, myUid, process.getgid());
|
|
728
|
+
console.log(good(" ✓ Fixed file ownership"));
|
|
729
|
+
fixed++;
|
|
730
|
+
}
|
|
731
|
+
catch {
|
|
732
|
+
console.log(bad(` ✗ Cannot fix — try: sudo chown $(whoami) ${authFile}`));
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
// Fix permissions (should be 0o600)
|
|
736
|
+
if (mode !== 0o600) {
|
|
737
|
+
try {
|
|
738
|
+
await fsP.chmod(authFile, 0o600);
|
|
739
|
+
console.log(good(" ✓ Fixed file permissions → 0o600"));
|
|
740
|
+
fixed++;
|
|
741
|
+
}
|
|
742
|
+
catch {
|
|
743
|
+
console.log(bad(` ✗ Cannot fix — try: chmod 600 ${authFile}`));
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
// Try to read and parse
|
|
747
|
+
try {
|
|
748
|
+
const content = await fsP.readFile(authFile, "utf-8");
|
|
749
|
+
try {
|
|
750
|
+
authData = JSON.parse(content);
|
|
751
|
+
}
|
|
752
|
+
catch {
|
|
753
|
+
console.log(bad(" ✗ Invalid JSON — backing up and resetting"));
|
|
754
|
+
const backupName = `auth.json.corrupt.${Date.now()}`;
|
|
755
|
+
await fsP.copyFile(authFile, path.join(ggDir, backupName));
|
|
756
|
+
await fsP.writeFile(authFile, "{}", { encoding: "utf-8", mode: 0o600 });
|
|
757
|
+
console.log(good(` ✓ Corrupt file backed up as ${backupName}`));
|
|
758
|
+
console.log(dim(' Run "ezcoder login" to re-authenticate'));
|
|
759
|
+
authData = {};
|
|
760
|
+
fixed++;
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
catch (readErr) {
|
|
764
|
+
const code = readErr.code;
|
|
765
|
+
if (code === "EACCES") {
|
|
766
|
+
console.log(bad(" ✗ Permission denied reading auth.json"));
|
|
767
|
+
console.log(dim(` Try: sudo chown $(whoami) ${authFile} && chmod 600 ${authFile}`));
|
|
768
|
+
}
|
|
769
|
+
else {
|
|
770
|
+
console.log(bad(` ✗ Read error: ${readErr instanceof Error ? readErr.message : String(readErr)}`));
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
catch {
|
|
775
|
+
console.log(dim(` Path: ${authFile}`));
|
|
776
|
+
console.log(warn(' Not found — run "ezcoder login" to authenticate'));
|
|
777
|
+
}
|
|
778
|
+
console.log();
|
|
779
|
+
// ── Credentials ─────────────────────────────────────────────
|
|
780
|
+
if (authData && Object.keys(authData).length > 0) {
|
|
781
|
+
console.log(accent(" Credentials\n"));
|
|
782
|
+
for (const p of Object.keys(authData)) {
|
|
783
|
+
const cred = authData[p];
|
|
784
|
+
if (!cred || typeof cred !== "object") {
|
|
785
|
+
console.log(bad(` ✗ ${p}: invalid entry — removing`));
|
|
786
|
+
delete authData[p];
|
|
787
|
+
authNeedsRewrite = true;
|
|
788
|
+
fixed++;
|
|
789
|
+
continue;
|
|
790
|
+
}
|
|
791
|
+
if (!cred.accessToken || typeof cred.accessToken !== "string") {
|
|
792
|
+
console.log(bad(` ✗ ${p}: missing accessToken — removing`));
|
|
793
|
+
delete authData[p];
|
|
794
|
+
authNeedsRewrite = true;
|
|
795
|
+
fixed++;
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
const token = String(cred.accessToken);
|
|
799
|
+
const masked = token.slice(0, 8) + "..." + token.slice(-4);
|
|
800
|
+
const expires = typeof cred.expiresAt === "number" ? new Date(cred.expiresAt).toISOString() : "unknown";
|
|
801
|
+
const expired = typeof cred.expiresAt === "number" && Date.now() > cred.expiresAt;
|
|
802
|
+
if (expired) {
|
|
803
|
+
console.log(warn(` ⚠ ${p}: ${masked} expired ${expires}`));
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
console.log(good(` ✓ ${p}: ${masked} expires ${expires}`));
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (authNeedsRewrite) {
|
|
810
|
+
try {
|
|
811
|
+
await fsP.writeFile(authFile, JSON.stringify(authData, null, 2), {
|
|
812
|
+
encoding: "utf-8",
|
|
813
|
+
mode: 0o600,
|
|
814
|
+
});
|
|
815
|
+
console.log(good(" ✓ Cleaned up auth.json"));
|
|
816
|
+
}
|
|
817
|
+
catch {
|
|
818
|
+
console.log(bad(" ✗ Failed to write cleaned auth.json"));
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
console.log();
|
|
822
|
+
}
|
|
823
|
+
// ── Temp Files ──────────────────────────────────────────────
|
|
824
|
+
try {
|
|
825
|
+
const entries = await fsP.readdir(ggDir);
|
|
826
|
+
const tmpFiles = entries.filter((e) => e.startsWith("auth.json.") && e.endsWith(".tmp"));
|
|
827
|
+
if (tmpFiles.length > 0) {
|
|
828
|
+
console.log(accent(" Temp Files\n"));
|
|
829
|
+
console.log(warn(` ⚠ ${tmpFiles.length} orphaned temp file(s) from interrupted writes`));
|
|
830
|
+
for (const tmp of tmpFiles) {
|
|
831
|
+
await fsP.unlink(path.join(ggDir, tmp)).catch(() => { });
|
|
832
|
+
}
|
|
833
|
+
console.log(good(` ✓ Removed ${tmpFiles.length} file(s)`));
|
|
834
|
+
fixed++;
|
|
835
|
+
console.log();
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
catch {
|
|
839
|
+
// Can't read directory — already flagged above
|
|
840
|
+
}
|
|
841
|
+
// ── Summary ─────────────────────────────────────────────────
|
|
842
|
+
if (fixed > 0) {
|
|
843
|
+
console.log(good(` ✓ Fixed ${fixed} issue${fixed > 1 ? "s" : ""}.`));
|
|
844
|
+
}
|
|
845
|
+
else {
|
|
846
|
+
console.log(good(" ✓ Everything looks good."));
|
|
847
|
+
}
|
|
848
|
+
console.log();
|
|
849
|
+
}
|
|
577
850
|
// ── Logout ─────────────────────────────────────────────────
|
|
578
851
|
async function runLogout() {
|
|
579
852
|
const paths = await ensureAppDirs();
|
|
@@ -599,37 +872,21 @@ async function runSessions() {
|
|
|
599
872
|
closeLogger();
|
|
600
873
|
process.exit(0);
|
|
601
874
|
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
let savedModel;
|
|
605
|
-
let savedThinkingEnabled = false;
|
|
606
|
-
let savedTheme = "auto";
|
|
607
|
-
try {
|
|
608
|
-
const raw = JSON.parse(fs.readFileSync(paths.settingsFile, "utf-8"));
|
|
609
|
-
if (raw.defaultProvider)
|
|
610
|
-
savedProvider = raw.defaultProvider;
|
|
611
|
-
if (raw.defaultModel)
|
|
612
|
-
savedModel = raw.defaultModel;
|
|
613
|
-
if (raw.thinkingEnabled === true)
|
|
614
|
-
savedThinkingEnabled = true;
|
|
615
|
-
if (raw.theme === "dark" || raw.theme === "light" || raw.theme === "auto")
|
|
616
|
-
savedTheme = raw.theme;
|
|
617
|
-
}
|
|
618
|
-
catch {
|
|
619
|
-
// No settings file — use defaults
|
|
620
|
-
}
|
|
621
|
-
const provider = savedProvider ?? "anthropic";
|
|
875
|
+
const saved2 = loadSavedSettings(paths.settingsFile);
|
|
876
|
+
const provider = saved2.provider ?? "anthropic";
|
|
622
877
|
function getDefault(p) {
|
|
623
878
|
if (p === "openai")
|
|
624
|
-
return "gpt-5.
|
|
879
|
+
return "gpt-5.4";
|
|
625
880
|
if (p === "glm")
|
|
626
881
|
return "glm-5.1";
|
|
627
882
|
if (p === "moonshot")
|
|
628
883
|
return "kimi-k2.5";
|
|
629
|
-
|
|
884
|
+
if (p === "minimax")
|
|
885
|
+
return "MiniMax-M2.7";
|
|
886
|
+
return "claude-opus-4-7";
|
|
630
887
|
}
|
|
631
|
-
const model =
|
|
632
|
-
const thinkingLevel =
|
|
888
|
+
const model = saved2.model ?? getDefault(provider);
|
|
889
|
+
const thinkingLevel = saved2.thinkingEnabled ? "medium" : undefined;
|
|
633
890
|
closeLogger();
|
|
634
891
|
await runInkTUI({
|
|
635
892
|
provider,
|
|
@@ -637,7 +894,7 @@ async function runSessions() {
|
|
|
637
894
|
cwd,
|
|
638
895
|
thinkingLevel,
|
|
639
896
|
resumeSessionPath: selectedPath,
|
|
640
|
-
theme:
|
|
897
|
+
theme: saved2.theme,
|
|
641
898
|
});
|
|
642
899
|
}
|
|
643
900
|
async function loadTelegramConfig() {
|
|
@@ -806,34 +1063,21 @@ async function runServe() {
|
|
|
806
1063
|
chalk.hex("#6b7280")(" ezcoder serve --bot-token TOKEN --user-id ID"));
|
|
807
1064
|
process.exit(1);
|
|
808
1065
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
let savedModel;
|
|
812
|
-
let savedThinkingEnabled = false;
|
|
813
|
-
try {
|
|
814
|
-
const raw = JSON.parse(fs.readFileSync(getAppPaths().settingsFile, "utf-8"));
|
|
815
|
-
if (raw.defaultProvider)
|
|
816
|
-
savedProvider = raw.defaultProvider;
|
|
817
|
-
if (raw.defaultModel)
|
|
818
|
-
savedModel = raw.defaultModel;
|
|
819
|
-
if (raw.thinkingEnabled === true)
|
|
820
|
-
savedThinkingEnabled = true;
|
|
821
|
-
}
|
|
822
|
-
catch {
|
|
823
|
-
// No settings file
|
|
824
|
-
}
|
|
825
|
-
const provider = serveValues.provider ?? savedProvider ?? "anthropic";
|
|
1066
|
+
const saved3 = loadSavedSettings();
|
|
1067
|
+
const provider = serveValues.provider ?? saved3.provider ?? "anthropic";
|
|
826
1068
|
function getDefault(p) {
|
|
827
1069
|
if (p === "openai")
|
|
828
|
-
return "gpt-5.
|
|
1070
|
+
return "gpt-5.4";
|
|
829
1071
|
if (p === "glm")
|
|
830
1072
|
return "glm-5.1";
|
|
831
1073
|
if (p === "moonshot")
|
|
832
1074
|
return "kimi-k2.5";
|
|
833
|
-
|
|
1075
|
+
if (p === "minimax")
|
|
1076
|
+
return "MiniMax-M2.7";
|
|
1077
|
+
return "claude-opus-4-7";
|
|
834
1078
|
}
|
|
835
|
-
const model = serveValues.model ??
|
|
836
|
-
const thinkingLevel =
|
|
1079
|
+
const model = serveValues.model ?? saved3.model ?? getDefault(provider);
|
|
1080
|
+
const thinkingLevel = saved3.thinkingEnabled ? "medium" : undefined;
|
|
837
1081
|
const paths = await ensureAppDirs();
|
|
838
1082
|
initLogger(paths.logFile, {
|
|
839
1083
|
version: CLI_VERSION,
|
|
@@ -854,10 +1098,16 @@ async function runServe() {
|
|
|
854
1098
|
function displayName(provider) {
|
|
855
1099
|
if (provider === "anthropic")
|
|
856
1100
|
return "Anthropic";
|
|
1101
|
+
if (provider === "xiaomi")
|
|
1102
|
+
return "Xiaomi (MiMo)";
|
|
857
1103
|
if (provider === "glm")
|
|
858
1104
|
return "Z.AI (GLM)";
|
|
859
1105
|
if (provider === "moonshot")
|
|
860
1106
|
return "Moonshot";
|
|
1107
|
+
if (provider === "minimax")
|
|
1108
|
+
return "MiniMax";
|
|
1109
|
+
if (provider === "openrouter")
|
|
1110
|
+
return "OpenRouter";
|
|
861
1111
|
return "OpenAI";
|
|
862
1112
|
}
|
|
863
1113
|
function extractText(content) {
|