66 lines
3.0 KiB
Plaintext
66 lines
3.0 KiB
Plaintext
const express = require('express');
|
|
const { execSync } = require('child_process');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
const crypto = require('crypto');
|
|
|
|
const app = express();
|
|
app.use(express.json({limit:'10mb'}));
|
|
|
|
const REPO_BASE = '/opt/zhuyuan/repos';
|
|
const USERS = JSON.parse(fs.readFileSync('/opt/zhuyuan/console/users.json','utf8'));
|
|
const TOKENS = {};
|
|
|
|
app.post('/api/mcp/login',(req,res)=>{
|
|
const{username,password}=req.body;
|
|
const u=USERS[username];
|
|
if(!u||u.pass!==password)return res.status(401).json({e:'wrong'});
|
|
const t=crypto.randomBytes(16).toString('hex');
|
|
TOKENS[t]={username,repos:u.repos};
|
|
res.json({token:t,repos:u.repos,url:'https://guanghuzhiqiu.top/mcp'});
|
|
});
|
|
|
|
function auth(req){const t=req.headers['x-mcp-token']||req.query.token;return TOKENS[t]||null}
|
|
function allow(user,repo){return user.repos.some(r=>repo.startsWith(r))}
|
|
|
|
app.post('/mcp/wake',(req,res)=>{
|
|
const u=auth(req);if(!u)return res.status(403).json({e:'unauth'});
|
|
res.json({speaker:'铸渊',user:u.username,repos:u.repos,brains:'https://guanghulab.com/code/bingshuo/hololake-brains'});
|
|
});
|
|
|
|
app.get('/mcp/brain_status',(req,res)=>{
|
|
const u=auth(req);if(!u)return res.status(403).json({e:'unauth'});
|
|
res.json({loaded:true,source:'https://guanghulab.com/code/bingshuo/hololake-brains',repos:u.repos});
|
|
});
|
|
|
|
app.get('/mcp/repo_read_file',(req,res)=>{
|
|
const u=auth(req);if(!u)return res.status(403).json({e:'unauth'});
|
|
const{repo,file}=req.query;
|
|
if(!allow(u,repo))return res.status(403).json({e:'not your repo'});
|
|
try{res.json({content:execSync('git --git-dir='+path.join(REPO_BASE,repo)+' show HEAD:'+file,{timeout:5000}).toString()})}catch(e){res.status(404).json({e:'not found'})}
|
|
});
|
|
|
|
app.get('/mcp/repo_list_files',(req,res)=>{
|
|
const u=auth(req);if(!u)return res.status(403).json({e:'unauth'});
|
|
const{repo}=req.query;
|
|
if(!allow(u,repo))return res.status(403).json({e:'not your repo'});
|
|
try{res.json({files:execSync('git --git-dir='+path.join(REPO_BASE,repo)+' ls-tree -r --name-only HEAD',{timeout:5000}).toString().trim().split('\n').filter(Boolean)})}catch(e){res.json({files:[]})}
|
|
});
|
|
|
|
app.get('/mcp/repo_status',(req,res)=>{
|
|
const u=auth(req);if(!u)return res.status(403).json({e:'unauth'});
|
|
const{repo}=req.query;
|
|
if(!allow(u,repo))return res.status(403).json({e:'not your repo'});
|
|
try{
|
|
const log=execSync('git --git-dir='+path.join(REPO_BASE,repo)+' log --oneline -5 --format=%h|%s|%an|%ai',{timeout:5000}).toString().trim();
|
|
res.json({commits:log.split('\n').map(l=>{const[h,m,a,d]=l.split('|');return{hash:h,msg:m,author:a,date:d}})});
|
|
}catch(e){res.json({commits:[]})}
|
|
});
|
|
|
|
app.get('/mcp/setup',(req,res)=>{
|
|
res.type('text').send('# 光湖 MCP 配置\n\n1. 获取token: POST /mcp/api/mcp/login\n2. 配置MCP URL: https://guanghuzhiqiu.top/mcp\n3. Header: x-mcp-token\n4. 工具: wake, brain_status, repo_read_file, repo_list_files, repo_status\n5. 共享大脑: https://guanghulab.com/code/bingshuo/hololake-brains');
|
|
});
|
|
|
|
app.get('/mcp/health',(req,res)=>res.json({ok:1}));
|
|
app.listen(3950,'127.0.0.1',()=>console.log('MCP:3950'));
|