@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.
@@ -2,6 +2,6 @@
2
2
  ██ ██ ██ ████ ██ ████ ██
3
3
  ██ ██ ██ ██ ██ ██ ██ ██ ██
4
4
  ██ ██ ██ ██ ██ ██ ██ ██ ██
5
- ██████ ███████ ██ ██ ████ ██ █0.7.13
5
+ ██████ ███████ ██ ██ ████ ██ █0.7.15
6
6
 
7
7
 
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "agent": {
3
3
  "name": "Clinn",
4
- "version": "0.7.13",
4
+ "version": "0.7.15",
5
5
  "description": "控制台智能体助手"
6
6
  },
7
7
  "llm": {
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.13";
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 cleanOldPATH() {
62
- if (!IS_WIN) return;
63
- for (const level of ["User", "Machine"]) {
64
- let raw = "";
65
- try {
66
- raw = execSync(`powershell -NoProfile -Command "[Environment]::GetEnvironmentVariable('Path', '${level}')"`, { encoding: "utf-8", windowsHide: true }).trim();
67
- } catch (_) { continue; }
68
- const parts = raw.split(";").filter(p => {
69
- const lo = p.toLowerCase().replace(/\//g, "\\");
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 addToPATH() {
80
- if (!IS_WIN) return;
81
- const dest = path.join(process.env.LOCALAPPDATA || "", "Programs", "Clinn");
82
- for (const level of ["User"]) {
83
- let raw = "";
84
- try {
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]::SetEnvironmentVariable('Path', '${raw.replace(/\\/g, "\\\\")}', '${level}')"`, { windowsHide: true });
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
- cleanOldPATH();
109
+ ensureNpmBinInPATH();
98
110
 
99
111
  ensureClinnDir();
100
112
 
101
113
  if (!fs.existsSync(CLINN_CONFIG)) {
102
- const pkgCfg = path.join(SRC, "config.json");
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} 安装完成! 输入 clinn 即可启动${N}`);
127
+ console.log(` ${G} 安装完成! 打开新终端输入 clinn 即可启动${N}`);
127
128
  console.log(` ${G}============================================${N}`);
128
129
  console.log("");
129
130
  console.log(` 版本: ${C}clinn --version${N}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghenya/clinn",
3
- "version": "0.7.13",
3
+ "version": "0.7.15",
4
4
  "description": "终端原生 AI 编程助手 — DeepSeek 驱动,50+ 工具,对话记忆,虚拟浏览器",
5
5
  "main": "Src/index.js",
6
6
  "bin": {