@ghenya/clinn 0.7.13 → 0.7.15
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/Logos/StartLogo.txt +1 -1
- package/Src/index.js +7 -0
- package/Src/llm.js +29 -0
- package/config.json +1 -1
- package/install.js +43 -42
- package/package.json +1 -1
package/Logos/StartLogo.txt
CHANGED
package/Src/index.js
CHANGED
|
@@ -933,6 +933,13 @@ async function main() {
|
|
|
933
933
|
showLogo();
|
|
934
934
|
buildAgent();
|
|
935
935
|
|
|
936
|
+
if (!config.llm.apiKey || config.llm.apiKey.startsWith("YOUR_")) {
|
|
937
|
+
console.log(` ${C.yellow}⚠ 未配置 API Key${C.reset}`);
|
|
938
|
+
console.log(` 设置: ${C.cyan}/api key <你的DeepSeek Key>${C.reset}`);
|
|
939
|
+
console.log(` 获取: ${C.dim}https://platform.deepseek.com/${C.reset}`);
|
|
940
|
+
console.log(div("="));
|
|
941
|
+
}
|
|
942
|
+
|
|
936
943
|
readline.emitKeypressEvents(process.stdin);
|
|
937
944
|
if (process.stdin.isTTY) process.stdin.setRawMode(true);
|
|
938
945
|
|
package/Src/llm.js
CHANGED
|
@@ -88,6 +88,14 @@ class LLMClient {
|
|
|
88
88
|
let data = "";
|
|
89
89
|
res.on("data", (chunk) => (data += chunk));
|
|
90
90
|
res.on("end", () => {
|
|
91
|
+
if (res.statusCode === 401) {
|
|
92
|
+
reject(new Error("API Key 无效或未设置 — 请用 /api key <你的Key> 设置"));
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (res.statusCode === 403) {
|
|
96
|
+
reject(new Error("API Key 无权限 (403 Forbidden)"));
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
91
99
|
try {
|
|
92
100
|
const json = JSON.parse(data);
|
|
93
101
|
if (json.error) {
|
|
@@ -123,6 +131,27 @@ class LLMClient {
|
|
|
123
131
|
|
|
124
132
|
return new Promise((resolve, reject) => {
|
|
125
133
|
const req = https.request(options, (res) => {
|
|
134
|
+
if (res.statusCode === 401) {
|
|
135
|
+
reject(new Error("API Key 无效或未设置 — 请用 /api key <你的Key> 设置"));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (res.statusCode === 403) {
|
|
139
|
+
reject(new Error("API Key 无权限 (403 Forbidden)"));
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (res.statusCode >= 400) {
|
|
143
|
+
let body = "";
|
|
144
|
+
res.on("data", (c) => (body += c));
|
|
145
|
+
res.on("end", () => {
|
|
146
|
+
try {
|
|
147
|
+
const j = JSON.parse(body);
|
|
148
|
+
reject(new Error(j.error?.message || `HTTP ${res.statusCode}`));
|
|
149
|
+
} catch (_) {
|
|
150
|
+
reject(new Error(`HTTP ${res.statusCode}: ${body.slice(0, 200)}`));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
126
155
|
let buffer = "";
|
|
127
156
|
const toolCallsMap = {};
|
|
128
157
|
let content = "";
|
package/config.json
CHANGED
package/install.js
CHANGED
|
@@ -6,12 +6,11 @@ const fs = require("fs");
|
|
|
6
6
|
const path = require("path");
|
|
7
7
|
const { execSync } = require("child_process");
|
|
8
8
|
|
|
9
|
-
const VER = "0.7.
|
|
9
|
+
const VER = "0.7.14";
|
|
10
10
|
const G = "\x1b[0;32m", C = "\x1b[0;36m", Y = "\x1b[0;33m", R = "\x1b[0;31m", N = "\x1b[0m", D = "\x1b[2m";
|
|
11
11
|
|
|
12
12
|
const IS_WIN = process.platform === "win32";
|
|
13
13
|
const HOME = os.homedir();
|
|
14
|
-
const SRC = __dirname;
|
|
15
14
|
const CLINN_HOME = path.join(HOME, ".clinn");
|
|
16
15
|
const CLINN_CONFIG = path.join(CLINN_HOME, "config.json");
|
|
17
16
|
|
|
@@ -58,48 +57,61 @@ function cleanOldShellRC() {
|
|
|
58
57
|
}
|
|
59
58
|
}
|
|
60
59
|
|
|
61
|
-
function
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return lo && !lo.includes("clinn") && !lo.includes("\\Clinn\\");
|
|
71
|
-
});
|
|
72
|
-
const clean = parts.join(";");
|
|
73
|
-
try {
|
|
74
|
-
execSync(`powershell -NoProfile -Command "[Environment]::SetEnvironmentVariable('Path', '${clean.replace(/\\/g, "\\\\")}', '${level}')"`, { windowsHide: true });
|
|
75
|
-
} catch (_) {}
|
|
60
|
+
function getNpmBinDir() {
|
|
61
|
+
if (IS_WIN) {
|
|
62
|
+
return path.join(process.env.APPDATA || "", "npm");
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const prefix = execSync("npm config get prefix", { encoding: "utf-8" }).trim();
|
|
66
|
+
return path.join(prefix, "bin");
|
|
67
|
+
} catch (_) {
|
|
68
|
+
return "/usr/local/bin";
|
|
76
69
|
}
|
|
77
70
|
}
|
|
78
71
|
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
raw = execSync(`powershell -NoProfile -Command "[Environment]::GetEnvironmentVariable('Path', '${level}')"`, { encoding: "utf-8", windowsHide: true }).trim();
|
|
86
|
-
} catch (_) { continue; }
|
|
87
|
-
if (!raw.toLowerCase().includes(dest.toLowerCase())) {
|
|
88
|
-
raw = raw + ";" + dest;
|
|
72
|
+
function ensureNpmBinInPATH() {
|
|
73
|
+
const npmBin = getNpmBinDir();
|
|
74
|
+
if (IS_WIN) {
|
|
75
|
+
const levels = ["User", "Machine"];
|
|
76
|
+
for (const level of levels) {
|
|
77
|
+
let raw = "";
|
|
89
78
|
try {
|
|
90
|
-
execSync(`powershell -NoProfile -Command "[Environment]::
|
|
79
|
+
raw = execSync(`powershell -NoProfile -Command "[Environment]::GetEnvironmentVariable('Path', '${level}')"`, { encoding: "utf-8", windowsHide: true }).trim();
|
|
80
|
+
} catch (_) { continue; }
|
|
81
|
+
const norm = npmBin.toLowerCase().replace(/\//g, "\\");
|
|
82
|
+
const parts = raw.split(";").filter(p => {
|
|
83
|
+
const lo = p.toLowerCase().replace(/\//g, "\\");
|
|
84
|
+
return lo && !lo.includes("clinn") && !lo.includes("\\Clinn\\");
|
|
85
|
+
});
|
|
86
|
+
const has = parts.some(p => p.toLowerCase().replace(/\//g, "\\") === norm);
|
|
87
|
+
if (!has) parts.push(npmBin);
|
|
88
|
+
const clean = parts.join(";");
|
|
89
|
+
try {
|
|
90
|
+
execSync(`powershell -NoProfile -Command "[Environment]::SetEnvironmentVariable('Path', '${clean.replace(/\\/g, "\\\\")}', '${level}')"`, { windowsHide: true });
|
|
91
91
|
} catch (_) {}
|
|
92
92
|
}
|
|
93
|
+
console.log(` ${G}PATH 已注入:${N} ${D}${npmBin}${N}`);
|
|
94
|
+
} else {
|
|
95
|
+
const shellRC = process.env.SHELL?.includes("zsh") ? ".zshrc" : ".bashrc";
|
|
96
|
+
const rcPath = path.join(HOME, shellRC);
|
|
97
|
+
let content = "";
|
|
98
|
+
if (fs.existsSync(rcPath)) content = fs.readFileSync(rcPath, "utf-8");
|
|
99
|
+
const marker = "# npm global bin";
|
|
100
|
+
if (!content.includes(`export PATH="${npmBin}:$PATH"`) && !content.includes(npmBin)) {
|
|
101
|
+
content += `\n${marker}\nexport PATH="${npmBin}:$PATH"\n`;
|
|
102
|
+
fs.writeFileSync(rcPath, content, "utf-8");
|
|
103
|
+
console.log(` ${G}PATH 已注入:${N} ${D}${npmBin} → ${rcPath}${N}`);
|
|
104
|
+
}
|
|
93
105
|
}
|
|
94
106
|
}
|
|
95
107
|
|
|
96
108
|
cleanOldShellRC();
|
|
97
|
-
|
|
109
|
+
ensureNpmBinInPATH();
|
|
98
110
|
|
|
99
111
|
ensureClinnDir();
|
|
100
112
|
|
|
101
113
|
if (!fs.existsSync(CLINN_CONFIG)) {
|
|
102
|
-
const pkgCfg = path.join(
|
|
114
|
+
const pkgCfg = path.join(__dirname, "config.json");
|
|
103
115
|
if (fs.existsSync(pkgCfg)) {
|
|
104
116
|
const cfg = JSON.parse(fs.readFileSync(pkgCfg, "utf-8"));
|
|
105
117
|
if (oldKey && oldKey !== "YOUR_API_KEY" && oldKey !== "YOUR_DEEPSEEK_API_KEY_HERE") {
|
|
@@ -108,22 +120,11 @@ if (!fs.existsSync(CLINN_CONFIG)) {
|
|
|
108
120
|
fs.writeFileSync(CLINN_CONFIG, JSON.stringify(cfg, null, 2), "utf-8");
|
|
109
121
|
console.log(` ${G}配置文件已创建:${N} ${D}${CLINN_CONFIG}${N}`);
|
|
110
122
|
}
|
|
111
|
-
} else if (oldKey && oldKey !== "YOUR_API_KEY" && oldKey !== "YOUR_DEEPSEEK_API_KEY_HERE") {
|
|
112
|
-
try {
|
|
113
|
-
const cfg = JSON.parse(fs.readFileSync(CLINN_CONFIG, "utf-8"));
|
|
114
|
-
if (cfg.llm.apiKey !== oldKey) {
|
|
115
|
-
cfg.llm.apiKey = oldKey;
|
|
116
|
-
fs.writeFileSync(CLINN_CONFIG, JSON.stringify(cfg, null, 2), "utf-8");
|
|
117
|
-
}
|
|
118
|
-
} catch (_) {}
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
addToPATH();
|
|
122
|
-
|
|
123
|
-
console.log(` ${Y}npm bin shim 已由 npm 自动创建${N}`);
|
|
124
125
|
console.log("");
|
|
125
126
|
console.log(` ${G}============================================${N}`);
|
|
126
|
-
console.log(` ${G} 安装完成!
|
|
127
|
+
console.log(` ${G} 安装完成! 打开新终端输入 clinn 即可启动${N}`);
|
|
127
128
|
console.log(` ${G}============================================${N}`);
|
|
128
129
|
console.log("");
|
|
129
130
|
console.log(` 版本: ${C}clinn --version${N}`);
|