@fengyunxuezhang/tiez 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/README.md +77 -0
- package/bin/tiez.js +50 -0
- package/dist/api/auth-routes.d.ts +3 -0
- package/dist/api/auth-routes.d.ts.map +1 -0
- package/dist/api/auth-routes.js +29 -0
- package/dist/api/auth-routes.js.map +1 -0
- package/dist/api/billing-routes.d.ts +3 -0
- package/dist/api/billing-routes.d.ts.map +1 -0
- package/dist/api/billing-routes.js +20 -0
- package/dist/api/billing-routes.js.map +1 -0
- package/dist/api/chat.d.ts +3 -0
- package/dist/api/chat.d.ts.map +1 -0
- package/dist/api/chat.js +70 -0
- package/dist/api/chat.js.map +1 -0
- package/dist/api/models.d.ts +3 -0
- package/dist/api/models.d.ts.map +1 -0
- package/dist/api/models.js +11 -0
- package/dist/api/models.js.map +1 -0
- package/dist/api/server.d.ts +2 -0
- package/dist/api/server.d.ts.map +1 -0
- package/dist/api/server.js +88 -0
- package/dist/api/server.js.map +1 -0
- package/dist/cli/commands.d.ts +19 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +66 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +145 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +24 -0
- package/dist/config.js.map +1 -0
- package/dist/db/index.d.ts +4 -0
- package/dist/db/index.d.ts.map +1 -0
- package/dist/db/index.js +80 -0
- package/dist/db/index.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +3 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +32 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/services/billing.d.ts +11 -0
- package/dist/services/billing.d.ts.map +1 -0
- package/dist/services/billing.js +45 -0
- package/dist/services/billing.js.map +1 -0
- package/dist/services/llm.d.ts +11 -0
- package/dist/services/llm.d.ts.map +1 -0
- package/dist/services/llm.js +29 -0
- package/dist/services/llm.js.map +1 -0
- package/dist/services/model-router.d.ts +7 -0
- package/dist/services/model-router.d.ts.map +1 -0
- package/dist/services/model-router.js +39 -0
- package/dist/services/model-router.js.map +1 -0
- package/dist/services/user.d.ts +44 -0
- package/dist/services/user.d.ts.map +1 -0
- package/dist/services/user.js +73 -0
- package/dist/services/user.js.map +1 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# 铁子 (Tiez) — 嵌入式开发 AI 助手
|
|
2
|
+
|
|
3
|
+
**一行命令安装,零依赖:**
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g tiez
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
或者直接从源码安装:
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
git clone https://gitee.com/fengyun-senior/tiez.git
|
|
13
|
+
cd tiez/03_source_node
|
|
14
|
+
npm install
|
|
15
|
+
npm link # 全局安装 tiez 命令
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 快速开始
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# 1. 初始化数据库
|
|
22
|
+
tiez init
|
|
23
|
+
|
|
24
|
+
# 2. 创建管理员
|
|
25
|
+
tiez admin admin@example.com 123456
|
|
26
|
+
|
|
27
|
+
# 3. 启动服务
|
|
28
|
+
DEEPSEEK_API_KEY="sk-your-deepseek-key" tiez
|
|
29
|
+
|
|
30
|
+
# 4. 浏览器打开管理后台
|
|
31
|
+
# http://localhost:8000/web/?api_key=刚才输出的Key
|
|
32
|
+
|
|
33
|
+
# 5. 新窗口打开交互式终端
|
|
34
|
+
# export TIEZ_API_KEY="刚才的Key"
|
|
35
|
+
# tiez chat
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 用法
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
tiez 启动 API 服务
|
|
42
|
+
tiez chat 启动交互式 CLI 终端
|
|
43
|
+
tiez init 初始化数据库
|
|
44
|
+
tiez admin <email> <password> 创建管理员
|
|
45
|
+
tiez --help 帮助
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## CLI 终端命令
|
|
49
|
+
|
|
50
|
+
| 命令 | 说明 | 示例 |
|
|
51
|
+
|------|------|------|
|
|
52
|
+
| `/chip <型号>` | 设置芯片 | `/chip stm32f103` |
|
|
53
|
+
| `/mode <模式>` | 切换模式 | `/mode deep` |
|
|
54
|
+
| `/load <文件>` | 加载代码 | `/load main.c` |
|
|
55
|
+
| `/save <文件>` | 保存回复 | `/save driver.c` |
|
|
56
|
+
| `/clear` | 清空对话 | `/clear` |
|
|
57
|
+
| `/help` | 帮助 | `/help` |
|
|
58
|
+
| `/exit` | 退出 | `/exit` |
|
|
59
|
+
|
|
60
|
+
## API 调用
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
from openai import OpenAI
|
|
64
|
+
client = OpenAI(api_key="你的Key", base_url="http://localhost:8000/v1")
|
|
65
|
+
resp = client.chat.completions.create(
|
|
66
|
+
model="tiez-default",
|
|
67
|
+
messages=[{"role": "user", "content": "写一个 STM32F103 I2C 驱动"}]
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## 环境变量
|
|
72
|
+
|
|
73
|
+
| 变量 | 说明 | 默认值 |
|
|
74
|
+
|------|------|--------|
|
|
75
|
+
| `DEEPSEEK_API_KEY` | DeepSeek API Key | - |
|
|
76
|
+
| `TIEZ_API_KEY` | 铁子 API Key(CLI 用) | - |
|
|
77
|
+
| `TIEZ_PORT` | 服务端口 | 8000 |
|
package/bin/tiez.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* 铁子 (Tiez) — 嵌入式开发 AI 助手
|
|
4
|
+
*
|
|
5
|
+
* 用法:
|
|
6
|
+
* tiez 启动 API 服务
|
|
7
|
+
* tiez chat 启动交互式 CLI
|
|
8
|
+
* tiez init 初始化数据库
|
|
9
|
+
* tiez admin 创建管理员
|
|
10
|
+
* tiez --help 帮助
|
|
11
|
+
*/
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
|
|
15
|
+
const BIN = path.resolve(__dirname, '..');
|
|
16
|
+
process.env.TIEZ_ROOT = BIN;
|
|
17
|
+
|
|
18
|
+
const cmd = process.argv[2] || 'start';
|
|
19
|
+
|
|
20
|
+
if (cmd === '--help' || cmd === '-h') {
|
|
21
|
+
console.log(`
|
|
22
|
+
铁子 (Tiez) v0.1.0 — 嵌入式开发 AI 助手
|
|
23
|
+
|
|
24
|
+
用法:
|
|
25
|
+
tiez 启动 API 服务 (http://localhost:8000)
|
|
26
|
+
tiez chat 启动交互式 CLI 终端
|
|
27
|
+
tiez init 初始化数据库并创建管理员
|
|
28
|
+
|
|
29
|
+
环境变量:
|
|
30
|
+
DEEPSEEK_API_KEY DeepSeek API Key(必填)
|
|
31
|
+
TIEZ_PORT 服务端口 (默认 8000)
|
|
32
|
+
`);
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const SCRIPT = path.join(BIN, 'dist', 'index.js');
|
|
37
|
+
const CLI_SCRIPT = path.join(BIN, 'dist', 'cli', 'index.js');
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
require('fs').accessSync(SCRIPT);
|
|
41
|
+
} catch {
|
|
42
|
+
console.log('正在编译 TypeScript...');
|
|
43
|
+
execSync('npx tsc', { cwd: BIN, stdio: 'inherit' });
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (cmd === 'chat') {
|
|
47
|
+
require(CLI_SCRIPT);
|
|
48
|
+
} else {
|
|
49
|
+
require(SCRIPT);
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-routes.d.ts","sourceRoot":"","sources":["../../src/api/auth-routes.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AA0BxB,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const express_1 = require("express");
|
|
4
|
+
const user_1 = require("../services/user");
|
|
5
|
+
const router = (0, express_1.Router)();
|
|
6
|
+
const userService = new user_1.UserService();
|
|
7
|
+
router.post('/v1/api-keys', (req, res) => {
|
|
8
|
+
const user = req.user;
|
|
9
|
+
const { name = '' } = req.body;
|
|
10
|
+
const { rawKey, apiKey } = userService.createApiKey(user.id, name);
|
|
11
|
+
res.json({ id: apiKey.id, key: rawKey, name: apiKey.name, prefix: apiKey.key_prefix, created_at: apiKey.created_at });
|
|
12
|
+
});
|
|
13
|
+
router.get('/v1/api-keys', (req, res) => {
|
|
14
|
+
const user = req.user;
|
|
15
|
+
const keys = userService.getApiKeys(user.id).map(k => ({
|
|
16
|
+
id: k.id, prefix: k.key_prefix, name: k.name, status: k.status,
|
|
17
|
+
last_used_at: k.last_used_at, created_at: k.created_at,
|
|
18
|
+
}));
|
|
19
|
+
res.json({ data: keys });
|
|
20
|
+
});
|
|
21
|
+
router.delete('/v1/api-keys/:keyId', (req, res) => {
|
|
22
|
+
const user = req.user;
|
|
23
|
+
const ok = userService.revokeApiKey(req.params.keyId, user.id);
|
|
24
|
+
if (!ok)
|
|
25
|
+
return res.status(404).json({ error: 'API Key not found' });
|
|
26
|
+
res.json({ status: 'revoked' });
|
|
27
|
+
});
|
|
28
|
+
exports.default = router;
|
|
29
|
+
//# sourceMappingURL=auth-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-routes.js","sourceRoot":"","sources":["../../src/api/auth-routes.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AACpD,2CAA+C;AAG/C,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AACxB,MAAM,WAAW,GAAG,IAAI,kBAAW,EAAE,CAAC;AAEtC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAC1D,MAAM,IAAI,GAAI,GAAW,CAAC,IAAY,CAAC;IACvC,MAAM,EAAE,IAAI,GAAG,EAAE,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;IACnE,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,UAAU,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;AACxH,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACzD,MAAM,IAAI,GAAI,GAAW,CAAC,IAAY,CAAC;IACvC,MAAM,IAAI,GAAG,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrD,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC,MAAM;QAC9D,YAAY,EAAE,CAAC,CAAC,YAAY,EAAE,UAAU,EAAE,CAAC,CAAC,UAAU;KACvD,CAAC,CAAC,CAAC;IACJ,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;AAC3B,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,CAAC,qBAAqB,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACnE,MAAM,IAAI,GAAI,GAAW,CAAC,IAAY,CAAC;IACvC,MAAM,EAAE,GAAG,WAAW,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC;IAC/D,IAAI,CAAC,EAAE;QAAE,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAAC;IACrE,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;AAClC,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-routes.d.ts","sourceRoot":"","sources":["../../src/api/billing-routes.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAgBxB,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const express_1 = require("express");
|
|
4
|
+
const billing_1 = require("../services/billing");
|
|
5
|
+
const user_1 = require("../services/user");
|
|
6
|
+
const router = (0, express_1.Router)();
|
|
7
|
+
router.get('/v1/billing/balance', (req, res) => {
|
|
8
|
+
const user = req.user;
|
|
9
|
+
const billing = new billing_1.BillingService();
|
|
10
|
+
res.json(billing.getUsageStats(user));
|
|
11
|
+
});
|
|
12
|
+
router.get('/v1/billing/plans', (_req, res) => {
|
|
13
|
+
const plans = Object.entries(user_1.PLAN_QUOTAS).map(([id, v]) => ({
|
|
14
|
+
id, name: id.charAt(0).toUpperCase() + id.slice(1),
|
|
15
|
+
price_rmb: v.price, tokens_monthly: v.tokens, rpm: v.rpm, tpm: v.tpm,
|
|
16
|
+
}));
|
|
17
|
+
res.json({ data: plans });
|
|
18
|
+
});
|
|
19
|
+
exports.default = router;
|
|
20
|
+
//# sourceMappingURL=billing-routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing-routes.js","sourceRoot":"","sources":["../../src/api/billing-routes.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AACpD,iDAAqD;AACrD,2CAAqD;AAErD,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,qBAAqB,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IAChE,MAAM,IAAI,GAAI,GAAW,CAAC,IAAY,CAAC;IACvC,MAAM,OAAO,GAAG,IAAI,wBAAc,EAAE,CAAC;IACrC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC;AACxC,CAAC,CAAC,CAAC;AAEH,MAAM,CAAC,GAAG,CAAC,mBAAmB,EAAE,CAAC,IAAa,EAAE,GAAa,EAAE,EAAE;IAC/D,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,kBAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QAC1D,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAClD,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE,cAAc,EAAE,CAAC,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG;KACrE,CAAC,CAAC,CAAC;IACJ,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5B,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../src/api/chat.ts"],"names":[],"mappings":"AAOA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAuExB,eAAe,MAAM,CAAC"}
|
package/dist/api/chat.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const express_1 = require("express");
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const llm_1 = require("../services/llm");
|
|
6
|
+
const billing_1 = require("../services/billing");
|
|
7
|
+
const model_router_1 = require("../services/model-router");
|
|
8
|
+
const router = (0, express_1.Router)();
|
|
9
|
+
router.post('/v1/chat/completions', async (req, res) => {
|
|
10
|
+
const user = req.user;
|
|
11
|
+
const { model = 'tiez-default', messages = [], stream = false, max_tokens = 4096, temperature = 0.3 } = req.body;
|
|
12
|
+
const estimated = billing_1.BillingService.estimateTokens(messages);
|
|
13
|
+
const billing = new billing_1.BillingService();
|
|
14
|
+
if (!billing.preAuth(user, estimated)) {
|
|
15
|
+
return res.status(402).json({
|
|
16
|
+
error: { message: `Insufficient balance. ${user.tokens_balance} tokens remaining.`, type: 'insufficient_quota', code: 402 },
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
const resolvedModel = (0, model_router_1.resolveModel)(model, user.plan);
|
|
20
|
+
const requestId = `chatcmpl-${(0, uuid_1.v4)().slice(0, 12)}`;
|
|
21
|
+
const llm = new llm_1.LLMService();
|
|
22
|
+
// 检查 DeepSeek Key 是否有效(非占位符)
|
|
23
|
+
const hasValidKey = !!process.env.DEEPSEEK_API_KEY && process.env.DEEPSEEK_API_KEY !== 'sk-placeholder';
|
|
24
|
+
try {
|
|
25
|
+
if (stream) {
|
|
26
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
27
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
28
|
+
res.setHeader('Connection', 'keep-alive');
|
|
29
|
+
if (!hasValidKey) {
|
|
30
|
+
const mock = { id: requestId, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000),
|
|
31
|
+
model: resolvedModel, choices: [{ index: 0, delta: { content: '铁子就绪,请设置 DEEPSEEK_API_KEY 以启用真实模型。' }, finish_reason: 'stop' }] };
|
|
32
|
+
res.write(`data: ${JSON.stringify(mock)}\n\n`);
|
|
33
|
+
res.write('data: [DONE]\n\n');
|
|
34
|
+
res.end();
|
|
35
|
+
billing.settle(user, 30, resolvedModel, requestId);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const streamResp = await llm.complete(messages, resolvedModel, true, max_tokens, temperature);
|
|
39
|
+
let fullContent = '';
|
|
40
|
+
for await (const chunk of streamResp) {
|
|
41
|
+
const content = chunk.choices?.[0]?.delta?.content || '';
|
|
42
|
+
fullContent += content;
|
|
43
|
+
res.write(`data: ${JSON.stringify({
|
|
44
|
+
id: requestId, object: 'chat.completion.chunk', created: Math.floor(Date.now() / 1000),
|
|
45
|
+
model: resolvedModel, choices: [{ index: 0, delta: { content }, finish_reason: null }],
|
|
46
|
+
})}\n\n`);
|
|
47
|
+
}
|
|
48
|
+
res.write('data: [DONE]\n\n');
|
|
49
|
+
res.end();
|
|
50
|
+
billing.settle(user, Math.max(30, Math.ceil(fullContent.length * 1.5)), resolvedModel, requestId);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const response = hasValidKey
|
|
54
|
+
? await llm.complete(messages, resolvedModel, false, max_tokens, temperature)
|
|
55
|
+
: { choices: [{ message: { content: '铁子就绪,请设置 DEEPSEEK_API_KEY 以启用真实模型。' } }], usage: { prompt_tokens: 10, completion_tokens: 20, total_tokens: 30 } };
|
|
56
|
+
const usage = { prompt_tokens: response.usage?.prompt_tokens || 10, completion_tokens: response.usage?.completion_tokens || 20, total_tokens: response.usage?.total_tokens || 30 };
|
|
57
|
+
billing.settle(user, usage.total_tokens, resolvedModel, requestId);
|
|
58
|
+
res.json({
|
|
59
|
+
id: requestId, object: 'chat.completion', created: Math.floor(Date.now() / 1000),
|
|
60
|
+
model: resolvedModel,
|
|
61
|
+
choices: [{ index: 0, message: { role: 'assistant', content: response.choices?.[0]?.message?.content || '' }, finish_reason: 'stop' }],
|
|
62
|
+
usage,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
res.status(502).json({ error: { message: `LLM error: ${err.message}`, type: 'server_error', code: 502 } });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
exports.default = router;
|
|
70
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/api/chat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../../src/api/models.ts"],"names":[],"mappings":"AAIA,QAAA,MAAM,MAAM,4CAAW,CAAC;AAOxB,eAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const express_1 = require("express");
|
|
4
|
+
const model_router_1 = require("../services/model-router");
|
|
5
|
+
const router = (0, express_1.Router)();
|
|
6
|
+
router.get('/v1/models', (req, res) => {
|
|
7
|
+
const user = req.user;
|
|
8
|
+
res.json({ object: 'list', data: (0, model_router_1.getAvailableModels)(user?.plan || 'free') });
|
|
9
|
+
});
|
|
10
|
+
exports.default = router;
|
|
11
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/api/models.ts"],"names":[],"mappings":";;AAAA,qCAAoD;AACpD,2DAA8D;AAG9D,MAAM,MAAM,GAAG,IAAA,gBAAM,GAAE,CAAC;AAExB,MAAM,CAAC,GAAG,CAAC,YAAY,EAAE,CAAC,GAAY,EAAE,GAAa,EAAE,EAAE;IACvD,MAAM,IAAI,GAAI,GAAW,CAAC,IAAY,CAAC;IACvC,GAAG,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAA,iCAAkB,EAAC,IAAI,EAAE,IAAI,IAAI,MAAM,CAAC,EAAE,CAAC,CAAC;AAC/E,CAAC,CAAC,CAAC;AAEH,kBAAe,MAAM,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"AAQA,wBAAgB,SAAS,gDAoCxB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.createApp = createApp;
|
|
7
|
+
const express_1 = __importDefault(require("express"));
|
|
8
|
+
const cors_1 = __importDefault(require("cors"));
|
|
9
|
+
const auth_1 = require("../middleware/auth");
|
|
10
|
+
const chat_1 = __importDefault(require("./chat"));
|
|
11
|
+
const models_1 = __importDefault(require("./models"));
|
|
12
|
+
const auth_routes_1 = __importDefault(require("./auth-routes"));
|
|
13
|
+
const billing_routes_1 = __importDefault(require("./billing-routes"));
|
|
14
|
+
function createApp() {
|
|
15
|
+
const app = (0, express_1.default)();
|
|
16
|
+
app.use((0, cors_1.default)());
|
|
17
|
+
app.use(express_1.default.json());
|
|
18
|
+
app.use(auth_1.authMiddleware);
|
|
19
|
+
// Health
|
|
20
|
+
app.get('/health', (_req, res) => res.json({ status: 'ok', version: '0.1.0' }));
|
|
21
|
+
// API routes
|
|
22
|
+
app.use(chat_1.default);
|
|
23
|
+
app.use(models_1.default);
|
|
24
|
+
app.use(auth_routes_1.default);
|
|
25
|
+
app.use(billing_routes_1.default);
|
|
26
|
+
// Web admin (simple HTML)
|
|
27
|
+
app.get('/web/', (req, res) => {
|
|
28
|
+
const user = req.user;
|
|
29
|
+
if (!user)
|
|
30
|
+
return res.status(401).json({ error: 'Not authenticated' });
|
|
31
|
+
const stats = new (require('../services/billing').BillingService)().getUsageStats(user);
|
|
32
|
+
const keys = new (require('../services/user').UserService)().getApiKeys(user.id);
|
|
33
|
+
res.send(renderDashboard(user, stats, keys));
|
|
34
|
+
});
|
|
35
|
+
app.get('/web/keys', (req, res) => {
|
|
36
|
+
const user = req.user;
|
|
37
|
+
if (!user)
|
|
38
|
+
return res.status(401).json({ error: 'Not authenticated' });
|
|
39
|
+
const keys = new (require('../services/user').UserService)().getApiKeys(user.id);
|
|
40
|
+
res.send(renderKeys(user, keys));
|
|
41
|
+
});
|
|
42
|
+
return app;
|
|
43
|
+
}
|
|
44
|
+
function renderDashboard(user, stats, keys) {
|
|
45
|
+
const pct = stats.monthly_quota > 0 ? (stats.monthly_used / stats.monthly_quota * 100).toFixed(1) : 0;
|
|
46
|
+
return `<!DOCTYPE html>
|
|
47
|
+
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
48
|
+
<title>铁子管理后台</title><script src="https://cdn.tailwindcss.com"></script></head>
|
|
49
|
+
<body class="bg-gray-50">
|
|
50
|
+
<nav class="bg-white shadow-sm border-b"><div class="max-w-5xl mx-auto px-4 py-3 flex items-center justify-between">
|
|
51
|
+
<span class="text-lg font-bold text-gray-800">铁子</span>
|
|
52
|
+
<div class="flex items-center gap-6">
|
|
53
|
+
<a href="/web/" class="text-sm text-gray-600 hover:text-gray-900">用量看板</a>
|
|
54
|
+
<a href="/web/keys" class="text-sm text-gray-600 hover:text-gray-900">API Key</a></div>
|
|
55
|
+
<div class="text-sm text-gray-500">${user.email} · ${user.plan}</div></div></nav>
|
|
56
|
+
<main class="max-w-5xl mx-auto px-4 py-6">
|
|
57
|
+
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
|
|
58
|
+
<div class="bg-white rounded-lg shadow p-4"><div class="text-sm text-gray-500">套餐</div><div class="text-2xl font-bold">${stats.plan}</div></div>
|
|
59
|
+
<div class="bg-white rounded-lg shadow p-4"><div class="text-sm text-gray-500">已用 / 月配额</div>
|
|
60
|
+
<div class="text-2xl font-bold">${stats.monthly_used.toLocaleString()} <span class="text-sm text-gray-400">/ ${stats.monthly_quota.toLocaleString()}</span></div>
|
|
61
|
+
<div class="w-full bg-gray-200 rounded-full h-2 mt-2"><div class="bg-blue-500 h-2 rounded-full" style="width:${pct}%"></div></div></div>
|
|
62
|
+
<div class="bg-white rounded-lg shadow p-4"><div class="text-sm text-gray-500">总余额</div><div class="text-2xl font-bold">${stats.balance.toLocaleString()}</div></div></div>
|
|
63
|
+
<div class="bg-white rounded-lg shadow p-4"><h2 class="text-lg font-bold mb-3">最近请求</h2>
|
|
64
|
+
${stats.recent_requests?.length ? `<table class="w-full text-sm"><thead><tr class="text-left text-gray-500 border-b"><th class="pb-2">模型</th><th class="pb-2">Token</th><th class="pb-2">时间</th></tr></thead><tbody>
|
|
65
|
+
${stats.recent_requests.map((r) => `<tr class="border-b border-gray-100"><td class="py-2">${r.model}</td><td class="py-2">${r.tokens}</td><td class="py-2 text-gray-500">${(r.time || '').slice(0, 19)}</td></tr>`).join('')}
|
|
66
|
+
</tbody></table>` : '<p class="text-gray-400 text-sm">暂无请求记录</p>'}</div></main></body></html>`;
|
|
67
|
+
}
|
|
68
|
+
function renderKeys(user, keys) {
|
|
69
|
+
return `<!DOCTYPE html>
|
|
70
|
+
<html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
|
|
71
|
+
<title>API Key 管理 - 铁子</title><script src="https://cdn.tailwindcss.com"></script></head>
|
|
72
|
+
<body class="bg-gray-50">
|
|
73
|
+
<nav class="bg-white shadow-sm border-b"><div class="max-w-5xl mx-auto px-4 py-3 flex items-center justify-between">
|
|
74
|
+
<span class="text-lg font-bold text-gray-800">铁子</span>
|
|
75
|
+
<div class="flex items-center gap-6">
|
|
76
|
+
<a href="/web/" class="text-sm text-gray-600 hover:text-gray-900">用量看板</a>
|
|
77
|
+
<a href="/web/keys" class="text-sm text-gray-600 hover:text-gray-900">API Key</a></div>
|
|
78
|
+
<div class="text-sm text-gray-500">${user.email} · ${user.plan}</div></div></nav>
|
|
79
|
+
<main class="max-w-5xl mx-auto px-4 py-6">
|
|
80
|
+
<div class="bg-white rounded-lg shadow p-4"><h2 class="text-lg font-bold mb-4">API Key 管理</h2>
|
|
81
|
+
${keys.length ? `<table class="w-full text-sm"><thead><tr class="text-left text-gray-500 border-b"><th class="pb-2">名称</th><th class="pb-2">Key 前缀</th><th class="pb-2">状态</th><th class="pb-2">最后使用</th><th class="pb-2">创建时间</th></tr></thead><tbody>
|
|
82
|
+
${keys.map(k => `<tr class="border-b border-gray-100"><td class="py-2">${k.name || '-'}</td><td class="py-2 font-mono text-xs">${k.prefix}...</td>
|
|
83
|
+
<td class="py-2"><span class="px-2 py-0.5 rounded-full text-xs ${k.status === 'active' ? 'bg-green-100 text-green-700' : 'bg-gray-100 text-gray-500'}">${k.status}</span></td>
|
|
84
|
+
<td class="py-2 text-gray-500 text-xs">${k.last_used_at ? k.last_used_at.slice(0, 19) : '-'}</td>
|
|
85
|
+
<td class="py-2 text-gray-500 text-xs">${(k.created_at || '').slice(0, 19)}</td></tr>`).join('')}
|
|
86
|
+
</tbody></table>` : '<p class="text-gray-400 text-sm">暂无 API Key</p>'}</div></main></body></html>`;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/api/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CliState {
|
|
2
|
+
chip: string;
|
|
3
|
+
mode: string;
|
|
4
|
+
messages: {
|
|
5
|
+
role: string;
|
|
6
|
+
content: string;
|
|
7
|
+
}[];
|
|
8
|
+
loadedFiles: {
|
|
9
|
+
path: string;
|
|
10
|
+
content: string;
|
|
11
|
+
}[];
|
|
12
|
+
lastResponse: string;
|
|
13
|
+
}
|
|
14
|
+
export declare function handleCommand(text: string, state: CliState): {
|
|
15
|
+
handled: boolean;
|
|
16
|
+
message: string;
|
|
17
|
+
exit?: boolean;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACjD,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,CA8DlH"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleCommand = handleCommand;
|
|
4
|
+
function handleCommand(text, state) {
|
|
5
|
+
const parts = text.trim().split(/\s+/);
|
|
6
|
+
const cmd = parts[0].toLowerCase();
|
|
7
|
+
const arg = parts.slice(1).join(' ');
|
|
8
|
+
switch (cmd) {
|
|
9
|
+
case '/chip':
|
|
10
|
+
if (!arg)
|
|
11
|
+
return { handled: true, message: `当前芯片: ${state.chip || '(未设置)'}` };
|
|
12
|
+
state.chip = arg;
|
|
13
|
+
return { handled: true, message: `已切换到芯片: ${arg}` };
|
|
14
|
+
case '/mode': {
|
|
15
|
+
const valid = ['quick', 'deep', 'review'];
|
|
16
|
+
if (!valid.includes(arg))
|
|
17
|
+
return { handled: true, message: `当前模式: ${state.mode} (可用: quick/deep/review)` };
|
|
18
|
+
state.mode = arg;
|
|
19
|
+
return { handled: true, message: `已切换到模式: ${arg}` };
|
|
20
|
+
}
|
|
21
|
+
case '/load': {
|
|
22
|
+
if (!arg)
|
|
23
|
+
return { handled: true, message: '用法: /load <文件路径>' };
|
|
24
|
+
try {
|
|
25
|
+
const fs = require('fs');
|
|
26
|
+
const content = fs.readFileSync(arg, 'utf-8');
|
|
27
|
+
state.loadedFiles.push({ path: arg, content });
|
|
28
|
+
return { handled: true, message: `已加载: ${arg} (${content.split('\n').length} 行)` };
|
|
29
|
+
}
|
|
30
|
+
catch (e) {
|
|
31
|
+
return { handled: true, message: `加载失败: ${e.message}` };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
case '/save': {
|
|
35
|
+
if (!arg)
|
|
36
|
+
return { handled: true, message: '用法: /save <文件名>' };
|
|
37
|
+
if (!state.lastResponse)
|
|
38
|
+
return { handled: true, message: '没有可保存的内容' };
|
|
39
|
+
try {
|
|
40
|
+
require('fs').writeFileSync(arg, state.lastResponse, 'utf-8');
|
|
41
|
+
return { handled: true, message: `已保存到: ${arg}` };
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
return { handled: true, message: `保存失败: ${e.message}` };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
case '/clear':
|
|
48
|
+
state.messages = [];
|
|
49
|
+
return { handled: true, message: '对话已清空' };
|
|
50
|
+
case '/help':
|
|
51
|
+
return { handled: true, message: `
|
|
52
|
+
可用命令:
|
|
53
|
+
/chip <型号> 设置芯片型号 (如 stm32f103, esp32)
|
|
54
|
+
/mode <模式> 切换模式 (quick 快速, deep 深度, review 审查)
|
|
55
|
+
/load <文件> 加载代码文件到上下文
|
|
56
|
+
/save <文件名> 保存回复到文件
|
|
57
|
+
/clear 清空对话历史
|
|
58
|
+
/help 显示帮助
|
|
59
|
+
/exit 退出` };
|
|
60
|
+
case '/exit':
|
|
61
|
+
return { handled: true, message: '再见!', exit: true };
|
|
62
|
+
default:
|
|
63
|
+
return { handled: false, message: `未知命令: ${cmd}。输入 /help 查看可用命令。` };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"commands.js","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":""}
|