@llmtune/cli 0.1.7 → 0.1.9
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/agent/loop.js +23 -6
- package/dist/commands/balance.d.ts +2 -0
- package/dist/commands/balance.js +65 -0
- package/dist/commands/chat.js +15 -11
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +212 -0
- package/dist/commands/login.js +3 -1
- package/dist/commands/models.js +9 -15
- package/dist/commands/update.d.ts +6 -0
- package/dist/commands/update.js +115 -0
- package/dist/context/cache.js +110 -0
- package/dist/context/workspace.js +2 -1
- package/dist/index.js +57 -5
- package/dist/repl/repl.d.ts +1 -0
- package/dist/repl/repl.js +51 -34
- package/dist/tools/permissions.d.ts +2 -0
- package/dist/tools/permissions.js +12 -3
- package/dist/tools/sanitize.d.ts +10 -0
- package/dist/tools/sanitize.js +87 -0
- package/dist/tools/tools/edit.js +8 -2
- package/dist/tools/tools/grep.js +2 -2
- package/dist/tools/tools/read.js +7 -4
- package/dist/tools/tools/web-fetch.js +74 -99
- package/dist/tools/tools/write.js +13 -7
- package/dist/ui/banner.d.ts +2 -0
- package/dist/ui/banner.js +29 -0
- package/dist/utils/streaming.d.ts +6 -3
- package/dist/utils/streaming.js +14 -19
- package/package.json +9 -3
package/dist/agent/loop.js
CHANGED
|
@@ -9,6 +9,8 @@ const auto_compact_1 = require("../compact/auto-compact");
|
|
|
9
9
|
const service_1 = require("../memory/service");
|
|
10
10
|
const tokens_1 = require("../utils/tokens");
|
|
11
11
|
const chalk_1 = __importDefault(require("chalk"));
|
|
12
|
+
const markdown_1 = require("../utils/markdown");
|
|
13
|
+
const streaming_1 = require("../utils/streaming");
|
|
12
14
|
async function runAgentLoop(client, conversation, registry, userInput, config, onTextChunk) {
|
|
13
15
|
const model = config.model ?? "z-ai/GLM-5.1";
|
|
14
16
|
const maxTurns = config.maxTurns ?? 20;
|
|
@@ -48,6 +50,9 @@ async function runAgentLoop(client, conversation, registry, userInput, config, o
|
|
|
48
50
|
let finalText = "";
|
|
49
51
|
for (let turn = 0; turn < maxTurns; turn++) {
|
|
50
52
|
const allMessages = buildApiMessages(conversation, contextPrompt);
|
|
53
|
+
// Clear the "thinking" indicator on subsequent turns
|
|
54
|
+
if (turn > 0)
|
|
55
|
+
process.stdout.write("\r \r");
|
|
51
56
|
const turnResult = useStream
|
|
52
57
|
? await runStreamingTurn(client, model, allMessages, openaiTools, onTextChunk)
|
|
53
58
|
: await runBufferedTurn(client, model, allMessages, openaiTools);
|
|
@@ -141,6 +146,10 @@ function buildApiMessages(conversation, contextPrompt) {
|
|
|
141
146
|
}),
|
|
142
147
|
];
|
|
143
148
|
}
|
|
149
|
+
/**
|
|
150
|
+
* Streaming turn — prints tokens live to the terminal as they arrive.
|
|
151
|
+
* After stream completes, final rendered markdown is shown.
|
|
152
|
+
*/
|
|
144
153
|
async function runStreamingTurn(client, model, messages, openaiTools, onTextChunk) {
|
|
145
154
|
const stream = await client.chat.completions.create({
|
|
146
155
|
model,
|
|
@@ -155,16 +164,18 @@ async function runStreamingTurn(client, model, messages, openaiTools, onTextChun
|
|
|
155
164
|
let currentToolCall = null;
|
|
156
165
|
let tokensIn = 0;
|
|
157
166
|
let tokensOut = 0;
|
|
167
|
+
// Create streaming display for live output
|
|
168
|
+
const display = (0, streaming_1.createStreamingDisplay)();
|
|
158
169
|
for await (const chunk of stream) {
|
|
159
170
|
const delta = chunk.choices[0]?.delta;
|
|
160
171
|
if (!delta)
|
|
161
172
|
continue;
|
|
162
173
|
if (delta.content) {
|
|
163
174
|
assistantContent += delta.content;
|
|
175
|
+
// Show live output to the user
|
|
176
|
+
display.onToken(delta.content);
|
|
164
177
|
if (onTextChunk)
|
|
165
178
|
onTextChunk(delta.content);
|
|
166
|
-
else
|
|
167
|
-
process.stdout.write(delta.content);
|
|
168
179
|
}
|
|
169
180
|
if (delta.tool_calls) {
|
|
170
181
|
for (const tc of delta.tool_calls) {
|
|
@@ -193,11 +204,18 @@ async function runStreamingTurn(client, model, messages, openaiTools, onTextChun
|
|
|
193
204
|
tokensOut += chunk.usage.completion_tokens ?? 0;
|
|
194
205
|
}
|
|
195
206
|
}
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
// Flush any remaining buffered tokens
|
|
208
|
+
display.flush();
|
|
209
|
+
// If no external handler and we have content, render it as markdown
|
|
210
|
+
if (!onTextChunk && assistantContent) {
|
|
211
|
+
// Content was already streamed raw via display, add a newline for spacing
|
|
212
|
+
console.log("");
|
|
213
|
+
}
|
|
198
214
|
return { assistantContent, toolCalls, tokensIn, tokensOut };
|
|
199
215
|
}
|
|
200
216
|
async function runBufferedTurn(client, model, messages, openaiTools) {
|
|
217
|
+
// Clear the "thinking" indicator
|
|
218
|
+
process.stdout.write("\r \r");
|
|
201
219
|
const response = await client.chat.completions.create({
|
|
202
220
|
model,
|
|
203
221
|
messages,
|
|
@@ -218,8 +236,7 @@ async function runBufferedTurn(client, model, messages, openaiTools) {
|
|
|
218
236
|
},
|
|
219
237
|
}));
|
|
220
238
|
if (assistantContent) {
|
|
221
|
-
|
|
222
|
-
console.log();
|
|
239
|
+
console.log((0, markdown_1.renderMarkdown)(assistantContent));
|
|
223
240
|
}
|
|
224
241
|
return {
|
|
225
242
|
assistantContent,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.balanceCommand = balanceCommand;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const config_1 = require("../auth/config");
|
|
9
|
+
async function balanceCommand() {
|
|
10
|
+
const apiKey = (0, config_1.getApiKey)();
|
|
11
|
+
if (!apiKey) {
|
|
12
|
+
console.log(chalk_1.default.red('Not authenticated. Run "llmtune login" first.'));
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
const config = (0, config_1.loadConfig)();
|
|
16
|
+
const apiBase = config.apiBase || "https://api.llmtune.io/api/agent/v1";
|
|
17
|
+
// Derive the base URL (strip /api/agent/v1 → root)
|
|
18
|
+
const baseUrl = apiBase.replace(/\/api\/agent\/v1\/?$/, "");
|
|
19
|
+
const url = `${baseUrl}/api/usage/balance-bar`;
|
|
20
|
+
try {
|
|
21
|
+
const res = await fetch(url, {
|
|
22
|
+
headers: {
|
|
23
|
+
Authorization: `Bearer ${apiKey}`,
|
|
24
|
+
"Content-Type": "application/json",
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
if (!res.ok) {
|
|
28
|
+
const text = await res.text();
|
|
29
|
+
console.log(chalk_1.default.red(`Failed to fetch balance (${res.status}): ${text}`));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const data = (await res.json());
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log(chalk_1.default.bold(" Account Balance"));
|
|
35
|
+
console.log("");
|
|
36
|
+
// Balance display
|
|
37
|
+
const balance = data.balanceUsd;
|
|
38
|
+
const balanceStr = balance < 0
|
|
39
|
+
? chalk_1.default.red(`-$${Math.abs(balance).toFixed(2)}`)
|
|
40
|
+
: balance < 1
|
|
41
|
+
? chalk_1.default.yellow(`$${balance.toFixed(4)}`)
|
|
42
|
+
: chalk_1.default.green(`$${balance.toFixed(2)}`);
|
|
43
|
+
console.log(` Balance: ${balanceStr}`);
|
|
44
|
+
// Token usage (30d)
|
|
45
|
+
if (data.totalTokens !== undefined) {
|
|
46
|
+
const tokens = data.totalTokens;
|
|
47
|
+
const formatted = tokens >= 1_000_000
|
|
48
|
+
? `${(tokens / 1_000_000).toFixed(1)}M`
|
|
49
|
+
: tokens >= 1_000
|
|
50
|
+
? `${(tokens / 1_000).toFixed(1)}K`
|
|
51
|
+
: `${tokens}`;
|
|
52
|
+
console.log(` Usage (30d): ${chalk_1.default.dim(formatted + " tokens")}`);
|
|
53
|
+
}
|
|
54
|
+
console.log("");
|
|
55
|
+
// Low balance warning
|
|
56
|
+
if (balance > 0 && balance < 0.50) {
|
|
57
|
+
console.log(chalk_1.default.yellow(" ⚠ Low balance — consider topping up at llmtune.io"));
|
|
58
|
+
console.log("");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
console.log(chalk_1.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=balance.js.map
|
package/dist/commands/chat.js
CHANGED
|
@@ -5,23 +5,27 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.chatCommand = chatCommand;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
8
|
+
const config_1 = require("../auth/config");
|
|
9
|
+
const client_1 = require("../auth/client");
|
|
10
|
+
const repl_1 = require("../repl/repl");
|
|
11
|
+
const banner_1 = require("../ui/banner");
|
|
11
12
|
async function chatCommand(options) {
|
|
12
|
-
const config = (0,
|
|
13
|
+
const config = (0, config_1.loadConfig)();
|
|
13
14
|
if (!config.apiKey) {
|
|
14
15
|
console.log(chalk_1.default.red('No API key configured. Run "llmtune login" first.'));
|
|
15
16
|
process.exit(1);
|
|
16
17
|
}
|
|
17
|
-
const client = (0,
|
|
18
|
+
const client = (0, client_1.createClient)();
|
|
18
19
|
const model = options.model || config.defaultModel || "z-ai/GLM-5.1";
|
|
19
20
|
const stream = options.stream !== false;
|
|
20
|
-
|
|
21
|
-
console.log(
|
|
22
|
-
|
|
23
|
-
console.log(chalk_1.default.dim(`
|
|
24
|
-
console.log(chalk_1.default.dim(`
|
|
25
|
-
|
|
21
|
+
// Render the LLMTune ASCII art banner
|
|
22
|
+
console.log((0, banner_1.renderBanner)());
|
|
23
|
+
// Connection info
|
|
24
|
+
console.log(chalk_1.default.dim(` Connected to ${(0, config_1.getApiBase)()}`));
|
|
25
|
+
console.log(chalk_1.default.dim(` Model: ${model}`));
|
|
26
|
+
console.log(chalk_1.default.dim(` Stream: ${stream}`));
|
|
27
|
+
console.log(chalk_1.default.dim(` Type /help for commands, /exit to quit`));
|
|
28
|
+
console.log("");
|
|
29
|
+
await (0, repl_1.startRepl)({ client, model, stream });
|
|
26
30
|
}
|
|
27
31
|
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.doctorCommand = doctorCommand;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const os = __importStar(require("os"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const child_process_1 = require("child_process");
|
|
45
|
+
const config_1 = require("../auth/config");
|
|
46
|
+
const version_1 = require("../version");
|
|
47
|
+
async function doctorCommand() {
|
|
48
|
+
const checks = [];
|
|
49
|
+
console.log(chalk_1.default.bold("\n🩺 LLMTune CLI Doctor\n"));
|
|
50
|
+
// 1. Node.js version
|
|
51
|
+
const nodeVersion = process.version;
|
|
52
|
+
const major = parseInt(nodeVersion.slice(1).split(".")[0], 10);
|
|
53
|
+
if (major >= 18) {
|
|
54
|
+
checks.push({ name: "Node.js", status: "pass", message: `v${nodeVersion.slice(1)}` });
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
checks.push({
|
|
58
|
+
name: "Node.js",
|
|
59
|
+
status: "fail",
|
|
60
|
+
message: `v${nodeVersion.slice(1)} — requires >= 18.0.0`,
|
|
61
|
+
fix: "Upgrade Node.js: https://nodejs.org/",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
// 2. CLI version
|
|
65
|
+
checks.push({ name: "CLI version", status: "pass", message: `v${version_1.CLI_VERSION}` });
|
|
66
|
+
// 3. Config file
|
|
67
|
+
const configPath = (0, config_1.getConfigPath)();
|
|
68
|
+
if (fs.existsSync(configPath)) {
|
|
69
|
+
checks.push({ name: "Config file", status: "pass", message: configPath });
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
checks.push({
|
|
73
|
+
name: "Config file",
|
|
74
|
+
status: "warn",
|
|
75
|
+
message: "Not found",
|
|
76
|
+
fix: "Run: llmtune login",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
// 4. API key
|
|
80
|
+
if ((0, config_1.isLoggedIn)()) {
|
|
81
|
+
const config = (0, config_1.loadConfig)();
|
|
82
|
+
const key = config.apiKey;
|
|
83
|
+
const masked = key && key.length > 12
|
|
84
|
+
? `${key.slice(0, 6)}...${key.slice(-4)}`
|
|
85
|
+
: key ? "(set)" : "(not set)";
|
|
86
|
+
checks.push({ name: "API key", status: "pass", message: masked });
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
checks.push({
|
|
90
|
+
name: "API key",
|
|
91
|
+
status: "fail",
|
|
92
|
+
message: "Not configured",
|
|
93
|
+
fix: "Run: llmtune login",
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// 5. API connectivity — use native fetch instead of curl (Windows compatible)
|
|
97
|
+
if ((0, config_1.isLoggedIn)()) {
|
|
98
|
+
try {
|
|
99
|
+
const config = (0, config_1.loadConfig)();
|
|
100
|
+
const apiBase = config.apiBase || "https://api.llmtune.io/api/agent/v1";
|
|
101
|
+
const apiKey = config.apiKey;
|
|
102
|
+
const controller = new AbortController();
|
|
103
|
+
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
104
|
+
const response = await fetch(`${apiBase}/models`, {
|
|
105
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
106
|
+
signal: controller.signal,
|
|
107
|
+
});
|
|
108
|
+
clearTimeout(timeout);
|
|
109
|
+
if (response.ok) {
|
|
110
|
+
checks.push({ name: "API connection", status: "pass", message: `${apiBase} (HTTP ${response.status})` });
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
checks.push({ name: "API connection", status: "warn", message: `${apiBase} (HTTP ${response.status})` });
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
checks.push({
|
|
118
|
+
name: "API connection",
|
|
119
|
+
status: "fail",
|
|
120
|
+
message: "Cannot reach API",
|
|
121
|
+
fix: "Check your network connection and API base URL",
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// 6. Config dir
|
|
126
|
+
const configDir = path.join(os.homedir(), ".llmtune");
|
|
127
|
+
if (fs.existsSync(configDir)) {
|
|
128
|
+
checks.push({ name: "Config dir", status: "pass", message: configDir });
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
checks.push({
|
|
132
|
+
name: "Config dir",
|
|
133
|
+
status: "warn",
|
|
134
|
+
message: "Not created yet",
|
|
135
|
+
fix: "Will be created on first login",
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// 7. Git
|
|
139
|
+
try {
|
|
140
|
+
const gitVersion = (0, child_process_1.execSync)("git --version", { encoding: "utf-8", timeout: 5000 }).trim();
|
|
141
|
+
checks.push({ name: "Git", status: "pass", message: gitVersion });
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
checks.push({
|
|
145
|
+
name: "Git",
|
|
146
|
+
status: "warn",
|
|
147
|
+
message: "Not found",
|
|
148
|
+
fix: "Install Git for full workspace context features",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
// 8. Docker (optional)
|
|
152
|
+
try {
|
|
153
|
+
const dockerVersion = (0, child_process_1.execSync)("docker --version", { encoding: "utf-8", timeout: 5000 }).trim();
|
|
154
|
+
checks.push({ name: "Docker", status: "pass", message: `${dockerVersion} (sandbox available)` });
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
checks.push({ name: "Docker", status: "warn", message: "Not found (sandbox mode unavailable)" });
|
|
158
|
+
}
|
|
159
|
+
// 9. Memory dir
|
|
160
|
+
const memoryDir = path.join(os.homedir(), ".llmtune", "memory");
|
|
161
|
+
if (fs.existsSync(memoryDir)) {
|
|
162
|
+
const files = fs.readdirSync(memoryDir).filter(f => f.endsWith(".md"));
|
|
163
|
+
checks.push({ name: "Memory", status: "pass", message: `${files.length} memory files` });
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
checks.push({ name: "Memory", status: "warn", message: "Not initialized" });
|
|
167
|
+
}
|
|
168
|
+
// 10. Check for updates
|
|
169
|
+
try {
|
|
170
|
+
const latest = (0, child_process_1.execSync)("npm view @llmtune/cli version", {
|
|
171
|
+
encoding: "utf-8",
|
|
172
|
+
timeout: 10000,
|
|
173
|
+
}).trim();
|
|
174
|
+
if (latest === version_1.CLI_VERSION) {
|
|
175
|
+
checks.push({ name: "Update", status: "pass", message: "Up to date" });
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
checks.push({
|
|
179
|
+
name: "Update",
|
|
180
|
+
status: "warn",
|
|
181
|
+
message: `Latest: v${latest} (you have v${version_1.CLI_VERSION})`,
|
|
182
|
+
fix: "Run: npm update -g @llmtune/cli",
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
checks.push({ name: "Update", status: "pass", message: "Could not check (offline?)" });
|
|
188
|
+
}
|
|
189
|
+
// Print results
|
|
190
|
+
for (const check of checks) {
|
|
191
|
+
const icon = check.status === "pass" ? chalk_1.default.green("✓")
|
|
192
|
+
: check.status === "warn" ? chalk_1.default.yellow("⚠")
|
|
193
|
+
: chalk_1.default.red("✗");
|
|
194
|
+
const label = chalk_1.default.bold(check.name.padEnd(16));
|
|
195
|
+
console.log(` ${icon} ${label} ${chalk_1.default.dim(check.message)}`);
|
|
196
|
+
if (check.fix && check.status !== "pass") {
|
|
197
|
+
console.log(`${" ".repeat(22)}${chalk_1.default.dim("→ " + check.fix)}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const fails = checks.filter(c => c.status === "fail").length;
|
|
201
|
+
const warns = checks.filter(c => c.status === "warn").length;
|
|
202
|
+
const passes = checks.filter(c => c.status === "pass").length;
|
|
203
|
+
console.log("");
|
|
204
|
+
if (fails === 0) {
|
|
205
|
+
console.log(chalk_1.default.green(` ${passes} passed, ${warns} warnings`));
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
console.log(chalk_1.default.red(` ${passes} passed, ${warns} warnings, ${fails} failed`));
|
|
209
|
+
}
|
|
210
|
+
console.log("");
|
|
211
|
+
}
|
|
212
|
+
//# sourceMappingURL=doctor.js.map
|
package/dist/commands/login.js
CHANGED
|
@@ -79,12 +79,14 @@ async function loginCommand() {
|
|
|
79
79
|
}
|
|
80
80
|
const apiBase = await ask("API base URL", existingUrl);
|
|
81
81
|
const model = await ask("Default model (leave empty for auto)", existingModel || undefined);
|
|
82
|
+
// FIX: Merge with existing config to preserve other keys (providers, preferences, etc.)
|
|
82
83
|
(0, config_1.saveConfig)({
|
|
84
|
+
...existing,
|
|
83
85
|
apiKey: key,
|
|
84
86
|
apiBase: apiBase.replace(/\/$/, ""),
|
|
85
87
|
defaultModel: model || undefined,
|
|
86
88
|
});
|
|
87
|
-
console.log(chalk_1.default.green("\
|
|
89
|
+
console.log(chalk_1.default.green("\n✓ Configuration saved!"));
|
|
88
90
|
console.log(` API key: ${maskKey(key)}`);
|
|
89
91
|
console.log(` API base: ${apiBase}`);
|
|
90
92
|
console.log(` Model: ${model || "auto"}`);
|
package/dist/commands/models.js
CHANGED
|
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.modelsCommand = modelsCommand;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
-
const
|
|
8
|
+
const markdown_1 = require("../utils/markdown");
|
|
9
9
|
const config_1 = require("../auth/config");
|
|
10
10
|
async function modelsCommand() {
|
|
11
11
|
const apiKey = (0, config_1.getApiKey)();
|
|
@@ -28,22 +28,16 @@ async function modelsCommand() {
|
|
|
28
28
|
process.exit(1);
|
|
29
29
|
}
|
|
30
30
|
const data = (await response.json());
|
|
31
|
+
console.log("");
|
|
31
32
|
if (data.subscription) {
|
|
32
|
-
console.log(chalk_1.default.cyan(
|
|
33
|
-
console.log(chalk_1.default.dim(`Daily quota: ${data.subscription.quotaDaily} requests
|
|
33
|
+
console.log(chalk_1.default.cyan(` Subscription: ${data.subscription.planName}`));
|
|
34
|
+
console.log(chalk_1.default.dim(` Daily quota: ${data.subscription.quotaDaily} requests`));
|
|
35
|
+
console.log("");
|
|
34
36
|
}
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
],
|
|
40
|
-
colWidths: [50, 20],
|
|
41
|
-
});
|
|
42
|
-
for (const model of data.data) {
|
|
43
|
-
table.push([model.id, model.owned_by]);
|
|
44
|
-
}
|
|
45
|
-
console.log(table.toString());
|
|
46
|
-
console.log(chalk_1.default.dim(`\n${data.data.length} models available`));
|
|
37
|
+
const headers = ["Model ID", "Provider"];
|
|
38
|
+
const rows = data.data.map((model) => [model.id, model.owned_by]);
|
|
39
|
+
console.log((0, markdown_1.renderTable)(headers, rows));
|
|
40
|
+
console.log(chalk_1.default.dim(`\n ${data.data.length} models available\n`));
|
|
47
41
|
}
|
|
48
42
|
catch (err) {
|
|
49
43
|
console.log(chalk_1.default.red(`Error: ${err instanceof Error ? err.message : String(err)}`));
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.updateCommand = updateCommand;
|
|
40
|
+
exports.checkForUpdate = checkForUpdate;
|
|
41
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
42
|
+
const child_process_1 = require("child_process");
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const os = __importStar(require("os"));
|
|
46
|
+
const version_1 = require("../version");
|
|
47
|
+
const CACHE_FILE = path.join(os.homedir(), ".llmtune", "cache", "update-check.json");
|
|
48
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // Once per day
|
|
49
|
+
async function updateCommand() {
|
|
50
|
+
console.log(chalk_1.default.dim("Checking for updates..."));
|
|
51
|
+
const current = version_1.CLI_VERSION;
|
|
52
|
+
const latest = fetchLatestVersion();
|
|
53
|
+
if (!latest) {
|
|
54
|
+
console.log(chalk_1.default.yellow("Could not check for updates. Are you online?"));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (latest === current) {
|
|
58
|
+
console.log(chalk_1.default.green(`\n Already on the latest version (v${current})\n`));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
console.log(chalk_1.default.yellow(`\n Update available: v${current} → v${latest}`));
|
|
62
|
+
console.log(chalk_1.default.dim(" Installing..."));
|
|
63
|
+
try {
|
|
64
|
+
(0, child_process_1.execSync)("npm install -g @llmtune/cli@latest", {
|
|
65
|
+
stdio: "inherit",
|
|
66
|
+
timeout: 120_000,
|
|
67
|
+
});
|
|
68
|
+
console.log(chalk_1.default.green(`\n Updated to v${latest}\n`));
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
console.log(chalk_1.default.red("\n Update failed. Try manually:"));
|
|
72
|
+
console.log(chalk_1.default.bold(" npm install -g @llmtune/cli@latest\n"));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check for updates (cached, runs once per day). Returns latest version or null.
|
|
77
|
+
*/
|
|
78
|
+
function checkForUpdate() {
|
|
79
|
+
try {
|
|
80
|
+
// Check cache first
|
|
81
|
+
try {
|
|
82
|
+
if (fs.existsSync(CACHE_FILE)) {
|
|
83
|
+
const cached = JSON.parse(fs.readFileSync(CACHE_FILE, "utf-8"));
|
|
84
|
+
if (Date.now() - cached.lastCheck < CHECK_INTERVAL_MS) {
|
|
85
|
+
return cached.latestVersion !== version_1.CLI_VERSION ? cached.latestVersion : null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
catch { /* ignore */ }
|
|
90
|
+
const latest = fetchLatestVersion();
|
|
91
|
+
if (!latest)
|
|
92
|
+
return null;
|
|
93
|
+
// Save to cache
|
|
94
|
+
const dir = path.dirname(CACHE_FILE);
|
|
95
|
+
if (!fs.existsSync(dir))
|
|
96
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
97
|
+
fs.writeFileSync(CACHE_FILE, JSON.stringify({ lastCheck: Date.now(), latestVersion: latest }), "utf-8");
|
|
98
|
+
return latest !== version_1.CLI_VERSION ? latest : null;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function fetchLatestVersion() {
|
|
105
|
+
try {
|
|
106
|
+
return (0, child_process_1.execSync)("npm view @llmtune/cli version", {
|
|
107
|
+
encoding: "utf-8",
|
|
108
|
+
timeout: 10_000,
|
|
109
|
+
}).trim();
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=update.js.map
|