@bddiudiu/vibeguard 0.1.0
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/.github/workflows/publish.yml +40 -0
- package/.vibeguard.yaml +49 -0
- package/README.md +203 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +47 -0
- package/dist/cli.js.map +1 -0
- package/dist/connectors/ollama.d.ts +7 -0
- package/dist/connectors/ollama.d.ts.map +1 -0
- package/dist/connectors/ollama.js +26 -0
- package/dist/connectors/ollama.js.map +1 -0
- package/dist/connectors/openai.d.ts +8 -0
- package/dist/connectors/openai.d.ts.map +1 -0
- package/dist/connectors/openai.js +20 -0
- package/dist/connectors/openai.js.map +1 -0
- package/dist/core/analyzer.d.ts +12 -0
- package/dist/core/analyzer.d.ts.map +1 -0
- package/dist/core/analyzer.js +38 -0
- package/dist/core/analyzer.js.map +1 -0
- package/dist/core/audit.d.ts +9 -0
- package/dist/core/audit.d.ts.map +1 -0
- package/dist/core/audit.js +52 -0
- package/dist/core/audit.js.map +1 -0
- package/dist/core/config.d.ts +27 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +46 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/git.d.ts +17 -0
- package/dist/core/git.d.ts.map +1 -0
- package/dist/core/git.js +92 -0
- package/dist/core/git.js.map +1 -0
- package/dist/rules/hallucination.d.ts +7 -0
- package/dist/rules/hallucination.d.ts.map +1 -0
- package/dist/rules/hallucination.js +20 -0
- package/dist/rules/hallucination.js.map +1 -0
- package/dist/rules/index.d.ts +3 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +27 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/security.d.ts +7 -0
- package/dist/rules/security.d.ts.map +1 -0
- package/dist/rules/security.js +19 -0
- package/dist/rules/security.js.map +1 -0
- package/package.json +54 -0
- package/src/cli.ts +61 -0
- package/src/connectors/ollama.ts +38 -0
- package/src/connectors/openai.ts +32 -0
- package/src/core/analyzer.ts +68 -0
- package/src/core/audit.ts +72 -0
- package/src/core/config.ts +76 -0
- package/src/core/git.ts +108 -0
- package/src/rules/hallucination.ts +27 -0
- package/src/rules/index.ts +37 -0
- package/src/rules/security.ts +25 -0
- package/tests/hallucination.test.ts +93 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
id-token: write
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
build-and-test:
|
|
13
|
+
runs-on: ubuntu-latest
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@v4
|
|
16
|
+
- uses: actions/setup-node@v4
|
|
17
|
+
with:
|
|
18
|
+
node-version: 20
|
|
19
|
+
cache: npm
|
|
20
|
+
- run: npm ci
|
|
21
|
+
- run: npm version "${GITHUB_REF_NAME#v}" --no-git-tag-version
|
|
22
|
+
- run: npm run build
|
|
23
|
+
- run: npm test
|
|
24
|
+
|
|
25
|
+
publish:
|
|
26
|
+
needs: build-and-test
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: actions/setup-node@v4
|
|
31
|
+
with:
|
|
32
|
+
node-version: 20
|
|
33
|
+
registry-url: https://registry.npmjs.org
|
|
34
|
+
cache: npm
|
|
35
|
+
- run: npm ci
|
|
36
|
+
- run: npm version "${GITHUB_REF_NAME#v}" --no-git-tag-version
|
|
37
|
+
- run: npm run build
|
|
38
|
+
- run: npm publish --provenance --access public
|
|
39
|
+
env:
|
|
40
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/.vibeguard.yaml
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# VibeGuard 默认规则配置
|
|
2
|
+
# 在项目根目录放置 .vibeguard.yaml 可覆盖此默认配置
|
|
3
|
+
|
|
4
|
+
# AI 模型配置
|
|
5
|
+
model:
|
|
6
|
+
# 提供商: openai | ollama
|
|
7
|
+
provider: ollama
|
|
8
|
+
# OpenAI 及兼容 API 配置 (当 provider=openai 时生效)
|
|
9
|
+
# 支持任何兼容 OpenAI 协议的第三方服务,只需设置 baseUrl
|
|
10
|
+
openai:
|
|
11
|
+
model: gpt-4o-mini
|
|
12
|
+
# 通过环境变量 OPENAI_API_KEY 设置 API Key (也可直接在此处填写)
|
|
13
|
+
# apiKey: sk-xxx
|
|
14
|
+
# 不设置 baseUrl 时默认使用 OpenAI 官方 API
|
|
15
|
+
# 第三方兼容服务示例:
|
|
16
|
+
# baseUrl: https://api.deepseek.com # DeepSeek
|
|
17
|
+
# baseUrl: https://api.moonshot.cn/v1 # Moonshot / Kimi
|
|
18
|
+
# baseUrl: https://api.openai-proxy.com # 任意 OpenAI 兼容代理
|
|
19
|
+
# Ollama 配置 (当 provider=ollama 时生效)
|
|
20
|
+
ollama:
|
|
21
|
+
model: llama3
|
|
22
|
+
baseUrl: http://localhost:11434
|
|
23
|
+
|
|
24
|
+
# 扫描配置
|
|
25
|
+
scan:
|
|
26
|
+
# 最大 diff 字符数,超出部分将被切片处理
|
|
27
|
+
maxDiffChars: 12000
|
|
28
|
+
# 是否启用语义幻觉检测
|
|
29
|
+
hallucinationDetection: true
|
|
30
|
+
# 是否启用安全扫描
|
|
31
|
+
securityScan: true
|
|
32
|
+
# 超时时间 (秒)
|
|
33
|
+
timeout: 30
|
|
34
|
+
|
|
35
|
+
# 架构禁令规则 (示例)
|
|
36
|
+
rules:
|
|
37
|
+
# 禁止使用的库
|
|
38
|
+
bannedImports:
|
|
39
|
+
- lodash.get
|
|
40
|
+
- request
|
|
41
|
+
# 禁止的模式
|
|
42
|
+
bannedPatterns:
|
|
43
|
+
- "eval("
|
|
44
|
+
- "new Function("
|
|
45
|
+
# 允许跳过的文件路径 (glob 模式)
|
|
46
|
+
ignorePaths:
|
|
47
|
+
- "dist/**"
|
|
48
|
+
- "node_modules/**"
|
|
49
|
+
- "*.min.js"
|
package/README.md
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# VibeGuard
|
|
2
|
+
|
|
3
|
+
**AI 代码生成的"语义安检门"**
|
|
4
|
+
|
|
5
|
+
> 你是否曾因为信任 AI 而直接合入了带有虚构 API 的代码?
|
|
6
|
+
> 你是否曾在 commit 里意外夹带了测试用的 Secret?
|
|
7
|
+
> **VibeGuard 在你按下 commit 键的一瞬,自动为你守住代码底线。**
|
|
8
|
+
|
|
9
|
+
## 核心特性
|
|
10
|
+
|
|
11
|
+
- **拦截幻觉** — 自动识别 AI 编造的函数、虚假 API 和逻辑矛盾
|
|
12
|
+
- **泄漏防护** — 语义识别硬编码的 API Key、Token 和敏感信息
|
|
13
|
+
- **零配置启动** — `npm install -g vibeguard && vibeguard init`
|
|
14
|
+
- **100% 本地隐私** — 完美适配 Ollama,让代码审计不出本地
|
|
15
|
+
|
|
16
|
+
## 工作原理
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
git commit → VibeGuard 提取 diff → AI 语义扫描 → 发现风险 → 拦截提交
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
VibeGuard 不查语法(那是 ESLint 的事),它只查"语义对不对"。它通过 Git Hook 介入你的开发流程,在 `git commit` 时自动扫描暂存区变更,利用 LLM(云端或本地 Ollama)进行语义分析,发现高危幻觉或安全风险时强制拦截 commit。
|
|
23
|
+
|
|
24
|
+
## 快速开始
|
|
25
|
+
|
|
26
|
+
### 安装
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm install -g vibeguard
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 初始化项目
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
cd your-project
|
|
36
|
+
vibeguard init
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
这会在你的项目中安装 pre-commit hook,之后每次 `git commit` 都会自动扫描。
|
|
40
|
+
|
|
41
|
+
### 手动扫描
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
vibeguard scan
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 跳过扫描
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
git commit --no-verify -m "skip vibeguard"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## 配置
|
|
54
|
+
|
|
55
|
+
在项目根目录创建 `.vibeguard.yaml`:
|
|
56
|
+
|
|
57
|
+
```yaml
|
|
58
|
+
model:
|
|
59
|
+
# 选择 AI 提供商: openai | ollama
|
|
60
|
+
provider: ollama
|
|
61
|
+
|
|
62
|
+
ollama:
|
|
63
|
+
model: llama3
|
|
64
|
+
baseUrl: http://localhost:11434
|
|
65
|
+
|
|
66
|
+
openai:
|
|
67
|
+
model: gpt-4o-mini
|
|
68
|
+
# API Key 通过环境变量 OPENAI_API_KEY 设置
|
|
69
|
+
|
|
70
|
+
scan:
|
|
71
|
+
maxDiffChars: 12000 # 最大扫描字符数
|
|
72
|
+
hallucinationDetection: true
|
|
73
|
+
securityScan: true
|
|
74
|
+
timeout: 30 # 超时时间(秒)
|
|
75
|
+
|
|
76
|
+
rules:
|
|
77
|
+
bannedImports:
|
|
78
|
+
- lodash.get
|
|
79
|
+
- request
|
|
80
|
+
bannedPatterns:
|
|
81
|
+
- "eval("
|
|
82
|
+
- "new Function("
|
|
83
|
+
ignorePaths:
|
|
84
|
+
- "dist/**"
|
|
85
|
+
- "*.min.js"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### 使用本地模型 (推荐)
|
|
89
|
+
|
|
90
|
+
1. 安装 [Ollama](https://ollama.ai)
|
|
91
|
+
2. 拉取模型: `ollama pull llama3`
|
|
92
|
+
3. 在 `.vibeguard.yaml` 中设置 `provider: ollama`
|
|
93
|
+
|
|
94
|
+
## 检测能力
|
|
95
|
+
|
|
96
|
+
### 幻觉检测
|
|
97
|
+
|
|
98
|
+
- 虚假 API 调用(如 `requests.get_with_auto_retry()`)
|
|
99
|
+
- 虚构的库引用(不存在的 npm 包或模块)
|
|
100
|
+
- 逻辑矛盾与类型不匹配
|
|
101
|
+
- 未实现的占位代码
|
|
102
|
+
- 架构禁令违规
|
|
103
|
+
|
|
104
|
+
### 安全扫描
|
|
105
|
+
|
|
106
|
+
- 硬编码密钥(API Key、Token、密码)
|
|
107
|
+
- 注入风险(SQL 注入、命令注入、XSS)
|
|
108
|
+
- 不安全的加密算法(MD5、SHA1)
|
|
109
|
+
- 危险函数调用(eval、new Function)
|
|
110
|
+
- 敏感信息泄露
|
|
111
|
+
|
|
112
|
+
## CLI 命令
|
|
113
|
+
|
|
114
|
+
| 命令 | 说明 |
|
|
115
|
+
|------|------|
|
|
116
|
+
| `vibeguard scan` | 扫描暂存区变更 |
|
|
117
|
+
| `vibeguard init` | 安装 pre-commit hook |
|
|
118
|
+
| `vibeguard uninstall` | 卸载 pre-commit hook |
|
|
119
|
+
| `vibeguard config` | 显示当前生效配置 |
|
|
120
|
+
| `vibeguard --help` | 查看帮助 |
|
|
121
|
+
|
|
122
|
+
## 工程架构
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
vibeguard/
|
|
126
|
+
├── src/
|
|
127
|
+
│ ├── cli.ts # CLI 入口
|
|
128
|
+
│ ├── core/
|
|
129
|
+
│ │ ├── audit.ts # 扫描调度器
|
|
130
|
+
│ │ ├── analyzer.ts # AI 分析与结果解析
|
|
131
|
+
│ │ ├── config.ts # 配置加载
|
|
132
|
+
│ │ └── git.ts # Git diff 提取与 Hook 管理
|
|
133
|
+
│ ├── connectors/
|
|
134
|
+
│ │ ├── openai.ts # OpenAI 适配器
|
|
135
|
+
│ │ └── ollama.ts # Ollama 适配器
|
|
136
|
+
│ └── rules/
|
|
137
|
+
│ ├── index.ts # Prompt 构建器
|
|
138
|
+
│ ├── hallucination.ts # 幻觉检测 Prompt
|
|
139
|
+
│ └── security.ts # 安全扫描 Prompt
|
|
140
|
+
├── tests/
|
|
141
|
+
├── .vibeguard.yaml # 默认规则配置
|
|
142
|
+
├── package.json
|
|
143
|
+
└── README.md
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## 风险与规避
|
|
147
|
+
|
|
148
|
+
| 风险 | 规避方案 |
|
|
149
|
+
|------|----------|
|
|
150
|
+
| 语义分析误伤正常代码 | 支持 `--no-verify` 跳过,本地白名单配置 |
|
|
151
|
+
| LLM 调用延迟 | 只扫描 diff 变更行及上下文,非全文件扫描 |
|
|
152
|
+
| 云端 API 成本 | 默认适配 Ollama 本地模型,零成本审计 |
|
|
153
|
+
|
|
154
|
+
## 开发
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
# 克隆项目
|
|
158
|
+
git clone https://github.com/your-org/vibeguard.git
|
|
159
|
+
cd vibeguard
|
|
160
|
+
|
|
161
|
+
# 安装依赖
|
|
162
|
+
npm install
|
|
163
|
+
|
|
164
|
+
# 开发模式 (监听编译)
|
|
165
|
+
npm run dev
|
|
166
|
+
|
|
167
|
+
# 运行测试
|
|
168
|
+
npm test
|
|
169
|
+
|
|
170
|
+
# 构建
|
|
171
|
+
npm run build
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
## 发布
|
|
175
|
+
|
|
176
|
+
发布通过 GitHub Actions 自动完成,只需两步:
|
|
177
|
+
|
|
178
|
+
1. 在 GitHub 仓库 Settings → Secrets 中添加 `NPM_TOKEN`(npm Access Token)
|
|
179
|
+
2. 在 GitHub 创建 Release,填写 tag(如 `v0.1.0`)并发布
|
|
180
|
+
|
|
181
|
+
Actions 会自动完成:构建 → 测试 → 发布到 npm。
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
# 也可以通过 git tag 触发
|
|
185
|
+
git tag v0.1.0
|
|
186
|
+
git push origin v0.1.0
|
|
187
|
+
# 然后在 GitHub 上基于该 tag 创建 Release
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 路线图
|
|
191
|
+
|
|
192
|
+
- [x] CLI 核心与 Git diff 提取
|
|
193
|
+
- [x] OpenAI API 集成(含第三方兼容 API)
|
|
194
|
+
- [x] Ollama 本地模型支持
|
|
195
|
+
- [x] pre-commit hook 自动安装
|
|
196
|
+
- [x] 终端风险等级可视化
|
|
197
|
+
- [x] 大型 Diff 切片处理
|
|
198
|
+
- [x] NPM 发布(GitHub Actions 自动化)
|
|
199
|
+
- [ ] IDE 插件 (VS Code / Cursor)
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { runScan } from "./core/audit.js";
|
|
5
|
+
import { installHook, uninstallHook } from "./core/git.js";
|
|
6
|
+
import { loadConfig } from "./core/config.js";
|
|
7
|
+
const program = new Command();
|
|
8
|
+
program
|
|
9
|
+
.name("vibeguard")
|
|
10
|
+
.description("AI 代码生成的语义安检门 — 在 git commit 前拦截幻觉与安全隐患")
|
|
11
|
+
.version("0.1.0");
|
|
12
|
+
program
|
|
13
|
+
.command("scan")
|
|
14
|
+
.description("扫描当前暂存区的变更")
|
|
15
|
+
.option("--no-verify", "强制扫描,不阻止 commit")
|
|
16
|
+
.action(async (opts) => {
|
|
17
|
+
const config = await loadConfig();
|
|
18
|
+
const result = await runScan(config);
|
|
19
|
+
if (!result.passed && opts.verify !== false) {
|
|
20
|
+
console.log(chalk.red.bold("\n🛡️ VibeGuard 拦截了本次提交,请修复上述问题后重试。"));
|
|
21
|
+
process.exit(1);
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
program
|
|
25
|
+
.command("init")
|
|
26
|
+
.description("在当前项目安装 git pre-commit hook")
|
|
27
|
+
.action(async () => {
|
|
28
|
+
await installHook();
|
|
29
|
+
console.log(chalk.green("✅ pre-commit hook 已安装。"));
|
|
30
|
+
console.log(chalk.dim(" 提交时 VibeGuard 将自动扫描变更。使用 --no-verify 可跳过。"));
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command("uninstall")
|
|
34
|
+
.description("卸载 git pre-commit hook")
|
|
35
|
+
.action(async () => {
|
|
36
|
+
await uninstallHook();
|
|
37
|
+
console.log(chalk.yellow("⚠️ pre-commit hook 已卸载。"));
|
|
38
|
+
});
|
|
39
|
+
program
|
|
40
|
+
.command("config")
|
|
41
|
+
.description("显示当前生效的配置")
|
|
42
|
+
.action(async () => {
|
|
43
|
+
const config = await loadConfig();
|
|
44
|
+
console.log(JSON.stringify(config, null, 2));
|
|
45
|
+
});
|
|
46
|
+
program.parse();
|
|
47
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,WAAW,CAAC;KACjB,WAAW,CAAC,yCAAyC,CAAC;KACtD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,YAAY,CAAC;KACzB,MAAM,CAAC,aAAa,EAAE,iBAAiB,CAAC;KACxC,MAAM,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;IACrB,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IAErC,IAAI,CAAC,MAAM,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QAC5C,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,IAAI,CACZ,sCAAsC,CACvC,CACF,CAAC;QACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,MAAM,CAAC;KACf,WAAW,CAAC,6BAA6B,CAAC;KAC1C,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,WAAW,EAAE,CAAC;IACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAC1D,CAAC;AACJ,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,wBAAwB,CAAC;KACrC,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,aAAa,EAAE,CAAC;IACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,0BAA0B,CAAC,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEL,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,WAAW,CAAC;KACxB,MAAM,CAAC,KAAK,IAAI,EAAE;IACjB,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/C,CAAC,CAAC,CAAC;AAEL,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../src/connectors/ollama.ts"],"names":[],"mappings":"AAAA,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,MAAM,CAAC,CA6BjB"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export async function callOllama(prompt, config) {
|
|
2
|
+
const response = await fetch(`${config.baseUrl}/api/chat`, {
|
|
3
|
+
method: "POST",
|
|
4
|
+
headers: { "Content-Type": "application/json" },
|
|
5
|
+
body: JSON.stringify({
|
|
6
|
+
model: config.model,
|
|
7
|
+
messages: [
|
|
8
|
+
{
|
|
9
|
+
role: "system",
|
|
10
|
+
content: "你是代码审计专家。请严格以 JSON 数组格式返回检测结果,不要包含任何其他文本。如果没有发现问题,返回空数组 []。",
|
|
11
|
+
},
|
|
12
|
+
{ role: "user", content: prompt },
|
|
13
|
+
],
|
|
14
|
+
stream: false,
|
|
15
|
+
options: {
|
|
16
|
+
temperature: 0.1,
|
|
17
|
+
},
|
|
18
|
+
}),
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`Ollama API error: ${response.status} ${response.statusText}`);
|
|
22
|
+
}
|
|
23
|
+
const data = (await response.json());
|
|
24
|
+
return data.message?.content ?? "[]";
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=ollama.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ollama.js","sourceRoot":"","sources":["../../src/connectors/ollama.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,MAAoB;IAEpB,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,WAAW,EAAE;QACzD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,QAAQ;oBACd,OAAO,EACL,6DAA6D;iBAChE;gBACD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;aAClC;YACD,MAAM,EAAE,KAAK;YACb,OAAO,EAAE;gBACP,WAAW,EAAE,GAAG;aACjB;SACF,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CACb,qBAAqB,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAC9D,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAuC,CAAC;IAC3E,OAAO,IAAI,CAAC,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;AACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/connectors/openai.ts"],"names":[],"mappings":"AAEA,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import OpenAI from "openai";
|
|
2
|
+
export async function callOpenAI(prompt, config) {
|
|
3
|
+
const client = new OpenAI({
|
|
4
|
+
apiKey: config.apiKey || process.env.OPENAI_API_KEY,
|
|
5
|
+
baseURL: config.baseUrl,
|
|
6
|
+
});
|
|
7
|
+
const response = await client.chat.completions.create({
|
|
8
|
+
model: config.model,
|
|
9
|
+
temperature: 0.1,
|
|
10
|
+
messages: [
|
|
11
|
+
{
|
|
12
|
+
role: "system",
|
|
13
|
+
content: "你是代码审计专家。请严格以 JSON 数组格式返回检测结果,不要包含任何其他文本。如果没有发现问题,返回空数组 []。",
|
|
14
|
+
},
|
|
15
|
+
{ role: "user", content: prompt },
|
|
16
|
+
],
|
|
17
|
+
});
|
|
18
|
+
return response.choices[0]?.message?.content ?? "[]";
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=openai.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/connectors/openai.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAQ5B,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAAc,EACd,MAAoB;IAEpB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;QACxB,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QACnD,OAAO,EAAE,MAAM,CAAC,OAAO;KACxB,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;QACpD,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,WAAW,EAAE,GAAG;QAChB,QAAQ,EAAE;YACR;gBACE,IAAI,EAAE,QAAQ;gBACd,OAAO,EACL,6DAA6D;aAChE;YACD,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;SAClC;KACF,CAAC,CAAC;IAEH,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,IAAI,IAAI,CAAC;AACvD,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DiffEntry } from "./git.js";
|
|
2
|
+
import { VibeguardConfig } from "./config.js";
|
|
3
|
+
export interface AuditResult {
|
|
4
|
+
file: string;
|
|
5
|
+
line?: number;
|
|
6
|
+
severity: "high" | "medium" | "low";
|
|
7
|
+
category: string;
|
|
8
|
+
message: string;
|
|
9
|
+
suggestion?: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function analyzeWithAI(entry: DiffEntry, config: VibeguardConfig): Promise<AuditResult[]>;
|
|
12
|
+
//# sourceMappingURL=analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../../src/core/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK9C,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAUD,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,eAAe,GACtB,OAAO,CAAC,WAAW,EAAE,CAAC,CAYxB"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { callOpenAI } from "../connectors/openai.js";
|
|
2
|
+
import { callOllama } from "../connectors/ollama.js";
|
|
3
|
+
import { buildAuditPrompt } from "../rules/index.js";
|
|
4
|
+
export async function analyzeWithAI(entry, config) {
|
|
5
|
+
const prompt = buildAuditPrompt(entry.diff, config);
|
|
6
|
+
const timeout = config.scan.timeout * 1000;
|
|
7
|
+
const raw = await Promise.race([
|
|
8
|
+
callAI(prompt, config),
|
|
9
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error("AI scan timeout")), timeout)),
|
|
10
|
+
]);
|
|
11
|
+
return parseResults(entry.filePath, raw);
|
|
12
|
+
}
|
|
13
|
+
async function callAI(prompt, config) {
|
|
14
|
+
if (config.model.provider === "ollama") {
|
|
15
|
+
return callOllama(prompt, config.model.ollama);
|
|
16
|
+
}
|
|
17
|
+
return callOpenAI(prompt, config.model.openai);
|
|
18
|
+
}
|
|
19
|
+
function parseResults(filePath, raw) {
|
|
20
|
+
const jsonMatch = raw.match(/\[[\s\S]*\]/);
|
|
21
|
+
if (!jsonMatch)
|
|
22
|
+
return [];
|
|
23
|
+
try {
|
|
24
|
+
const items = JSON.parse(jsonMatch[0]);
|
|
25
|
+
return items.map((item) => ({
|
|
26
|
+
file: filePath,
|
|
27
|
+
line: item.line,
|
|
28
|
+
severity: item.severity,
|
|
29
|
+
category: item.category,
|
|
30
|
+
message: item.message,
|
|
31
|
+
suggestion: item.suggestion,
|
|
32
|
+
}));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../../src/core/analyzer.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAmBrD,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,KAAgB,EAChB,MAAuB;IAEvB,MAAM,MAAM,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACpD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;IAE3C,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;QAC7B,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC;QACtB,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC,EAAE,OAAO,CAAC,CAChE;KACF,CAAC,CAAC;IAEH,OAAO,YAAY,CAAC,KAAK,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC;AAED,KAAK,UAAU,MAAM,CACnB,MAAc,EACd,MAAuB;IAEvB,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACvC,OAAO,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACjD,CAAC;IACD,OAAO,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AAED,SAAS,YAAY,CAAC,QAAgB,EAAE,GAAW;IACjD,MAAM,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;IAC3C,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,CAAC;IAE1B,IAAI,CAAC;QACH,MAAM,KAAK,GAAmB,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { VibeguardConfig } from "./config.js";
|
|
2
|
+
import { AuditResult } from "./analyzer.js";
|
|
3
|
+
export interface ScanResult {
|
|
4
|
+
passed: boolean;
|
|
5
|
+
issues: AuditResult[];
|
|
6
|
+
scannedFiles: number;
|
|
7
|
+
}
|
|
8
|
+
export declare function runScan(config: VibeguardConfig): Promise<ScanResult>;
|
|
9
|
+
//# sourceMappingURL=audit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,eAAe,EAAE,MAAM,aAAa,CAAC;AAC1D,OAAO,EAAiB,WAAW,EAAE,MAAM,eAAe,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,UAAU,CAAC,CA2B1E"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { getCachedDiff } from "./git.js";
|
|
3
|
+
import { analyzeWithAI } from "./analyzer.js";
|
|
4
|
+
export async function runScan(config) {
|
|
5
|
+
const entries = getCachedDiff(config.rules.ignorePaths);
|
|
6
|
+
if (entries.length === 0) {
|
|
7
|
+
console.log(chalk.dim("🛡️ VibeGuard: 暂存区无代码变更,跳过扫描。"));
|
|
8
|
+
return { passed: true, issues: [], scannedFiles: 0 };
|
|
9
|
+
}
|
|
10
|
+
console.log(chalk.cyan(`🛡️ VibeGuard: 发现 ${entries.length} 个变更文件,开始扫描...\n`));
|
|
11
|
+
const allIssues = [];
|
|
12
|
+
for (const entry of entries) {
|
|
13
|
+
const truncated = truncateDiff(entry, config.scan.maxDiffChars);
|
|
14
|
+
const issues = await analyzeWithAI(truncated, config);
|
|
15
|
+
allIssues.push(...issues);
|
|
16
|
+
}
|
|
17
|
+
printReport(allIssues);
|
|
18
|
+
return {
|
|
19
|
+
passed: allIssues.filter((i) => i.severity === "high").length === 0,
|
|
20
|
+
issues: allIssues,
|
|
21
|
+
scannedFiles: entries.length,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function truncateDiff(entry, maxChars) {
|
|
25
|
+
if (entry.diff.length <= maxChars)
|
|
26
|
+
return entry;
|
|
27
|
+
return {
|
|
28
|
+
filePath: entry.filePath,
|
|
29
|
+
diff: entry.diff.slice(0, maxChars) + "\n... (diff truncated)",
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function printReport(issues) {
|
|
33
|
+
if (issues.length === 0) {
|
|
34
|
+
console.log(chalk.green.bold("✅ VibeGuard: 未检测到风险,可以安全提交。"));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
console.log(chalk.yellow.bold(`\n⚠️ 检测到 ${issues.length} 个问题:\n`));
|
|
38
|
+
for (const issue of issues) {
|
|
39
|
+
const icon = issue.severity === "high"
|
|
40
|
+
? chalk.red.bold("🚨 HIGH")
|
|
41
|
+
: issue.severity === "medium"
|
|
42
|
+
? chalk.yellow("⚠️ MED ")
|
|
43
|
+
: chalk.blue("ℹ️ LOW ");
|
|
44
|
+
console.log(`${icon} ${chalk.bold(issue.file)}:${issue.line ?? "?"}`);
|
|
45
|
+
console.log(` ${issue.message}`);
|
|
46
|
+
if (issue.suggestion) {
|
|
47
|
+
console.log(chalk.dim(` 💡 建议: ${issue.suggestion}`));
|
|
48
|
+
}
|
|
49
|
+
console.log();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=audit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audit.js","sourceRoot":"","sources":["../../src/core/audit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface VibeguardConfig {
|
|
2
|
+
model: {
|
|
3
|
+
provider: "openai" | "ollama";
|
|
4
|
+
openai: {
|
|
5
|
+
model: string;
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
};
|
|
9
|
+
ollama: {
|
|
10
|
+
model: string;
|
|
11
|
+
baseUrl: string;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
scan: {
|
|
15
|
+
maxDiffChars: number;
|
|
16
|
+
hallucinationDetection: boolean;
|
|
17
|
+
securityScan: boolean;
|
|
18
|
+
timeout: number;
|
|
19
|
+
};
|
|
20
|
+
rules: {
|
|
21
|
+
bannedImports: string[];
|
|
22
|
+
bannedPatterns: string[];
|
|
23
|
+
ignorePaths: string[];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export declare function loadConfig(): Promise<VibeguardConfig>;
|
|
27
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE;QACL,QAAQ,EAAE,QAAQ,GAAG,QAAQ,CAAC;QAC9B,MAAM,EAAE;YACN,KAAK,EAAE,MAAM,CAAC;YACd,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,OAAO,CAAC,EAAE,MAAM,CAAC;SAClB,CAAC;QACF,MAAM,EAAE;YACN,KAAK,EAAE,MAAM,CAAC;YACd,OAAO,EAAE,MAAM,CAAC;SACjB,CAAC;KACH,CAAC;IACF,IAAI,EAAE;QACJ,YAAY,EAAE,MAAM,CAAC;QACrB,sBAAsB,EAAE,OAAO,CAAC;QAChC,YAAY,EAAE,OAAO,CAAC;QACtB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,KAAK,EAAE;QACL,aAAa,EAAE,MAAM,EAAE,CAAC;QACxB,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,WAAW,EAAE,MAAM,EAAE,CAAC;KACvB,CAAC;CACH;AA2BD,wBAAsB,UAAU,IAAI,OAAO,CAAC,eAAe,CAAC,CAoB3D"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from "fs";
|
|
2
|
+
import { join } from "path";
|
|
3
|
+
import yaml from "js-yaml";
|
|
4
|
+
const DEFAULT_CONFIG = {
|
|
5
|
+
model: {
|
|
6
|
+
provider: "ollama",
|
|
7
|
+
openai: {
|
|
8
|
+
model: "gpt-4o-mini",
|
|
9
|
+
// 默认使用 OpenAI 官方 API,不设置 baseUrl
|
|
10
|
+
},
|
|
11
|
+
ollama: {
|
|
12
|
+
model: "llama3",
|
|
13
|
+
baseUrl: "http://localhost:11434",
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
scan: {
|
|
17
|
+
maxDiffChars: 12000,
|
|
18
|
+
hallucinationDetection: true,
|
|
19
|
+
securityScan: true,
|
|
20
|
+
timeout: 30,
|
|
21
|
+
},
|
|
22
|
+
rules: {
|
|
23
|
+
bannedImports: [],
|
|
24
|
+
bannedPatterns: [],
|
|
25
|
+
ignorePaths: ["dist/**", "node_modules/**"],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
export async function loadConfig() {
|
|
29
|
+
const configPath = join(process.cwd(), ".vibeguard.yaml");
|
|
30
|
+
if (!existsSync(configPath)) {
|
|
31
|
+
return DEFAULT_CONFIG;
|
|
32
|
+
}
|
|
33
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
34
|
+
const userConfig = yaml.load(raw);
|
|
35
|
+
return {
|
|
36
|
+
model: {
|
|
37
|
+
...DEFAULT_CONFIG.model,
|
|
38
|
+
...userConfig?.model,
|
|
39
|
+
openai: { ...DEFAULT_CONFIG.model.openai, ...userConfig?.model?.openai },
|
|
40
|
+
ollama: { ...DEFAULT_CONFIG.model.ollama, ...userConfig?.model?.ollama },
|
|
41
|
+
},
|
|
42
|
+
scan: { ...DEFAULT_CONFIG.scan, ...userConfig?.scan },
|
|
43
|
+
rules: { ...DEFAULT_CONFIG.rules, ...userConfig?.rules },
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,IAAI,MAAM,SAAS,CAAC;AA4B3B,MAAM,cAAc,GAAoB;IACtC,KAAK,EAAE;QACL,QAAQ,EAAE,QAAQ;QAClB,MAAM,EAAE;YACN,KAAK,EAAE,aAAa;YACpB,iCAAiC;SAClC;QACD,MAAM,EAAE;YACN,KAAK,EAAE,QAAQ;YACf,OAAO,EAAE,wBAAwB;SAClC;KACF;IACD,IAAI,EAAE;QACJ,YAAY,EAAE,KAAK;QACnB,sBAAsB,EAAE,IAAI;QAC5B,YAAY,EAAE,IAAI;QAClB,OAAO,EAAE,EAAE;KACZ;IACD,KAAK,EAAE;QACL,aAAa,EAAE,EAAE;QACjB,cAAc,EAAE,EAAE;QAClB,WAAW,EAAE,CAAC,SAAS,EAAE,iBAAiB,CAAC;KAC5C;CACF,CAAC;AAEF,MAAM,CAAC,KAAK,UAAU,UAAU;IAC9B,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,iBAAiB,CAAC,CAAC;IAE1D,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC5B,OAAO,cAAc,CAAC;IACxB,CAAC;IAED,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC9C,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAA6B,CAAC;IAE9D,OAAO;QACL,KAAK,EAAE;YACL,GAAG,cAAc,CAAC,KAAK;YACvB,GAAG,UAAU,EAAE,KAAK;YACpB,MAAM,EAAE,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE;YACxE,MAAM,EAAE,EAAE,GAAG,cAAc,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE;SACzE;QACD,IAAI,EAAE,EAAE,GAAG,cAAc,CAAC,IAAI,EAAE,GAAG,UAAU,EAAE,IAAI,EAAE;QACrD,KAAK,EAAE,EAAE,GAAG,cAAc,CAAC,KAAK,EAAE,GAAG,UAAU,EAAE,KAAK,EAAE;KACzD,CAAC;AACJ,CAAC"}
|