Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
backend:
name: 后端(Java)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: 安装 JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: temurin
cache: maven
# 单测均为纯单元测试,不依赖 MySQL,可直接跑
- name: 编译 + 单测
run: ./mvnw -B clean test

app:
name: 桌面端(Flutter)
runs-on: ubuntu-latest
defaults:
run:
working-directory: clients/app
steps:
- uses: actions/checkout@v4
- name: 安装 Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
- name: 拉依赖
run: flutter pub get
- name: 静态分析
run: flutter analyze
- name: 单测
run: flutter test

cli:
name: CLI(Node)
runs-on: ubuntu-latest
defaults:
run:
working-directory: clients/cli
steps:
- uses: actions/checkout@v4
- name: 安装 Node 20
uses: actions/setup-node@v4
with:
node-version: '20'
cache: npm
cache-dependency-path: clients/cli/package-lock.json
- name: 装依赖
run: npm ci
- name: 类型检查
run: npm run typecheck
- name: 单测
run: npm test
- name: 构建
run: npm run build
8 changes: 3 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ target/
# 远端拉取的服务器日志,不进仓库
logs/

# 前端
frontend/node_modules/
frontend/dist/
# 构建产物:前端 build 会输出到此,不提交(由 CI/本地构建生成)
src/main/resources/static/
# CLI 客户端(Node)构建产物与依赖
clients/cli/node_modules/
clients/cli/dist/
Binary file added .mvn/wrapper/maven-wrapper.jar
Binary file not shown.
19 changes: 19 additions & 0 deletions .mvn/wrapper/maven-wrapper.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
wrapperVersion=3.3.2
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jar
6 changes: 3 additions & 3 deletions DESIGN.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# LowenSSH 设计系统

> AI SSH 智能运维 Agent 的前端设计源(source of truth)。两种界面形态共用这一套设计 token 与事件语义规则。
> AI SSH 智能运维 Agent 的界面设计源(source of truth)。桌面端与 CLI 共用这一套设计 token 与事件语义规则。

## 1. 设计 thesis(一句话)

Expand Down Expand Up @@ -91,8 +91,8 @@
## 8. 反 AI-slop 约束(自我要求)

- 不用紫色渐变、不用居中堆叠、不用三列图标网格、不用装饰性色块。
- 不引入 UI 组件库(Element/Antd)——它们会盖掉这套精心设计的事件语义色,且对一个 6 类事件的对话界面是杀鸡用牛刀。手写组件,保持设计控制权。
- 密码字段用 `type=password`;连接信息只在前端内存,不落 localStorage(避免明文密钥留在浏览器)
- 不引入重型 UI 组件库——它们会盖掉这套精心设计的事件语义色,且对一个 6 类事件的对话界面是杀鸡用牛刀。手写组件,保持设计控制权。
- 密码字段做遮挡输入;明文密码只在内存中短暂存在,落盘一律 AES-GCM 加密,不留明文

## 9. 安全相关的前端约束

Expand Down
21 changes: 5 additions & 16 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
# ---- 阶段 1:构建前端 ----
# Vite 产物输出到 ../src/main/resources/static,被后端打进 jar 一起托管
FROM node:22-alpine AS frontend
WORKDIR /build/frontend
# 先拷依赖清单,利用 Docker 层缓存:源码变了不必重装依赖
COPY frontend/package*.json ./
RUN npm ci
COPY frontend/ ./
# 产物写到 /build/src/main/resources/static(相对 outDir ../src/...)
RUN npm run build

# ---- 阶段 2:打后端 jar ----
# ---- 阶段 1:打后端 jar ----
FROM maven:3.9-eclipse-temurin-17 AS backend
WORKDIR /build
# 先拷 pom 预热依赖缓存
# 先拷 pom 预热依赖缓存:源码变了不必重新下依赖
COPY pom.xml ./
RUN mvn -q dependency:go-offline
# 拷后端源码 + 上一阶段构建好的前端产物
# 拷后端源码
COPY src/ ./src/
COPY --from=frontend /build/src/main/resources/static ./src/main/resources/static
# 跳过测试打包(测试需要 MySQL,构建环境没有)
RUN mvn -q clean package -DskipTests

# ---- 阶段 3:运行 ----
# ---- 阶段 2:运行 ----
# 只带 JRE,镜像更小
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=backend /build/target/lowenssh-*.jar app.jar
EXPOSE 8081
# 纯后端 API 服务,供 Flutter 桌面端 / CLI 客户端连接
# 密钥全走环境变量,镜像里不含任何凭据
ENTRYPOINT ["java", "-jar", "app.jar"]
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Lowen

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
66 changes: 36 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,41 @@

AI 驱动的 SSH 智能运维 Agent。给它一个运维目标和一台服务器,它会像工程师一样一步步排查:自己决定跑什么命令、读结果、调整思路,直到给出结论。危险命令会被安全门禁实时拦截。

> 这是一个面试项目,演示如何从零手写一个 agentic loop,而不是套用现成框架。核心看点是「看得见 AI 在干什么,也看得见安全护栏起作用」。
核心看点是「看得见 AI 在干什么,也看得见安全护栏起作用」——整个 agentic loop 是从零手写的,不套用任何编排框架。

## 三种形态

同一套「Agent loop + 安全门禁 + 上下文管理」理念,落地为三个独立实现,按需选用:

| 形态 | 目录 | 技术栈 | 说明 |
|------|------|--------|------|
| **后端服务** | `src/` | Java 17 · Spring Boot 3.4 · Spring AI | REST + SSE API,参考实现,逻辑最完整 |
| **桌面客户端** | `clients/app/` | Flutter(macOS / Windows) | 独立桌面应用,内置全套逻辑,直连大模型 |
| **CLI 客户端** | `clients/cli/` | Node 20 · Ink(TUI) | 终端里跑,类 Claude Code 的交互,内置全套逻辑 |

三者**互不依赖**:桌面端和 CLI 各自内置 SSH + Agent loop + 门禁 + 大模型调用,不需要先起后端。门禁规则与事件语义在三端手动对齐。

> 想了解手写 agentic loop、安全门禁、上下文管理的设计取舍,见 [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)。

## 能力

- **手写 Agentic Loop**:不依赖 LangChain 之类的编排框架,自己实现「模型决策 → 调工具 → 喂回结果 → 再决策」的循环,逻辑完全可控、可读。
- **安全门禁三态**:每条命令在执行前经过 `deny / ask / allow` 判定。`rm -rf`、`find -delete` 等高危操作直接拦截,模型被拦后会自主改用安全方式。
- **流式可视化**:SSE 实时推送 6 类事件(模型 token、要跑的命令、命令结果、被拦截、最终结论、错误),前端逐字渲染整个排查过程
- **流式可视化**:实时推送多类事件(模型 token、要跑的命令、命令结果、被拦截、最终结论、错误),逐字渲染整个排查过程
- **上下文管理**:多轮对话爆 context 时,自动做大工具结果截断 + 全量 LLM 摘要,复用消息表持久化。
- **双前端**:图形版(产品形态)和终端版(工具形态),同一套会话状态,两种界面随时切换。
- **全程审计**:每次连接、每条命令、每个拦截决策都落库,可追溯。

## 技术栈

**后端**:Java 17 · Spring Boot 3.4 · Spring AI 1.1 · JSch(SSH)· MyBatis-Plus · MySQL · GLM-4.6
**后端**:Java 17 · Spring Boot 3.4 · Spring AI 1.1 · JSch(SSH)· MyBatis-Plus · MySQL · GLM-4.6(OpenAI 兼容协议,可换任意兼容模型)

**桌面端**:Flutter · Dart(macOS / Windows 桌面)

**CLI**:Node 20 · TypeScript · Ink · ssh2 · openai SDK

**前端**:Vite 6 · Vue 3.5(Composition API)· vue-router 4
## 快速开始(后端服务)

## 快速开始
后端提供 REST + SSE API。客户端的运行方式见各自目录的 README([桌面端](clients/app/README.md) · [CLI](clients/cli/README.md))。

### 方式一:Docker 一键启动(推荐)

Expand All @@ -31,7 +48,7 @@ export GLM_API_KEY='你的智谱AI key' # https://open.bigmodel.cn 申请
docker compose up --build
```

compose 会自动起 MySQL(建库 + 执行 schema.sql 建表)、构建前后端、等 DB 就绪后启动应用。访问 http://localhost:8081 即可
compose 会自动起 MySQL(建库 + 执行 schema.sql 建表)、构建后端、等 DB 就绪后启动应用。API 监听 http://localhost:8081。

### 方式二:本地手动启动

Expand All @@ -48,34 +65,22 @@ export GLM_API_KEY='你的智谱AI key' # https://open.bigmodel.cn 申请

#### 2. 初始化数据库

先建库,再执行建表脚本:
先建库再执行建表脚本

```bash
mysql -u root -p -e "CREATE DATABASE IF NOT EXISTS lowenssh DEFAULT CHARSET utf8mb4;"
mysql -u root -p lowenssh < src/main/resources/schema.sql
```

#### 3. 构建前端
#### 3. 启动后端

```bash
cd frontend
npm install
npm run build # 产物输出到 ../src/main/resources/static/,由后端直接托管
```

#### 4. 启动后端
项目自带 Maven Wrapper,无需预装 Maven:

```bash
mvn spring-boot:run
./mvnw spring-boot:run # Windows 用 mvnw.cmd
```

访问 http://localhost:8081 即可使用(前端和 API 同端口)。

### 开发模式(前后端分离调试)

```bash
cd frontend && npm run dev # dev server 在 5173,/api 自动代理到后端 8081
```
API 监听 http://localhost:8081。

## 项目结构

Expand All @@ -87,19 +92,20 @@ LowenSSH/
│ └── ...
├── src/main/resources/
│ ├── application.yml # 配置(密钥走环境变量)
│ ├── schema.sql # 建表脚本
│ └── static/ # 前端构建产物(npm run build 生成)
├── frontend/ # Vite + Vue3 双界面前端(见 frontend/README.md)
└── DESIGN.md # 前端设计规范
│ └── schema.sql # 建表脚本
├── clients/
│ ├── app/ # Flutter 桌面客户端(见 clients/app/README.md)
│ └── cli/ # Node CLI 客户端(见 clients/cli/README.md)
└── DESIGN.md # 设计规范
```

## 安全说明

- 所有密钥走环境变量,源码无任何明文凭据。
- 前端密码字段不写入 localStorage/sessionStorage,不打印到控制台。
- 客户端密码字段不写入明文持久化(AES-GCM 加密落库),不打印到控制台。
- 安全门禁的高危命令规则(含 `rm -rf`、`find -delete` 等变体)是真实防护,请勿在生产前移除。
- 这是一个运维 Agent,会真实在目标服务器执行命令。请只连接你有权操作的服务器。

## License

MIT
[MIT](LICENSE)
50 changes: 39 additions & 11 deletions clients/app/README.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,45 @@
# lowenssh
# LowenSSH 桌面客户端

A new Flutter project.
LowenSSH 的桌面应用形态,基于 Flutter,支持 macOS 和 Windows。

## Getting Started
内置全套逻辑——SSH 连接、手写 Agent loop、安全门禁、上下文管理、直连大模型——**不依赖项目的 Java 后端**,独立运行。

This project is a starting point for a Flutter application.
## 环境要求

A few resources to get you started if this is your first Flutter project:
- [Flutter SDK](https://docs.flutter.dev/get-started/install) 3.12+(Dart 3.12+)
- macOS 构建需 Xcode;Windows 构建需 Visual Studio(含「使用 C++ 的桌面开发」工作负载)

- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
确认环境就绪:

For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
```bash
flutter doctor
```

## 运行

```bash
cd clients/app
flutter pub get

flutter run -d macos # macOS
flutter run -d windows # Windows
```

## 打包

```bash
flutter build macos # 产物在 build/macos/Build/Products/Release/
flutter build windows # 产物在 build/windows/x64/runner/Release/
```

## 大模型配置

首次启动后,在应用内「设置」里填入大模型 API Key(默认接入 GLM,走 OpenAI 兼容协议,可改 baseURL / model 换成任意兼容模型)。

配置保存在 `~/.lowenssh/config.json`。也可通过环境变量 `GLM_API_KEY` 注入,优先级高于配置文件,且不会被写回文件。

## 安全说明

- 主机密码 AES-GCM 加密后落盘,不存明文,不打印到控制台。
- 端口转发隧道默认绑定 `127.0.0.1`,仅本机可访问,不暴露到局域网。
- 这是一个运维 Agent,会真实在目标服务器执行命令。请只连接你有权操作的服务器。
Loading
Loading