llmpt 1.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/CHANGELOG.md +32 -0
- package/LICENSE +21 -0
- package/README.md +130 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/server/api/routes.d.ts +6 -0
- package/dist/server/api/routes.d.ts.map +1 -0
- package/dist/server/api/routes.js +128 -0
- package/dist/server/api/routes.js.map +1 -0
- package/dist/server/app.d.ts +14 -0
- package/dist/server/app.d.ts.map +1 -0
- package/dist/server/app.js +75 -0
- package/dist/server/app.js.map +1 -0
- package/dist/server/config/crypto.d.ts +14 -0
- package/dist/server/config/crypto.d.ts.map +1 -0
- package/dist/server/config/crypto.js +63 -0
- package/dist/server/config/crypto.js.map +1 -0
- package/dist/server/config/loader.d.ts +12 -0
- package/dist/server/config/loader.d.ts.map +1 -0
- package/dist/server/config/loader.js +142 -0
- package/dist/server/config/loader.js.map +1 -0
- package/dist/server/providers/anthropic.d.ts +9 -0
- package/dist/server/providers/anthropic.d.ts.map +1 -0
- package/dist/server/providers/anthropic.js +29 -0
- package/dist/server/providers/anthropic.js.map +1 -0
- package/dist/server/providers/base.d.ts +14 -0
- package/dist/server/providers/base.d.ts.map +1 -0
- package/dist/server/providers/base.js +15 -0
- package/dist/server/providers/base.js.map +1 -0
- package/dist/server/providers/ollama.d.ts +9 -0
- package/dist/server/providers/ollama.d.ts.map +1 -0
- package/dist/server/providers/ollama.js +24 -0
- package/dist/server/providers/ollama.js.map +1 -0
- package/dist/server/providers/openai.d.ts +9 -0
- package/dist/server/providers/openai.d.ts.map +1 -0
- package/dist/server/providers/openai.js +32 -0
- package/dist/server/providers/openai.js.map +1 -0
- package/dist/server/providers/registry.d.ts +23 -0
- package/dist/server/providers/registry.d.ts.map +1 -0
- package/dist/server/providers/registry.js +50 -0
- package/dist/server/providers/registry.js.map +1 -0
- package/dist/server/proxy/handler.d.ts +9 -0
- package/dist/server/proxy/handler.d.ts.map +1 -0
- package/dist/server/proxy/handler.js +101 -0
- package/dist/server/proxy/handler.js.map +1 -0
- package/dist/server/proxy/stream.d.ts +9 -0
- package/dist/server/proxy/stream.d.ts.map +1 -0
- package/dist/server/proxy/stream.js +200 -0
- package/dist/server/proxy/stream.js.map +1 -0
- package/dist/server/static.d.ts +6 -0
- package/dist/server/static.d.ts.map +1 -0
- package/dist/server/static.js +58 -0
- package/dist/server/static.js.map +1 -0
- package/dist/server/trace/collector.d.ts +59 -0
- package/dist/server/trace/collector.d.ts.map +1 -0
- package/dist/server/trace/collector.js +278 -0
- package/dist/server/trace/collector.js.map +1 -0
- package/dist/server/trace/sanitizer.d.ts +5 -0
- package/dist/server/trace/sanitizer.d.ts.map +1 -0
- package/dist/server/trace/sanitizer.js +45 -0
- package/dist/server/trace/sanitizer.js.map +1 -0
- package/dist/server/trace/writer.d.ts +26 -0
- package/dist/server/trace/writer.d.ts.map +1 -0
- package/dist/server/trace/writer.js +125 -0
- package/dist/server/trace/writer.js.map +1 -0
- package/dist/server/types.d.ts +167 -0
- package/dist/server/types.d.ts.map +1 -0
- package/dist/server/types.js +5 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/ws/server.d.ts +20 -0
- package/dist/server/ws/server.d.ts.map +1 -0
- package/dist/server/ws/server.js +78 -0
- package/dist/server/ws/server.js.map +1 -0
- package/dist/web/assets/index-BleB8LKa.js +21 -0
- package/dist/web/assets/index-DAsXJzMt.css +1 -0
- package/dist/web/assets/markdown-DsSlw-Z4.js +20 -0
- package/dist/web/assets/react-vendor-BnH7jZBV.js +21 -0
- package/dist/web/assets/rolldown-runtime-QTnfLwEv.js +1 -0
- package/dist/web/index.html +18 -0
- package/lpt.config.yaml +11 -0
- package/package.json +70 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## v1.1.0 (2026-06-15)
|
|
4
|
+
|
|
5
|
+
### 新增
|
|
6
|
+
|
|
7
|
+
- **Thinking 内联展示**:响应区域的 Thinking 内容不再独立成一个顶层 Section,改为嵌在 Response Section 内部顶部,以折叠块形式展示(默认收起),与 messages 历史中的 ThinkingBlock 风格一致
|
|
8
|
+
- **Thinking Markdown 渲染**:Thinking 折叠块展开后支持「原文 / 渲染」切换,渲染模式走 react-markdown,修复了因父容器 `.content-block` 的 `white-space: pre-wrap` 继承导致空行堆积的问题
|
|
9
|
+
- **Thinking 复制按钮**:hover 到 Thinking 标题行时出现复制按钮,固定在行尾右侧
|
|
10
|
+
- **自定义 ConfirmDialog**:替换浏览器原生 `window.confirm`,所有删除确认操作改为居中精美弹窗,支持 Esc 取消、Enter 确认、毛玻璃遮罩、弹出动画
|
|
11
|
+
|
|
12
|
+
### 优化
|
|
13
|
+
|
|
14
|
+
- Thinking 标题行三角符号与文字间距加宽
|
|
15
|
+
- Thinking 展开时 toggle 按钮(原文/渲染)与标题同行,不再另起一行
|
|
16
|
+
- 去掉 Thinking 标题行的行数统计(`N 行`)信息,界面更简洁
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## v1.0.0 (2026-06-14)
|
|
21
|
+
|
|
22
|
+
初始发布。
|
|
23
|
+
|
|
24
|
+
- 单进程单端口代理服务(默认 19900)
|
|
25
|
+
- 支持 OpenAI / Anthropic / Ollama 三类 Provider
|
|
26
|
+
- 实时 WebSocket 推送,Trace 列表自动更新
|
|
27
|
+
- Trace 详情:System Prompt、Messages、Response、Tool Calls、Raw JSON
|
|
28
|
+
- Markdown 渲染(react-markdown + highlight.js 代码高亮)
|
|
29
|
+
- Token 用量栏(prompt / completion / reasoning / cache)
|
|
30
|
+
- 深色 / 浅色主题切换
|
|
31
|
+
- Trace JSONL 持久化,重启可恢复历史
|
|
32
|
+
- AES-256-GCM API Key 加密存储
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zhxsking
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# LPT — LLM Proxy Trace
|
|
2
|
+
|
|
3
|
+
> 本地 LLM 代理工具,零侵入地拦截并可视化所有 OpenAI / Anthropic / Ollama API 调用
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
你的应用 ──► http://localhost:19900 ──► 真实 LLM Provider
|
|
7
|
+
│
|
|
8
|
+
▼
|
|
9
|
+
Web Dashboard(实时 Trace 监控)
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## 功能特性
|
|
15
|
+
|
|
16
|
+
- **零侵入代理** — 只需把 `base_url` 改为 `http://localhost:19900`,无需修改任何应用代码
|
|
17
|
+
- **实时流式追踪** — WebSocket 推送,实时响应
|
|
18
|
+
- **完整 Trace 记录** — 请求体、响应体、思考链(thinking)、Tool Call、Token 用量全记录
|
|
19
|
+
- **Markdown 渲染** — 响应内容支持原文 / Markdown 双视图,代码块语法高亮
|
|
20
|
+
- **Token 用量统计** — 输入/输出/推理/缓存命中 Token 分项展示,实时速度统计
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## 快速开始
|
|
25
|
+
|
|
26
|
+
### 方式一:npx 一键启动(推荐)
|
|
27
|
+
|
|
28
|
+
无需安装,直接运行:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npx llmpt
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
首次运行会在当前目录生成 `lpt.config.yaml` 配置文件,按提示填入 API Key 后即可使用。
|
|
35
|
+
|
|
36
|
+
### 方式二:全局安装
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install -g llmpt
|
|
40
|
+
llmpt
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### 方式三:从源码运行
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
git clone https://github.com/zhxsking/llm_proxy_trace.git
|
|
47
|
+
cd llm_proxy_trace
|
|
48
|
+
npm install
|
|
49
|
+
npm run build
|
|
50
|
+
npm start
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
启动后浏览器打开 `http://localhost:19900`。
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 配置
|
|
58
|
+
|
|
59
|
+
启动后编辑当前目录的 `lpt.config.yaml`:
|
|
60
|
+
|
|
61
|
+
```yaml
|
|
62
|
+
proxy:
|
|
63
|
+
port: 19900 # 代理端口
|
|
64
|
+
|
|
65
|
+
providers:
|
|
66
|
+
openai:
|
|
67
|
+
apiKey: "sk-..." # OpenAI API Key
|
|
68
|
+
baseUrl: "https://api.openai.com"
|
|
69
|
+
|
|
70
|
+
anthropic:
|
|
71
|
+
apiKey: "sk-ant-..." # Anthropic API Key
|
|
72
|
+
|
|
73
|
+
ollama:
|
|
74
|
+
baseUrl: "http://localhost:11434"
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
修改配置后重启服务生效。
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## 接入客户端
|
|
82
|
+
|
|
83
|
+
将 LLM SDK 的 `base_url` 指向本地代理,其余配置保持不变:
|
|
84
|
+
|
|
85
|
+
**Python(OpenAI SDK)**
|
|
86
|
+
```python
|
|
87
|
+
from openai import OpenAI
|
|
88
|
+
client = OpenAI(
|
|
89
|
+
base_url="http://localhost:19900/v1",
|
|
90
|
+
api_key="any", # 代理会替换为配置文件中的真实 key
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**环境变量(适用于大多数客户端)**
|
|
95
|
+
```bash
|
|
96
|
+
OPENAI_BASE_URL=http://localhost:19900/v1
|
|
97
|
+
OPENAI_API_KEY=any
|
|
98
|
+
|
|
99
|
+
# Anthropic
|
|
100
|
+
ANTHROPIC_BASE_URL=http://localhost:19900
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 技术栈
|
|
106
|
+
|
|
107
|
+
| 层 | 技术 |
|
|
108
|
+
|----|------|
|
|
109
|
+
| 运行时 | Node.js 18+ |
|
|
110
|
+
| 语言 | TypeScript 5.x |
|
|
111
|
+
| 后端框架 | [Hono](https://hono.dev) |
|
|
112
|
+
| 前端框架 | React 18 + Vite 6 |
|
|
113
|
+
| 样式 | CSS Variables + Tailwind CSS |
|
|
114
|
+
| 字体 | 系统默认字体栈 |
|
|
115
|
+
| 实时通信 | WebSocket (`ws`) |
|
|
116
|
+
| Markdown | react-markdown + remark-gfm + highlight.js |
|
|
117
|
+
| 配置 | js-yaml |
|
|
118
|
+
| 加密 | Node.js crypto(AES-256-GCM) |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## 致谢
|
|
123
|
+
|
|
124
|
+
界面设计和 Trace 展示风格参考了 [claude-tap](https://github.com/liaohch3/claude-tap) 项目。
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## License
|
|
129
|
+
|
|
130
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ============================================================
|
|
3
|
+
// LPT CLI Entry Point - Unified server (proxy + dashboard + WS)
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { createApp } from './server/app.js';
|
|
6
|
+
import { loadConfig } from './server/config/loader.js';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
async function main() {
|
|
9
|
+
console.log('🚀 LPT (LLM Proxy Trace) 启动中...');
|
|
10
|
+
// Load configuration — pass explicit path so saveConfig writes back to the same file
|
|
11
|
+
const configPath = path.resolve(process.cwd(), 'lpt.config.yaml');
|
|
12
|
+
const config = loadConfig(configPath);
|
|
13
|
+
console.log(`📋 配置加载成功:端口 ${config.proxy.port}`);
|
|
14
|
+
// Create application
|
|
15
|
+
const { app, registry, collector, ws } = createApp(config, configPath);
|
|
16
|
+
// Create HTTP server with Hono handler
|
|
17
|
+
const { serve } = await import('@hono/node-server');
|
|
18
|
+
const server = serve({
|
|
19
|
+
fetch: app.fetch,
|
|
20
|
+
port: config.proxy.port,
|
|
21
|
+
});
|
|
22
|
+
// Attach WebSocket to the same HTTP server
|
|
23
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
|
+
ws.attachToServer(server, '/ws');
|
|
25
|
+
console.log(`\n🌐 服务运行中:http://localhost:${config.proxy.port}`);
|
|
26
|
+
console.log(`🔌 WebSocket:ws://localhost:${config.proxy.port}/ws`);
|
|
27
|
+
console.log(`📁 Trace 目录:${config.trace.dir}`);
|
|
28
|
+
console.log('\n 代理路由:');
|
|
29
|
+
console.log(' POST /v1/chat/completions (OpenAI)');
|
|
30
|
+
console.log(' POST /v1/completions (OpenAI)');
|
|
31
|
+
console.log(' GET /v1/models');
|
|
32
|
+
console.log(' POST /v1/messages (Anthropic)');
|
|
33
|
+
console.log(' POST /api/chat (Ollama)');
|
|
34
|
+
console.log(' 管理 API:');
|
|
35
|
+
console.log(' GET /api/traces');
|
|
36
|
+
console.log(' GET /api/traces/:id');
|
|
37
|
+
console.log(' GET /api/providers');
|
|
38
|
+
console.log(' PUT /api/providers/:type');
|
|
39
|
+
console.log(' GET /api/stats');
|
|
40
|
+
console.log('\n ⌨️ Ctrl+C 停止\n');
|
|
41
|
+
// Auto-open dashboard
|
|
42
|
+
if (config.dashboard.autoOpen) {
|
|
43
|
+
const url = `http://localhost:${config.proxy.port}`;
|
|
44
|
+
setTimeout(async () => {
|
|
45
|
+
try {
|
|
46
|
+
const { exec } = await import('node:child_process');
|
|
47
|
+
const cmd = process.platform === 'win32' ? `start ${url}` :
|
|
48
|
+
process.platform === 'darwin' ? `open ${url}` :
|
|
49
|
+
`xdg-open ${url}`;
|
|
50
|
+
exec(cmd);
|
|
51
|
+
}
|
|
52
|
+
catch { /* ignore */ }
|
|
53
|
+
}, 1500);
|
|
54
|
+
}
|
|
55
|
+
// Graceful shutdown
|
|
56
|
+
const shutdown = () => {
|
|
57
|
+
console.log('\n🛑 正在关闭服务...');
|
|
58
|
+
registry.stopRefreshTimers();
|
|
59
|
+
ws.stop();
|
|
60
|
+
server.close();
|
|
61
|
+
console.log('👋 已退出');
|
|
62
|
+
process.exit(0);
|
|
63
|
+
};
|
|
64
|
+
process.on('SIGINT', shutdown);
|
|
65
|
+
process.on('SIGTERM', shutdown);
|
|
66
|
+
}
|
|
67
|
+
main().catch((err) => {
|
|
68
|
+
console.error('启动失败:', err);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|
|
71
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { TraceCollector } from '../trace/collector.js';
|
|
3
|
+
import type { ProviderRegistry } from '../providers/registry.js';
|
|
4
|
+
import type { AppConfig } from '../types.js';
|
|
5
|
+
export declare function createApiRoutes(collector: TraceCollector, registry: ProviderRegistry, config: AppConfig, configPath?: string): Hono;
|
|
6
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/server/api/routes.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAgC,MAAM,aAAa,CAAC;AAI3E,wBAAgB,eAAe,CAC7B,SAAS,EAAE,cAAc,EACzB,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,SAAS,EACjB,UAAU,CAAC,EAAE,MAAM,GAClB,IAAI,CA0IN"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Management API Routes
|
|
3
|
+
// ============================================================
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { maskApiKey } from '../config/crypto.js';
|
|
7
|
+
import { saveConfig } from '../config/loader.js';
|
|
8
|
+
export function createApiRoutes(collector, registry, config, configPath) {
|
|
9
|
+
const api = new Hono();
|
|
10
|
+
// --- Trace APIs ---
|
|
11
|
+
api.get('/traces', (c) => {
|
|
12
|
+
const model = c.req.query('model');
|
|
13
|
+
const provider = c.req.query('provider');
|
|
14
|
+
const status = c.req.query('status');
|
|
15
|
+
const keyword = c.req.query('keyword');
|
|
16
|
+
const limit = parseInt(c.req.query('limit') || '50');
|
|
17
|
+
const offset = parseInt(c.req.query('offset') || '0');
|
|
18
|
+
const result = collector.search({ model, provider, status, keyword, limit, offset });
|
|
19
|
+
return c.json({
|
|
20
|
+
traces: result.traces,
|
|
21
|
+
total: result.total,
|
|
22
|
+
page: Math.floor(offset / limit) + 1,
|
|
23
|
+
pageSize: limit,
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
// POST /api/traces/reload → 静态路由必须在 /:id 动态路由之前
|
|
27
|
+
api.post('/traces/reload', (c) => {
|
|
28
|
+
const count = collector.loadFromDisk();
|
|
29
|
+
return c.json({ ok: true, loaded: count });
|
|
30
|
+
});
|
|
31
|
+
// GET /api/traces/files → 获取所有 JSONL 文件名列表
|
|
32
|
+
api.get('/traces/files', (c) => {
|
|
33
|
+
const files = collector.getWriter().getTraceFiles().map(f => path.basename(f));
|
|
34
|
+
return c.json({ files });
|
|
35
|
+
});
|
|
36
|
+
// DELETE /api/traces/file/:name → 删除指定文件及其内存记录
|
|
37
|
+
api.delete('/traces/file/:name', (c) => {
|
|
38
|
+
const name = c.req.param('name');
|
|
39
|
+
const ok = collector.deleteFile(name);
|
|
40
|
+
return c.json({ ok });
|
|
41
|
+
});
|
|
42
|
+
api.get('/traces/:id', (c) => {
|
|
43
|
+
const id = c.req.param('id');
|
|
44
|
+
const trace = collector.get(id);
|
|
45
|
+
if (!trace)
|
|
46
|
+
return c.json({ error: 'Trace not found' }, 404);
|
|
47
|
+
return c.json(trace);
|
|
48
|
+
});
|
|
49
|
+
api.delete('/traces', (c) => {
|
|
50
|
+
const permanent = c.req.query('files') === 'true';
|
|
51
|
+
collector.clear();
|
|
52
|
+
if (permanent) {
|
|
53
|
+
collector.getWriter().deleteAllFiles();
|
|
54
|
+
}
|
|
55
|
+
return c.json({ ok: true, permanent });
|
|
56
|
+
});
|
|
57
|
+
// --- Provider APIs ---
|
|
58
|
+
// GET /api/providers → 返回当前已配置的 provider 列表
|
|
59
|
+
api.get('/providers', (c) => {
|
|
60
|
+
const providers = registry.getAll().map((p) => ({
|
|
61
|
+
type: p.type,
|
|
62
|
+
baseUrl: p.config.baseUrl,
|
|
63
|
+
apiKey: p.config.apiKey ? maskApiKey(p.config.apiKey) : undefined,
|
|
64
|
+
}));
|
|
65
|
+
return c.json(providers);
|
|
66
|
+
});
|
|
67
|
+
// PUT /api/providers/:type → 更新某个 type 的配置
|
|
68
|
+
api.put('/providers/:type', async (c) => {
|
|
69
|
+
const type = c.req.param('type');
|
|
70
|
+
if (!['openai', 'anthropic', 'ollama'].includes(type)) {
|
|
71
|
+
return c.json({ error: 'Invalid provider type' }, 400);
|
|
72
|
+
}
|
|
73
|
+
const body = await c.req.json();
|
|
74
|
+
const existing = config.providers[type] || {};
|
|
75
|
+
const updated = {
|
|
76
|
+
baseUrl: body.baseUrl ?? existing.baseUrl ?? '',
|
|
77
|
+
apiKey: body.apiKey ?? existing.apiKey,
|
|
78
|
+
headers: body.headers ?? existing.headers,
|
|
79
|
+
};
|
|
80
|
+
config.providers[type] = updated;
|
|
81
|
+
registry.registerAll(config.providers);
|
|
82
|
+
saveConfig(config, configPath);
|
|
83
|
+
return c.json({ ok: true });
|
|
84
|
+
});
|
|
85
|
+
// DELETE /api/providers/:type → 移除某个 type
|
|
86
|
+
api.delete('/providers/:type', (c) => {
|
|
87
|
+
const type = c.req.param('type');
|
|
88
|
+
delete config.providers[type];
|
|
89
|
+
registry.registerAll(config.providers);
|
|
90
|
+
saveConfig(config, configPath);
|
|
91
|
+
return c.json({ ok: true });
|
|
92
|
+
});
|
|
93
|
+
// --- Config APIs ---
|
|
94
|
+
api.get('/config', (c) => {
|
|
95
|
+
return c.json({
|
|
96
|
+
proxy: config.proxy,
|
|
97
|
+
trace: { dir: config.trace.dir, maxFileSize: config.trace.maxFileSize },
|
|
98
|
+
dashboard: config.dashboard,
|
|
99
|
+
providerCount: Object.keys(config.providers).length,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// --- Stats APIs ---
|
|
103
|
+
api.get('/stats', (c) => {
|
|
104
|
+
const allTraces = collector.getAll();
|
|
105
|
+
const completed = allTraces.filter((t) => t.status === 'completed');
|
|
106
|
+
const totalCalls = allTraces.length;
|
|
107
|
+
const totalTokens = completed.reduce((sum, t) => sum + (t.usage?.totalTokens || 0), 0);
|
|
108
|
+
const avgDuration = completed.length > 0
|
|
109
|
+
? completed.reduce((sum, t) => sum + t.duration, 0) / completed.length
|
|
110
|
+
: 0;
|
|
111
|
+
const callsByModel = {};
|
|
112
|
+
const callsByProvider = {};
|
|
113
|
+
for (const t of allTraces) {
|
|
114
|
+
callsByModel[t.model] = (callsByModel[t.model] || 0) + 1;
|
|
115
|
+
callsByProvider[t.provider] = (callsByProvider[t.provider] || 0) + 1;
|
|
116
|
+
}
|
|
117
|
+
return c.json({
|
|
118
|
+
totalCalls,
|
|
119
|
+
totalTokens,
|
|
120
|
+
avgDuration: Math.round(avgDuration),
|
|
121
|
+
callsByModel,
|
|
122
|
+
callsByProvider,
|
|
123
|
+
recentCalls: allTraces.slice(0, 10),
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
return api;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../../src/server/api/routes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Hono } from 'hono';
|
|
2
|
+
import type { AppConfig } from './types.js';
|
|
3
|
+
import { ProviderRegistry } from './providers/registry.js';
|
|
4
|
+
import { TraceCollector } from './trace/collector.js';
|
|
5
|
+
import { WSServer } from './ws/server.js';
|
|
6
|
+
export interface AppContext {
|
|
7
|
+
app: Hono;
|
|
8
|
+
registry: ProviderRegistry;
|
|
9
|
+
collector: TraceCollector;
|
|
10
|
+
ws: WSServer;
|
|
11
|
+
config: AppConfig;
|
|
12
|
+
}
|
|
13
|
+
export declare function createApp(config: AppConfig, configPath?: string): AppContext;
|
|
14
|
+
//# sourceMappingURL=app.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK5B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAK1C,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,IAAI,CAAC;IACV,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,SAAS,EAAE,cAAc,CAAC;IAC1B,EAAE,EAAE,QAAQ,CAAC;IACb,MAAM,EAAE,SAAS,CAAC;CACnB;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,UAAU,CA2E5E"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// Hono App - 主应用入口
|
|
3
|
+
// ============================================================
|
|
4
|
+
import { Hono } from 'hono';
|
|
5
|
+
import { cors } from 'hono/cors';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import fs from 'node:fs';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { ProviderRegistry } from './providers/registry.js';
|
|
10
|
+
import { TraceCollector } from './trace/collector.js';
|
|
11
|
+
import { WSServer } from './ws/server.js';
|
|
12
|
+
import { handleProxy } from './proxy/handler.js';
|
|
13
|
+
import { createApiRoutes } from './api/routes.js';
|
|
14
|
+
import { serveStaticFiles } from './static.js';
|
|
15
|
+
export function createApp(config, configPath) {
|
|
16
|
+
const app = new Hono();
|
|
17
|
+
// Initialize services
|
|
18
|
+
const registry = new ProviderRegistry();
|
|
19
|
+
const collector = new TraceCollector(config.trace.dir, config.trace.maxFileSize, config.trace.maxInMemory);
|
|
20
|
+
const ws = new WSServer();
|
|
21
|
+
// Register providers from config
|
|
22
|
+
registry.registerAll(config.providers);
|
|
23
|
+
// Wire up WebSocket callbacks for trace events
|
|
24
|
+
collector.setCallbacks((trace) => ws.broadcast({ type: 'trace:new', data: trace }), (update) => ws.broadcast({ type: 'trace:update', data: update }));
|
|
25
|
+
// Middleware
|
|
26
|
+
// Security: restrict CORS to localhost origins only.
|
|
27
|
+
// This prevents any web page from a remote origin from reading traces or
|
|
28
|
+
// calling management APIs (which could expose LLM conversation contents).
|
|
29
|
+
app.use('*', cors({
|
|
30
|
+
origin: (origin) => {
|
|
31
|
+
if (!origin)
|
|
32
|
+
return origin; // same-origin / non-browser requests
|
|
33
|
+
try {
|
|
34
|
+
const url = new URL(origin);
|
|
35
|
+
const host = url.hostname;
|
|
36
|
+
if (host === 'localhost' || host === '127.0.0.1' || host === '::1')
|
|
37
|
+
return origin;
|
|
38
|
+
}
|
|
39
|
+
catch { /* ignore malformed origin */ }
|
|
40
|
+
return null; // reject everything else
|
|
41
|
+
},
|
|
42
|
+
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
|
|
43
|
+
allowHeaders: ['Content-Type', 'Authorization', 'x-api-key'],
|
|
44
|
+
}));
|
|
45
|
+
// --- Management API (MUST be before proxy catch-all) ---
|
|
46
|
+
const apiRoutes = createApiRoutes(collector, registry, config, configPath);
|
|
47
|
+
app.route('/api', apiRoutes);
|
|
48
|
+
// --- Health check ---
|
|
49
|
+
app.get('/health', (c) => c.json({ status: 'ok', proxy: true }));
|
|
50
|
+
// --- Proxy Routes ---
|
|
51
|
+
// Models endpoint(返回空列表,不再远程拉取)
|
|
52
|
+
app.get('/v1/models', (c) => c.json({ object: 'list', data: [] }));
|
|
53
|
+
app.get('/models', (c) => c.json({ object: 'list', data: [] }));
|
|
54
|
+
app.get('/api/tags', (c) => c.json({ models: [] }));
|
|
55
|
+
// OpenAI-compatible completions
|
|
56
|
+
app.post('/v1/chat/completions', async (c) => handleProxy(c, registry, collector, ws));
|
|
57
|
+
app.post('/chat/completions', async (c) => handleProxy(c, registry, collector, ws));
|
|
58
|
+
app.post('/v1/completions', async (c) => handleProxy(c, registry, collector, ws));
|
|
59
|
+
app.post('/completions', async (c) => handleProxy(c, registry, collector, ws));
|
|
60
|
+
// Anthropic-compatible
|
|
61
|
+
app.post('/v1/messages', async (c) => handleProxy(c, registry, collector, ws));
|
|
62
|
+
// Ollama-compatible
|
|
63
|
+
app.post('/api/chat', async (c) => handleProxy(c, registry, collector, ws));
|
|
64
|
+
app.post('/api/generate', async (c) => handleProxy(c, registry, collector, ws));
|
|
65
|
+
// Catch-all for any remaining /v1/* paths (except /api/* management routes)
|
|
66
|
+
app.all('/v1/*', async (c) => handleProxy(c, registry, collector, ws));
|
|
67
|
+
// --- Static file serving for frontend (SPA) ---
|
|
68
|
+
// Check for dist/web directory (built frontend)
|
|
69
|
+
const webDist = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../dist/web');
|
|
70
|
+
if (fs.existsSync(webDist)) {
|
|
71
|
+
app.use('*', serveStaticFiles(webDist));
|
|
72
|
+
}
|
|
73
|
+
return { app, registry, collector, ws, config };
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=app.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"app.js","sourceRoot":"","sources":["../../src/server/app.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encrypt a plaintext string → base64 encoded ciphertext
|
|
3
|
+
* Format: base64(iv[16] + authTag[16] + ciphertext)
|
|
4
|
+
*/
|
|
5
|
+
export declare function encrypt(plaintext: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Decrypt a base64 encoded ciphertext → plaintext string
|
|
8
|
+
*/
|
|
9
|
+
export declare function decrypt(ciphertext: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Mask an API key for display: sk-1234...abcd
|
|
12
|
+
*/
|
|
13
|
+
export declare function maskApiKey(key: string): string;
|
|
14
|
+
//# sourceMappingURL=crypto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/server/config/crypto.ts"],"names":[],"mappings":"AA2BA;;;GAGG;AACH,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAYjD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAgBlD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAG9C"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// API Key Encryption / Decryption (AES-256-GCM)
|
|
3
|
+
// ============================================================
|
|
4
|
+
import crypto from 'node:crypto';
|
|
5
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
6
|
+
const IV_LENGTH = 16;
|
|
7
|
+
const TAG_LENGTH = 16;
|
|
8
|
+
// Fixed salt — unique to this application, provides domain separation.
|
|
9
|
+
// Key also incorporates machine identity so ciphertext is not portable
|
|
10
|
+
// between machines (reduces risk if config file is leaked).
|
|
11
|
+
const APP_SALT = 'lpt-proxy-trace-v1';
|
|
12
|
+
/**
|
|
13
|
+
* Derive a 32-byte key using HKDF from machine identity.
|
|
14
|
+
* Even if hostname/username both fall back to defaults, the fixed
|
|
15
|
+
* APP_SALT ensures the key is domain-separated from other apps.
|
|
16
|
+
*/
|
|
17
|
+
function deriveKey() {
|
|
18
|
+
const hostname = process.env.COMPUTERNAME || process.env.HOSTNAME || 'localhost';
|
|
19
|
+
const username = process.env.USERNAME || process.env.USER || 'default';
|
|
20
|
+
const ikm = Buffer.from(`${hostname}:${username}`, 'utf-8');
|
|
21
|
+
// HKDF-SHA256: info = app salt, length = 32 bytes
|
|
22
|
+
return crypto.hkdfSync('sha256', ikm, Buffer.alloc(0), APP_SALT, 32);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Encrypt a plaintext string → base64 encoded ciphertext
|
|
26
|
+
* Format: base64(iv[16] + authTag[16] + ciphertext)
|
|
27
|
+
*/
|
|
28
|
+
export function encrypt(plaintext) {
|
|
29
|
+
const key = deriveKey();
|
|
30
|
+
const iv = crypto.randomBytes(IV_LENGTH);
|
|
31
|
+
const cipher = crypto.createCipheriv(ALGORITHM, key, iv);
|
|
32
|
+
const encrypted = Buffer.concat([
|
|
33
|
+
cipher.update(plaintext, 'utf-8'),
|
|
34
|
+
cipher.final(),
|
|
35
|
+
]);
|
|
36
|
+
const tag = cipher.getAuthTag();
|
|
37
|
+
return Buffer.concat([iv, tag, encrypted]).toString('base64');
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Decrypt a base64 encoded ciphertext → plaintext string
|
|
41
|
+
*/
|
|
42
|
+
export function decrypt(ciphertext) {
|
|
43
|
+
const key = deriveKey();
|
|
44
|
+
const combined = Buffer.from(ciphertext, 'base64');
|
|
45
|
+
if (combined.length < IV_LENGTH + TAG_LENGTH) {
|
|
46
|
+
throw new Error('Invalid ciphertext: too short');
|
|
47
|
+
}
|
|
48
|
+
const iv = combined.subarray(0, IV_LENGTH);
|
|
49
|
+
const tag = combined.subarray(IV_LENGTH, IV_LENGTH + TAG_LENGTH);
|
|
50
|
+
const encrypted = combined.subarray(IV_LENGTH + TAG_LENGTH);
|
|
51
|
+
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv);
|
|
52
|
+
decipher.setAuthTag(tag);
|
|
53
|
+
return Buffer.concat([decipher.update(encrypted), decipher.final()]).toString('utf-8');
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Mask an API key for display: sk-1234...abcd
|
|
57
|
+
*/
|
|
58
|
+
export function maskApiKey(key) {
|
|
59
|
+
if (!key || key.length <= 8)
|
|
60
|
+
return '***';
|
|
61
|
+
return `${key.slice(0, 4)}...${key.slice(-4)}`;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=crypto.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../../src/server/config/crypto.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AppConfig } from '../types.js';
|
|
2
|
+
declare const DEFAULT_CONFIG: AppConfig;
|
|
3
|
+
/**
|
|
4
|
+
* Load config from YAML file, falling back to defaults
|
|
5
|
+
*/
|
|
6
|
+
export declare function loadConfig(configPath?: string): AppConfig;
|
|
7
|
+
/**
|
|
8
|
+
* Save config back to YAML file (encrypts API keys)
|
|
9
|
+
*/
|
|
10
|
+
export declare function saveConfig(config: AppConfig, configPath?: string): void;
|
|
11
|
+
export { DEFAULT_CONFIG };
|
|
12
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../../src/server/config/loader.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAmC,MAAM,aAAa,CAAC;AA4C9E,QAAA,MAAM,cAAc,EAAE,SAerB,CAAC;AAEF;;GAEG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAkCzD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAevE;AAmBD,OAAO,EAAE,cAAc,EAAE,CAAC"}
|