glmcode 0.1.5 → 0.1.6
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/CHANGELOG.md +7 -6
- package/LICENSE +1 -0
- package/README.md +41 -10
- package/package.json +12 -6
- package/src/auth.js +54 -32
- package/src/cli.js +30 -87
- package/src/config.js +8 -17
- package/src/shell.js +5 -0
- package/glmcode-0.1.4.tgz +0 -0
- package/glmcode-0.1.5.tgz +0 -0
- package/src/agent.js +0 -46
- package/src/mcp.js +0 -13
- package/src/platform/browser.js +0 -30
- package/src/platform/shell.js +0 -64
- package/src/skills.js +0 -17
- package/src/tools.js +0 -14
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 0.1.
|
|
4
|
-
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
3
|
+
## 0.1.6
|
|
4
|
+
|
|
5
|
+
- 修复 API Key 检测:环境变量 + `~/.glmcode/auth.json`
|
|
6
|
+
- 增加 `auth set/status/logout`
|
|
7
|
+
- 修复没有 `termux-open-url` 时的 ENOENT 崩溃
|
|
8
|
+
- 更新智谱 API Key 管理页面
|
|
9
|
+
- 保留跨平台 Shell 检测与配置入口
|
package/LICENSE
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
MIT License
|
package/README.md
CHANGED
|
@@ -1,29 +1,60 @@
|
|
|
1
|
-
# GLMCode 0.1.
|
|
1
|
+
# GLMCode 0.1.6
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
Install:
|
|
3
|
+
## 安装
|
|
6
4
|
```bash
|
|
7
5
|
npm install -g glmcode
|
|
6
|
+
glmcode --version
|
|
8
7
|
```
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
## API Key 自动检测
|
|
10
|
+
检测顺序:
|
|
11
|
+
1. `ZHIPU_API_KEY`
|
|
12
|
+
2. `GLM_API_KEY`
|
|
13
|
+
3. `~/.glmcode/auth.json`
|
|
14
|
+
|
|
15
|
+
已有 Key:
|
|
11
16
|
```bash
|
|
12
|
-
glmcode
|
|
17
|
+
glmcode auth set YOUR_API_KEY
|
|
13
18
|
```
|
|
14
19
|
|
|
15
|
-
|
|
20
|
+
状态:
|
|
21
|
+
```bash
|
|
22
|
+
glmcode auth status
|
|
23
|
+
```
|
|
16
24
|
|
|
17
|
-
|
|
25
|
+
没有 Key:
|
|
18
26
|
```bash
|
|
19
27
|
glmcode auth login
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
无 GUI 环境:
|
|
31
|
+
```bash
|
|
20
32
|
glmcode auth login --no-browser
|
|
21
33
|
```
|
|
22
34
|
|
|
23
|
-
|
|
35
|
+
官方 API Key 页面:
|
|
36
|
+
https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys
|
|
37
|
+
|
|
38
|
+
## Shell
|
|
24
39
|
```bash
|
|
25
40
|
glmcode shell
|
|
26
41
|
glmcode shell list
|
|
42
|
+
glmcode run "echo hello"
|
|
27
43
|
```
|
|
28
44
|
|
|
29
|
-
|
|
45
|
+
0.1.6 不再假定 `termux-open-url` 一定存在;Windows、macOS、Linux/Alpine/Android 环境分别检测可用启动器和 Shell。
|
|
46
|
+
|
|
47
|
+
## 配置
|
|
48
|
+
配置:`~/.glmcode/config.json`
|
|
49
|
+
认证:`~/.glmcode/auth.json`
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
glmcode config
|
|
53
|
+
glmcode config set model glm-4.5
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## 开发
|
|
57
|
+
```bash
|
|
58
|
+
npm run check
|
|
59
|
+
npm pack
|
|
60
|
+
```
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glmcode",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Cross-platform GLM
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Cross-platform GLM CLI with local authentication and shell detection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"glmcode": "src/cli.js",
|
|
@@ -9,9 +9,15 @@
|
|
|
9
9
|
"gc": "src/cli.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"
|
|
13
|
-
"check": "node --check src/cli.js && node --check src/auth.js && node --check src/platform/shell.js && node --check src/platform/browser.js"
|
|
12
|
+
"check": "node --check src/cli.js && node --check src/auth.js && node --check src/config.js && node --check src/shell.js"
|
|
14
13
|
},
|
|
15
|
-
"
|
|
16
|
-
|
|
14
|
+
"files": [
|
|
15
|
+
"src/",
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=18"
|
|
22
|
+
}
|
|
17
23
|
}
|
package/src/auth.js
CHANGED
|
@@ -1,40 +1,62 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { execFileSync, spawn } from "node:child_process";
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
6
|
+
export const CONFIG_DIR = path.join(os.homedir(), ".glmcode");
|
|
7
|
+
export const AUTH_FILE = path.join(CONFIG_DIR, "auth.json");
|
|
8
|
+
export const API_KEYS_URL = "https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} catch { return ""; }
|
|
10
|
+
function readJson(file, fallback = {}) {
|
|
11
|
+
try { return JSON.parse(fs.readFileSync(file, "utf8")); } catch { return fallback; }
|
|
12
|
+
}
|
|
13
|
+
function writeJson(file, value) {
|
|
14
|
+
fs.mkdirSync(CONFIG_DIR, {recursive:true, mode:0o700});
|
|
15
|
+
fs.writeFileSync(file, JSON.stringify(value,null,2)+"\n", {mode:0o600});
|
|
16
|
+
try { fs.chmodSync(file,0o600); } catch {}
|
|
18
17
|
}
|
|
18
|
+
function key(v) { const s = v == null ? "" : String(v).trim(); return s || null; }
|
|
19
19
|
|
|
20
|
-
export
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
export function getApiKey() {
|
|
21
|
+
const env = key(process.env.ZHIPU_API_KEY) || key(process.env.GLM_API_KEY);
|
|
22
|
+
if (env) return {apiKey:env, source:"environment"};
|
|
23
|
+
const a = readJson(AUTH_FILE,{});
|
|
24
|
+
const saved = key(a.apiKey) || key(a.api_key) || key(a.key) || key(a.token);
|
|
25
|
+
return saved ? {apiKey:saved, source:AUTH_FILE} : null;
|
|
26
|
+
}
|
|
27
|
+
export function saveApiKey(apiKey) {
|
|
28
|
+
const k = key(apiKey);
|
|
29
|
+
if (!k) throw new Error("API Key 不能为空");
|
|
30
|
+
writeJson(AUTH_FILE,{provider:"zhipu",apiKey:k,updatedAt:new Date().toISOString()});
|
|
31
|
+
return AUTH_FILE;
|
|
32
|
+
}
|
|
33
|
+
export function clearAuth() { writeJson(AUTH_FILE,{}); }
|
|
24
34
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
function exists(cmd) {
|
|
36
|
+
try { execFileSync(process.platform==="win32" ? "where" : "which",[cmd],{stdio:"ignore"}); return true; }
|
|
37
|
+
catch { return false; }
|
|
38
|
+
}
|
|
39
|
+
export function openUrl(url) {
|
|
40
|
+
const candidates = process.platform==="win32"
|
|
41
|
+
? [["cmd",["/c","start","",url]]]
|
|
42
|
+
: process.platform==="darwin"
|
|
43
|
+
? [["open",[url]]]
|
|
44
|
+
: [["xdg-open",[url]],["termux-open-url",[url]],["gio",["open",url]]];
|
|
45
|
+
for (const [cmd,args] of candidates) {
|
|
46
|
+
if (!exists(cmd)) continue;
|
|
47
|
+
try { const p=spawn(cmd,args,{detached:true,stdio:"ignore"}); p.unref(); return true; } catch {}
|
|
28
48
|
}
|
|
29
|
-
|
|
30
|
-
if (noBrowser) console.log(`请在浏览器中打开:\n${loginUrl}\n`);
|
|
31
|
-
else await openBrowser(loginUrl);
|
|
32
|
-
return Boolean(getApiKey());
|
|
49
|
+
return false;
|
|
33
50
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
console.log(
|
|
51
|
+
export function login({noBrowser=false}={}) {
|
|
52
|
+
const found=getApiKey();
|
|
53
|
+
if (found) { console.log(`已检测到 API Key(来源:${found.source}),无需重新配置。`); return found; }
|
|
54
|
+
console.log("未检测到 GLM API Key。");
|
|
55
|
+
console.log(`API Key 管理页面:${API_KEYS_URL}`);
|
|
56
|
+
if (!noBrowser && openUrl(API_KEYS_URL)) console.log("已尝试打开默认浏览器。");
|
|
57
|
+
else console.log("当前环境无法自动打开浏览器,请手动复制上面的地址。");
|
|
58
|
+
console.log("浏览器登录不会自动把网页中的 API Key 暴露给 CLI。");
|
|
59
|
+
console.log("获得 Key 后运行:glmcode auth set <API_KEY>");
|
|
60
|
+
return null;
|
|
40
61
|
}
|
|
62
|
+
export function maskKey(k) { return k && k.length>8 ? `${k.slice(0,4)}...${k.slice(-4)}` : "********"; }
|
package/src/cli.js
CHANGED
|
@@ -1,88 +1,31 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
/skills Skills
|
|
33
|
-
/shell Shell 信息
|
|
34
|
-
/shell list 可用 Shell
|
|
35
|
-
/pwd 当前目录
|
|
36
|
-
/clear 清屏
|
|
37
|
-
/exit 退出
|
|
38
|
-
`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
async function main() {
|
|
42
|
-
const args = process.argv.slice(2);
|
|
43
|
-
if (args.includes("--version") || args.includes("-v")) return console.log(VERSION);
|
|
44
|
-
|
|
45
|
-
if (args[0] === "auth" && args[1] === "login") {
|
|
46
|
-
await login({ noBrowser: args.includes("--no-browser") });
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
if (args[0] === "models") {
|
|
50
|
-
models.forEach(([id, label]) => console.log(`${id}\t${label}`));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
if (args[0] === "shell") {
|
|
54
|
-
if (args[1] === "list") return listAvailableShells().forEach(x => console.log(x));
|
|
55
|
-
printShell();
|
|
56
|
-
return;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (!getApiKey()) await login();
|
|
60
|
-
|
|
61
|
-
console.log(`GLMCode v${VERSION}`);
|
|
62
|
-
printShell();
|
|
63
|
-
console.log(`模型: ${getConfig().model} Skills: ${discoverSkills().length}`);
|
|
64
|
-
|
|
65
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: "\n> " });
|
|
66
|
-
const agent = createAgent({ getConfig });
|
|
67
|
-
rl.prompt();
|
|
68
|
-
|
|
69
|
-
rl.on("line", async line => {
|
|
70
|
-
const text = line.trim();
|
|
71
|
-
if (!text) return rl.prompt();
|
|
72
|
-
if (text === "/exit" || text === "/quit") return rl.close();
|
|
73
|
-
if (text === "/help") { help(); return rl.prompt(); }
|
|
74
|
-
if (text === "/clear") { console.clear(); return rl.prompt(); }
|
|
75
|
-
if (text === "/auth") { await login(); return rl.prompt(); }
|
|
76
|
-
if (text === "/shell") { printShell(); return rl.prompt(); }
|
|
77
|
-
if (text === "/shell list") { listAvailableShells().forEach(x => console.log(x)); return rl.prompt(); }
|
|
78
|
-
if (text === "/skills") { discoverSkills().forEach(s => console.log(`${s.name}\t${s.path}`)); return rl.prompt(); }
|
|
79
|
-
if (text === "/models") { models.forEach(([id, label]) => console.log(`${id}\t${label}`)); return rl.prompt(); }
|
|
80
|
-
if (text.startsWith("/model ")) { saveConfig({ model: text.slice(7).trim() }); console.log(`模型已切换为 ${getConfig().model}`); return rl.prompt(); }
|
|
81
|
-
if (text === "/thinking") { saveConfig({ thinking: !getConfig().thinking }); console.log(`思考模式: ${getConfig().thinking ? "开" : "关"}`); return rl.prompt(); }
|
|
82
|
-
try { await agent.run(text); } catch (e) { console.error(`\n错误: ${e.message}`); }
|
|
83
|
-
rl.prompt();
|
|
84
|
-
});
|
|
85
|
-
rl.on("close", () => process.exit(0));
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
main().catch(e => { console.error(e); process.exit(1); });
|
|
2
|
+
import {spawnSync} from "node:child_process";
|
|
3
|
+
import {getApiKey,saveApiKey,clearAuth,login,maskKey} from "./auth.js";
|
|
4
|
+
import {loadConfig,saveConfig} from "./config.js";
|
|
5
|
+
import {shellInfo,availableShells} from "./shell.js";
|
|
6
|
+
const VERSION="0.1.6";
|
|
7
|
+
function help(){console.log(`GLMCode ${VERSION}
|
|
8
|
+
Usage: glmcode [command]
|
|
9
|
+
auth login [--no-browser] 检测 Key;没有则打开官方页面
|
|
10
|
+
auth set <API_KEY> 保存 Key 到 ~/.glmcode/auth.json
|
|
11
|
+
auth status 查看认证状态(Key 脱敏)
|
|
12
|
+
auth logout 删除本地 Key
|
|
13
|
+
shell 查看 OS/CPU/Shell
|
|
14
|
+
shell list 列出可用 Shell
|
|
15
|
+
run <command> 使用当前 Shell 执行命令
|
|
16
|
+
config 查看配置
|
|
17
|
+
config set <key> <value> 修改配置
|
|
18
|
+
skills / plugins / mcp 查看对应配置
|
|
19
|
+
--version 显示版本`);}
|
|
20
|
+
function runShell(command){const s=shellInfo(),win=process.platform==="win32";const f=win?(s.shell==="powershell"||s.shell==="pwsh"?s.executable:process.env.ComSpec||"cmd.exe"):s.executable;const a=win?(s.shell==="powershell"||s.shell==="pwsh"?["-NoProfile","-Command",command]:["/d","/s","/c",command]):["-lc",command];const r=spawnSync(f,a,{stdio:"inherit"});process.exitCode=r.status??1;}
|
|
21
|
+
function value(v){if(v==="true")return true;if(v==="false")return false;if(/^-?\d+(?:\.\d+)?$/.test(v))return Number(v);try{return JSON.parse(v);}catch{return v;}}
|
|
22
|
+
const a=process.argv.slice(2);
|
|
23
|
+
if(!a.length){const f=getApiKey();if(!f){console.log(`GLMCode ${VERSION}\n未检测到 API Key。运行 glmcode auth login。`);process.exitCode=1;}else{const c=loadConfig();console.log(`GLMCode ${VERSION}\nProvider: ${c.provider}\nModel: ${c.model}\nAPI Key: ${maskKey(f.apiKey)} (${f.source})\n认证检测成功。`);}}
|
|
24
|
+
else if(a[0]==="--version"||a[0]==="-v")console.log(VERSION);
|
|
25
|
+
else if(a[0]==="--help"||a[0]==="-h"||a[0]==="help")help();
|
|
26
|
+
else if(a[0]==="auth"){const s=a[1]||"status";if(s==="login")login({noBrowser:a.includes("--no-browser")});else if(s==="set"){if(!a[2]){console.error("用法:glmcode auth set <API_KEY>");process.exitCode=2;}else console.log(`API Key 已保存到 ${saveApiKey(a[2])}`);}else if(s==="status"){const f=getApiKey();console.log(f?`API Key: ${maskKey(f.apiKey)}\n来源: ${f.source}`:"未检测到 API Key。");}else if(s==="logout"){clearAuth();console.log("本地 API Key 已删除。");}else help();}
|
|
27
|
+
else if(a[0]==="shell"){if(a[1]==="list")console.log(availableShells().join("\n")||"未检测到额外 Shell");else{const s=shellInfo();console.log(`OS: ${s.os}\nArchitecture: ${s.architecture}\nShell: ${s.shell}\nExecutable: ${s.executable}`);}}
|
|
28
|
+
else if(a[0]==="run"){if(!a[1]){console.error("用法:glmcode run <command>");process.exitCode=2;}else runShell(a.slice(1).join(" "));}
|
|
29
|
+
else if(a[0]==="config"){const c=loadConfig();if(a[1]==="set"){if(!a[2]||a[3]===undefined){console.error("用法:glmcode config set <key> <value>");process.exitCode=2;}else{c[a[2]]=value(a.slice(3).join(" "));saveConfig(c);console.log(`已设置 ${a[2]}`);}}else console.log(JSON.stringify(c,null,2));}
|
|
30
|
+
else if(["skills","plugins","mcp"].includes(a[0]))console.log(JSON.stringify(loadConfig()[a[0]],null,2));
|
|
31
|
+
else{console.error(`未知命令:${a[0]}`);help();process.exitCode=2;}
|
package/src/config.js
CHANGED
|
@@ -1,17 +1,8 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
export function
|
|
9
|
-
try { return { model: "glm-4.5-air", thinking: false, ...JSON.parse(readFileSync(file, "utf8")) }; }
|
|
10
|
-
catch { return { model: "glm-4.5-air", thinking: false }; }
|
|
11
|
-
}
|
|
12
|
-
export function saveConfig(patch) {
|
|
13
|
-
mkdirSync(dir, { recursive: true });
|
|
14
|
-
const next = { ...getConfig(), ...patch };
|
|
15
|
-
writeFileSync(file, JSON.stringify(next, null, 2));
|
|
16
|
-
return next;
|
|
17
|
-
}
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export const CONFIG_DIR=path.join(os.homedir(),".glmcode");
|
|
5
|
+
export const CONFIG_FILE=path.join(CONFIG_DIR,"config.json");
|
|
6
|
+
const defaults={provider:"zhipu",model:"glm-4.5",freeModel:"glm-4.5-air",baseUrl:"https://open.bigmodel.cn/api/paas/v4",maxTokens:4096,temperature:0.7,thinking:true,approval:"ask",autoCompact:true,skillsDirs:[],plugins:[],mcp:{},startupCommand:"",autoOpenLogin:true};
|
|
7
|
+
export function loadConfig(){try{return {...defaults,...JSON.parse(fs.readFileSync(CONFIG_FILE,"utf8"))};}catch{return {...defaults};}}
|
|
8
|
+
export function saveConfig(c){fs.mkdirSync(CONFIG_DIR,{recursive:true,mode:0o700});fs.writeFileSync(CONFIG_FILE,JSON.stringify(c,null,2)+"\n",{mode:0o600});}
|
package/src/shell.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import {execFileSync} from "node:child_process";
|
|
3
|
+
function shell(){if(process.platform==="win32"){const p=process.env.PWSH||process.env.POWERSHELL;if(p)return{name:"powershell",executable:p};return{name:"cmd",executable:process.env.ComSpec||"cmd.exe"};}const e=process.env.SHELL||"/bin/sh";return{name:e.split("/").pop()||"sh",executable:e};}
|
|
4
|
+
export function shellInfo(){const s=shell();return{os:process.platform,architecture:process.arch,shell:s.name,executable:s.executable};}
|
|
5
|
+
export function availableShells(){const c=process.platform==="win32"?["cmd","powershell","pwsh"]:["sh","bash","zsh","fish","ash"];return c.filter(n=>{try{execFileSync(process.platform==="win32"?"where":"which",[n],{stdio:"ignore"});return true;}catch{return false;}});}
|
package/glmcode-0.1.4.tgz
DELETED
|
Binary file
|
package/glmcode-0.1.5.tgz
DELETED
|
Binary file
|
package/src/agent.js
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { getApiKey } from "./auth.js";
|
|
2
|
-
import { discoverSkills } from "./skills.js";
|
|
3
|
-
|
|
4
|
-
const endpoint = "https://open.bigmodel.cn/api/paas/v4/chat/completions";
|
|
5
|
-
|
|
6
|
-
export function createAgent({ getConfig }) {
|
|
7
|
-
return { async run(input) {
|
|
8
|
-
const key = getApiKey();
|
|
9
|
-
if (!key) throw new Error("没有可用的 GLM API Key,请运行 glmcode auth login。");
|
|
10
|
-
const cfg = getConfig();
|
|
11
|
-
const skillText = discoverSkills().map(s => `## Skill: ${s.name}\n${s.content}`).join("\n\n");
|
|
12
|
-
const res = await fetch(endpoint, {
|
|
13
|
-
method: "POST",
|
|
14
|
-
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${key}` },
|
|
15
|
-
body: JSON.stringify({
|
|
16
|
-
model: cfg.model,
|
|
17
|
-
messages: [
|
|
18
|
-
{ role: "system", content: `You are GLMCode, a coding assistant.\n${skillText}` },
|
|
19
|
-
{ role: "user", content: input }
|
|
20
|
-
],
|
|
21
|
-
stream: true
|
|
22
|
-
})
|
|
23
|
-
});
|
|
24
|
-
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
|
|
25
|
-
const reader = res.body.getReader();
|
|
26
|
-
const decoder = new TextDecoder();
|
|
27
|
-
let buffer = "";
|
|
28
|
-
while (true) {
|
|
29
|
-
const { value, done } = await reader.read();
|
|
30
|
-
if (done) break;
|
|
31
|
-
buffer += decoder.decode(value, { stream: true });
|
|
32
|
-
const lines = buffer.split("\n");
|
|
33
|
-
buffer = lines.pop() || "";
|
|
34
|
-
for (const line of lines) {
|
|
35
|
-
if (!line.startsWith("data:")) continue;
|
|
36
|
-
const data = line.slice(5).trim();
|
|
37
|
-
if (data === "[DONE]") continue;
|
|
38
|
-
try {
|
|
39
|
-
const delta = JSON.parse(data).choices?.[0]?.delta?.content;
|
|
40
|
-
if (delta) process.stdout.write(delta);
|
|
41
|
-
} catch {}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
process.stdout.write("\n");
|
|
45
|
-
}};
|
|
46
|
-
}
|
package/src/mcp.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { readFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { homedir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
|
|
5
|
-
const file = join(homedir(), ".glmcode", "mcp.json");
|
|
6
|
-
export function loadMcp() {
|
|
7
|
-
try { return JSON.parse(readFileSync(file, "utf8")); }
|
|
8
|
-
catch { return { mcpServers: {} }; }
|
|
9
|
-
}
|
|
10
|
-
export function saveMcp(config) {
|
|
11
|
-
mkdirSync(join(homedir(), ".glmcode"), { recursive: true });
|
|
12
|
-
writeFileSync(file, JSON.stringify(config, null, 2));
|
|
13
|
-
}
|
package/src/platform/browser.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { detectPlatform } from "./shell.js";
|
|
3
|
-
|
|
4
|
-
function tryRun(command, args) {
|
|
5
|
-
return new Promise(resolve => {
|
|
6
|
-
const child = execFile(command, args, { windowsHide: true }, error => resolve(!error));
|
|
7
|
-
child.on("error", () => resolve(false));
|
|
8
|
-
});
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export async function openBrowser(url) {
|
|
12
|
-
const p = detectPlatform();
|
|
13
|
-
|
|
14
|
-
if (process.env.GLMCODE_NO_BROWSER === "1") {
|
|
15
|
-
console.log(`\n浏览器自动打开已禁用。\n请手动打开:\n${url}\n`);
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (p.platform === "win32") {
|
|
20
|
-
if (await tryRun("cmd.exe", ["/c", "start", "", url])) return true;
|
|
21
|
-
} else if (p.platform === "darwin") {
|
|
22
|
-
if (await tryRun("open", [url])) return true;
|
|
23
|
-
} else {
|
|
24
|
-
if (p.isTermux && await tryRun("termux-open-url", [url])) return true;
|
|
25
|
-
if (await tryRun("xdg-open", [url])) return true;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
console.log(`\n当前环境没有可用的浏览器启动器。\n请在浏览器中打开:\n${url}\n`);
|
|
29
|
-
return false;
|
|
30
|
-
}
|
package/src/platform/shell.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
import os from "node:os";
|
|
2
|
-
import { execFileSync } from "node:child_process";
|
|
3
|
-
|
|
4
|
-
function exists(command) {
|
|
5
|
-
try {
|
|
6
|
-
if (process.platform === "win32") {
|
|
7
|
-
execFileSync("where", [command], { stdio: "ignore" });
|
|
8
|
-
} else {
|
|
9
|
-
execFileSync("sh", ["-lc", `command -v ${JSON.stringify(command)}`], { stdio: "ignore" });
|
|
10
|
-
}
|
|
11
|
-
return true;
|
|
12
|
-
} catch { return false; }
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function detectPlatform() {
|
|
16
|
-
return {
|
|
17
|
-
platform: process.platform,
|
|
18
|
-
arch: process.arch,
|
|
19
|
-
isTermux: Boolean(process.env.TERMUX_VERSION || process.env.PREFIX?.includes("com.termux")),
|
|
20
|
-
release: os.release()
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export function detectShell() {
|
|
25
|
-
const p = detectPlatform();
|
|
26
|
-
if (p.platform === "win32") {
|
|
27
|
-
if (process.env.PSModulePath && exists("pwsh")) return { name: "PowerShell", executable: "pwsh" };
|
|
28
|
-
if (process.env.PSModulePath) return { name: "PowerShell", executable: "powershell.exe" };
|
|
29
|
-
return { name: "CMD", executable: process.env.ComSpec || "cmd.exe" };
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const envShell = process.env.SHELL || "";
|
|
33
|
-
const name = envShell.split("/").pop();
|
|
34
|
-
if (["bash", "zsh", "fish", "sh", "dash", "ksh", "nu"].includes(name) && exists(name)) {
|
|
35
|
-
return { name, executable: envShell };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
for (const candidate of ["bash", "zsh", "fish", "sh"]) {
|
|
39
|
-
if (exists(candidate)) return { name: candidate, executable: candidate };
|
|
40
|
-
}
|
|
41
|
-
return { name: "sh", executable: "/bin/sh" };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function listAvailableShells() {
|
|
45
|
-
if (process.platform === "win32") {
|
|
46
|
-
return [
|
|
47
|
-
...(exists("pwsh") ? ["powershell"] : []),
|
|
48
|
-
...(exists("powershell.exe") ? ["powershell.exe"] : []),
|
|
49
|
-
"cmd.exe",
|
|
50
|
-
...(exists("bash.exe") ? ["bash.exe"] : [])
|
|
51
|
-
];
|
|
52
|
-
}
|
|
53
|
-
return ["sh", ...(exists("bash") ? ["bash"] : []), ...(exists("zsh") ? ["zsh"] : []), ...(exists("fish") ? ["fish"] : [])];
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function shellCommand(command, shell = detectShell().executable) {
|
|
57
|
-
if (process.platform === "win32" && /powershell|pwsh/i.test(shell)) {
|
|
58
|
-
return { file: shell, args: ["-NoLogo", "-NoProfile", "-Command", command] };
|
|
59
|
-
}
|
|
60
|
-
if (process.platform === "win32" && /cmd/i.test(shell)) {
|
|
61
|
-
return { file: shell, args: ["/d", "/s", "/c", command] };
|
|
62
|
-
}
|
|
63
|
-
return { file: shell, args: ["-lc", command] };
|
|
64
|
-
}
|
package/src/skills.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { homedir } from "node:os";
|
|
4
|
-
|
|
5
|
-
export function discoverSkills() {
|
|
6
|
-
const roots = [join(process.cwd(), ".glmcode", "skills"), join(homedir(), ".glmcode", "skills")];
|
|
7
|
-
const out = [];
|
|
8
|
-
for (const root of roots) {
|
|
9
|
-
if (!existsSync(root)) continue;
|
|
10
|
-
for (const e of readdirSync(root, { withFileTypes: true })) {
|
|
11
|
-
if (!e.isDirectory()) continue;
|
|
12
|
-
const p = join(root, e.name, "SKILL.md");
|
|
13
|
-
if (existsSync(p)) out.push({ name: e.name, path: p, content: readFileSync(p, "utf8") });
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
return out;
|
|
17
|
-
}
|
package/src/tools.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from "node:fs";
|
|
2
|
-
import { execFile } from "node:child_process";
|
|
3
|
-
import { detectShell, shellCommand } from "./platform/shell.js";
|
|
4
|
-
|
|
5
|
-
export async function readFile(path) { return fs.readFile(path, "utf8"); }
|
|
6
|
-
export async function writeFile(path, content) { await fs.writeFile(path, content, "utf8"); return `wrote ${path}`; }
|
|
7
|
-
|
|
8
|
-
export async function runShell(command, cwd = process.cwd()) {
|
|
9
|
-
const s = detectShell(), spec = shellCommand(command, s.executable);
|
|
10
|
-
return await new Promise((resolve, reject) => {
|
|
11
|
-
execFile(spec.file, spec.args, { cwd, maxBuffer: 10 * 1024 * 1024 },
|
|
12
|
-
(error, stdout, stderr) => error ? reject(new Error(stderr || error.message)) : resolve({ stdout, stderr, code: 0, shell: s.name }));
|
|
13
|
-
});
|
|
14
|
-
}
|