deeper-cli 1.0.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 +254 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +12067 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +415 -0
- package/dist/index.js +1599 -0
- package/dist/index.js.map +1 -0
- package/docs/superpowers/plans/2026-05-14-deepercode-implementation.md +24 -0
- package/docs/superpowers/plans/2026-05-14-deepercode-plan.md +1248 -0
- package/docs/superpowers/specs/2026-05-14-deepercode-design.md +560 -0
- package/package.json +60 -0
- package/src/cli/bootstrap.ts +69 -0
- package/src/cli/chat-repl.ts +932 -0
- package/src/cli/commands/chat.ts +39 -0
- package/src/cli/commands/chat.tsx +39 -0
- package/src/cli/commands/config.ts +133 -0
- package/src/cli/commands/mcp.ts +172 -0
- package/src/cli/commands/run.ts +147 -0
- package/src/cli/commands/skill.ts +152 -0
- package/src/cli/index.ts +184 -0
- package/src/core/bugscan.ts +145 -0
- package/src/core/config.ts +285 -0
- package/src/core/constants.ts +49 -0
- package/src/core/eventbus.ts +202 -0
- package/src/core/logger.ts +109 -0
- package/src/core/storage.ts +96 -0
- package/src/index.ts +26 -0
- package/src/mcp/ConfigLoader.ts +74 -0
- package/src/mcp/MCPClient.ts +326 -0
- package/src/mcp/ResourceAdapter.ts +58 -0
- package/src/mcp/SSETransport.ts +133 -0
- package/src/mcp/StdioTransport.ts +116 -0
- package/src/mcp/ToolAdapter.ts +71 -0
- package/src/mcp/types.ts +58 -0
- package/src/memory/xmemory.ts +275 -0
- package/src/model/DeepSeekClient.ts +292 -0
- package/src/model/MessageBuilder.ts +155 -0
- package/src/model/RetryManager.ts +82 -0
- package/src/model/StreamHandler.ts +158 -0
- package/src/model/types.ts +86 -0
- package/src/skills/SkillCreator.ts +153 -0
- package/src/skills/SkillEngine.ts +158 -0
- package/src/skills/SkillExecutor.ts +107 -0
- package/src/skills/SkillLoader.ts +182 -0
- package/src/skills/SkillRegistry.ts +73 -0
- package/src/skills/SkillTrigger.ts +82 -0
- package/src/skills/types.ts +28 -0
- package/src/tools/ToolExecutor.ts +103 -0
- package/src/tools/ToolRegistry.ts +71 -0
- package/src/tools/ToolValidator.ts +103 -0
- package/src/tools/builtin/ai/context_summarize.ts +76 -0
- package/src/tools/builtin/ai/memory_store.ts +86 -0
- package/src/tools/builtin/ai/prompt_template.ts +71 -0
- package/src/tools/builtin/ai/skill_create.ts +53 -0
- package/src/tools/builtin/ai/subagent.ts +39 -0
- package/src/tools/builtin/ai/todo_manager.ts +157 -0
- package/src/tools/builtin/ai/token_count.ts +196 -0
- package/src/tools/builtin/ai/tool_create.ts +52 -0
- package/src/tools/builtin/code/analyze_deps.ts +72 -0
- package/src/tools/builtin/code/bug_scan.ts +80 -0
- package/src/tools/builtin/code/code_metrics.ts +111 -0
- package/src/tools/builtin/code/extract_function.ts +86 -0
- package/src/tools/builtin/code/format_code.ts +57 -0
- package/src/tools/builtin/code/generate_code.ts +75 -0
- package/src/tools/builtin/code/import_organizer.ts +82 -0
- package/src/tools/builtin/code/lint_code.ts +48 -0
- package/src/tools/builtin/code/parse_ast.ts +86 -0
- package/src/tools/builtin/code/refactor_code.ts +63 -0
- package/src/tools/builtin/code/type_check.ts +48 -0
- package/src/tools/builtin/data/chart_generate.ts +62 -0
- package/src/tools/builtin/data/csv_parse.ts +56 -0
- package/src/tools/builtin/data/data_diff.ts +79 -0
- package/src/tools/builtin/data/data_transform.ts +74 -0
- package/src/tools/builtin/data/data_validate.ts +75 -0
- package/src/tools/builtin/data/json_parse.ts +71 -0
- package/src/tools/builtin/data/template_render.ts +58 -0
- package/src/tools/builtin/data/toml_parse.ts +42 -0
- package/src/tools/builtin/data/xml_parse.ts +79 -0
- package/src/tools/builtin/data/yaml_parse.ts +42 -0
- package/src/tools/builtin/database/db_backup.ts +53 -0
- package/src/tools/builtin/database/db_restore.ts +51 -0
- package/src/tools/builtin/database/db_schema.ts +66 -0
- package/src/tools/builtin/database/nosql_query.ts +50 -0
- package/src/tools/builtin/database/orm_generate.ts +66 -0
- package/src/tools/builtin/database/redis_command.ts +46 -0
- package/src/tools/builtin/database/sql_migrate.ts +55 -0
- package/src/tools/builtin/database/sql_query.ts +60 -0
- package/src/tools/builtin/filesystem/batch_read.ts +56 -0
- package/src/tools/builtin/filesystem/batch_write.ts +67 -0
- package/src/tools/builtin/filesystem/copy_file.ts +36 -0
- package/src/tools/builtin/filesystem/create_dir.ts +30 -0
- package/src/tools/builtin/filesystem/delete_file.ts +30 -0
- package/src/tools/builtin/filesystem/diff_files.ts +47 -0
- package/src/tools/builtin/filesystem/edit_file.ts +47 -0
- package/src/tools/builtin/filesystem/file_info.ts +52 -0
- package/src/tools/builtin/filesystem/glob_find.ts +44 -0
- package/src/tools/builtin/filesystem/list_dir.ts +51 -0
- package/src/tools/builtin/filesystem/merge_files.ts +44 -0
- package/src/tools/builtin/filesystem/move_file.ts +37 -0
- package/src/tools/builtin/filesystem/read_file.ts +55 -0
- package/src/tools/builtin/filesystem/watch_file.ts +33 -0
- package/src/tools/builtin/filesystem/write_file.ts +45 -0
- package/src/tools/builtin/index.ts +244 -0
- package/src/tools/builtin/network/api_call.ts +79 -0
- package/src/tools/builtin/network/browser_action.ts +54 -0
- package/src/tools/builtin/network/check_url.ts +59 -0
- package/src/tools/builtin/network/download_file.ts +64 -0
- package/src/tools/builtin/network/graphql_query.ts +46 -0
- package/src/tools/builtin/network/http_request.ts +61 -0
- package/src/tools/builtin/network/parse_html.ts +101 -0
- package/src/tools/builtin/network/proxy_request.ts +53 -0
- package/src/tools/builtin/network/screenshot_page.ts +58 -0
- package/src/tools/builtin/network/web_fetch.ts +70 -0
- package/src/tools/builtin/network/web_search.ts +128 -0
- package/src/tools/builtin/network/websocket_connect.ts +70 -0
- package/src/tools/builtin/project/build_project.ts +68 -0
- package/src/tools/builtin/project/config_manage.ts +99 -0
- package/src/tools/builtin/project/coverage_report.ts +59 -0
- package/src/tools/builtin/project/docker_manage.ts +90 -0
- package/src/tools/builtin/project/env_manage.ts +88 -0
- package/src/tools/builtin/project/npm_manage.ts +71 -0
- package/src/tools/builtin/project/project_init.ts +59 -0
- package/src/tools/builtin/project/run_test.ts +74 -0
- package/src/tools/builtin/search/codebase_search.ts +76 -0
- package/src/tools/builtin/search/find_definition.ts +84 -0
- package/src/tools/builtin/search/find_references.ts +75 -0
- package/src/tools/builtin/search/fuzzy_find.ts +75 -0
- package/src/tools/builtin/search/grep_search.ts +90 -0
- package/src/tools/builtin/search/regex_find.ts +91 -0
- package/src/tools/builtin/search/search_docs.ts +51 -0
- package/src/tools/builtin/search/search_package.ts +50 -0
- package/src/tools/builtin/search/symbol_search.ts +82 -0
- package/src/tools/builtin/search/text_search.ts +63 -0
- package/src/tools/builtin/security/decrypt_file.ts +54 -0
- package/src/tools/builtin/security/encrypt_file.ts +52 -0
- package/src/tools/builtin/security/hash_generate.ts +48 -0
- package/src/tools/builtin/security/jwt_decode.ts +53 -0
- package/src/tools/builtin/security/secret_scan.ts +82 -0
- package/src/tools/builtin/security/vulnerability_check.ts +71 -0
- package/src/tools/builtin/shell/background_terminal.ts +38 -0
- package/src/tools/builtin/shell/check_status.ts +48 -0
- package/src/tools/builtin/shell/interactive_terminal.ts +31 -0
- package/src/tools/builtin/shell/kill_terminal.ts +29 -0
- package/src/tools/builtin/shell/list_terminals.ts +61 -0
- package/src/tools/builtin/shell/pipe_commands.ts +55 -0
- package/src/tools/builtin/shell/process-pool.ts +150 -0
- package/src/tools/builtin/shell/run_async.ts +73 -0
- package/src/tools/builtin/shell/run_command.ts +60 -0
- package/src/tools/builtin/shell/send_ctrl_keys.ts +43 -0
- package/src/tools/builtin/shell/send_keys.ts +36 -0
- package/src/tools/builtin/shell/send_text.ts +35 -0
- package/src/tools/builtin/shell/shell_script.ts +65 -0
- package/src/tools/builtin/shell/stop_command.ts +40 -0
- package/src/tools/builtin/shell/terminal_resize.ts +31 -0
- package/src/tools/builtin/shell/terminal_screenshot.ts +28 -0
- package/src/tools/builtin/system/log_viewer.ts +89 -0
- package/src/tools/builtin/system/notify_user.ts +55 -0
- package/src/tools/builtin/system/process_list.ts +66 -0
- package/src/tools/builtin/system/resource_monitor.ts +66 -0
- package/src/tools/builtin/system/system_info.ts +41 -0
- package/src/tools/tool-types.ts +97 -0
- package/src/ui/AgentTree.tsx +98 -0
- package/src/ui/App.tsx +46 -0
- package/src/ui/ChatView.tsx +278 -0
- package/src/ui/ConfirmDialog.tsx +68 -0
- package/src/ui/DiffView.tsx +64 -0
- package/src/ui/FilePreview.tsx +59 -0
- package/src/ui/InputBox.tsx +267 -0
- package/src/ui/MessageBubble.tsx +30 -0
- package/src/ui/Spinner.tsx +35 -0
- package/src/ui/StatusBar.tsx +41 -0
- package/src/ui/ToolCallCard.tsx +73 -0
- package/src/ui/ansi.ts +50 -0
- package/src/ui/markdown.ts +238 -0
- package/src/ui/themes/dark.ts +4 -0
- package/src/ui/themes/default.ts +25 -0
- package/src/ui/themes/light.ts +14 -0
- package/tests/unit/BuiltinTools.test.ts +129 -0
- package/tests/unit/BuiltinToolsIntegration.test.ts +111 -0
- package/tests/unit/FilesystemTools.test.ts +211 -0
- package/tests/unit/SkillLoader.test.ts +141 -0
- package/tests/unit/SkillRegistry.test.ts +113 -0
- package/tests/unit/ToolExecutor.test.ts +160 -0
- package/tests/unit/ToolRegistry.test.ts +103 -0
- package/tests/unit/ToolValidator.test.ts +137 -0
- package/tsconfig.json +28 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.ts +20 -0
|
@@ -0,0 +1,1248 @@
|
|
|
1
|
+
# DeeperCode 实施计划
|
|
2
|
+
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
|
+
|
|
5
|
+
**Goal:** 构建完整的 DeeperCode CLI 工具——基于 DeepSeek-V4-Pro 的终端 AI Agentic 编程助手,包含 105 个内置工具、Agent 系统、Skills 引擎、MCP 客户端和 Ink/React 终端 UI。
|
|
6
|
+
|
|
7
|
+
**Architecture:** 采用模块化分层架构:core(基础层)→ model/tools/mcp/skills(能力层)→ context/agent(编排层)→ ui/cli(交互层)。Agent 系统采用树形委派模式,主 Agent 可动态 fork 子 Agent。Ink+React 驱动现代化终端 REPL 界面。
|
|
8
|
+
|
|
9
|
+
**Tech Stack:** TypeScript 5.x, Node.js 20+, Ink 5, React 18, tsup, Vitest, pnpm, @modelcontextprotocol/sdk, isolated-vm, eventsource-parser, tree-sitter
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Phase 1: 项目脚手架 & 核心基础设施
|
|
14
|
+
|
|
15
|
+
### Task 1: 初始化项目结构
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- Create: `e:\deeper-code\package.json`
|
|
19
|
+
- Create: `e:\deeper-code\tsconfig.json`
|
|
20
|
+
- Create: `e:\deeper-code\tsconfig.build.json`
|
|
21
|
+
- Create: `e:\deeper-code\.eslintrc.json`
|
|
22
|
+
- Create: `e:\deeper-code\.prettierrc`
|
|
23
|
+
- Create: `e:\deeper-code\vitest.config.ts`
|
|
24
|
+
|
|
25
|
+
- [ ] **Step 1: 创建 package.json**
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"name": "deeper",
|
|
30
|
+
"version": "1.0.0",
|
|
31
|
+
"description": "DeeperCode - AI Agentic Coding CLI powered by DeepSeek V4 Pro",
|
|
32
|
+
"type": "module",
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"bin": {
|
|
35
|
+
"deeper": "./dist/cli/index.js"
|
|
36
|
+
},
|
|
37
|
+
"scripts": {
|
|
38
|
+
"dev": "tsup --watch",
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:watch": "vitest",
|
|
42
|
+
"lint": "eslint src/ --ext .ts,.tsx",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
},
|
|
45
|
+
"dependencies": {
|
|
46
|
+
"ink": "^5.0.0",
|
|
47
|
+
"ink-markdown": "^3.0.0",
|
|
48
|
+
"ink-gradient": "^3.0.0",
|
|
49
|
+
"ink-select-input": "^7.0.0",
|
|
50
|
+
"ink-text-input": "^6.0.0",
|
|
51
|
+
"ink-spinner": "^5.0.0",
|
|
52
|
+
"ink-use-stdout-dimensions": "^2.0.0",
|
|
53
|
+
"react": "^18.3.0",
|
|
54
|
+
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
55
|
+
"marked": "^14.0.0",
|
|
56
|
+
"highlight.js": "^11.10.0",
|
|
57
|
+
"eventsource-parser": "^3.0.0",
|
|
58
|
+
"tree-sitter": "^0.22.0",
|
|
59
|
+
"tree-sitter-javascript": "^0.23.0",
|
|
60
|
+
"tree-sitter-typescript": "^0.23.0",
|
|
61
|
+
"tree-sitter-python": "^0.23.0",
|
|
62
|
+
"chokidar": "^4.0.0",
|
|
63
|
+
"commander": "^13.0.0",
|
|
64
|
+
"js-yaml": "^4.1.0",
|
|
65
|
+
"diff": "^7.0.0",
|
|
66
|
+
"strip-ansi": "^7.1.0",
|
|
67
|
+
"fuse.js": "^7.0.0"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/node": "^22.0.0",
|
|
71
|
+
"@types/react": "^18.3.0",
|
|
72
|
+
"@types/diff": "^7.0.0",
|
|
73
|
+
"@types/js-yaml": "^4.0.0",
|
|
74
|
+
"typescript": "^5.7.0",
|
|
75
|
+
"tsup": "^8.3.0",
|
|
76
|
+
"vitest": "^3.0.0",
|
|
77
|
+
"eslint": "^9.0.0",
|
|
78
|
+
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
79
|
+
"@typescript-eslint/parser": "^8.0.0",
|
|
80
|
+
"prettier": "^3.4.0"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
- [ ] **Step 2: 创建 tsconfig.json**
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"compilerOptions": {
|
|
90
|
+
"target": "ES2022",
|
|
91
|
+
"module": "ESNext",
|
|
92
|
+
"moduleResolution": "bundler",
|
|
93
|
+
"lib": ["ES2022"],
|
|
94
|
+
"jsx": "react-jsx",
|
|
95
|
+
"outDir": "./dist",
|
|
96
|
+
"rootDir": "./src",
|
|
97
|
+
"strict": true,
|
|
98
|
+
"esModuleInterop": true,
|
|
99
|
+
"skipLibCheck": true,
|
|
100
|
+
"forceConsistentCasingInFileNames": true,
|
|
101
|
+
"declaration": true,
|
|
102
|
+
"declarationMap": true,
|
|
103
|
+
"sourceMap": true,
|
|
104
|
+
"resolveJsonModule": true,
|
|
105
|
+
"isolatedModules": true
|
|
106
|
+
},
|
|
107
|
+
"include": ["src/**/*"],
|
|
108
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- [ ] **Step 3: 创建 tsconfig.build.json**
|
|
113
|
+
|
|
114
|
+
```json
|
|
115
|
+
{
|
|
116
|
+
"extends": "./tsconfig.json",
|
|
117
|
+
"compilerOptions": {
|
|
118
|
+
"sourceMap": false,
|
|
119
|
+
"declarationMap": false
|
|
120
|
+
},
|
|
121
|
+
"exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.test.tsx"]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- [ ] **Step 4: 创建 .eslintrc.json**
|
|
126
|
+
|
|
127
|
+
```json
|
|
128
|
+
{
|
|
129
|
+
"root": true,
|
|
130
|
+
"parser": "@typescript-eslint/parser",
|
|
131
|
+
"plugins": ["@typescript-eslint"],
|
|
132
|
+
"extends": [
|
|
133
|
+
"eslint:recommended",
|
|
134
|
+
"plugin:@typescript-eslint/recommended"
|
|
135
|
+
],
|
|
136
|
+
"env": {
|
|
137
|
+
"node": true,
|
|
138
|
+
"es2022": true
|
|
139
|
+
},
|
|
140
|
+
"rules": {
|
|
141
|
+
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
|
|
142
|
+
"@typescript-eslint/explicit-function-return-type": "off",
|
|
143
|
+
"no-console": "warn"
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
- [ ] **Step 5: 创建 .prettierrc**
|
|
149
|
+
|
|
150
|
+
```json
|
|
151
|
+
{
|
|
152
|
+
"semi": true,
|
|
153
|
+
"singleQuote": true,
|
|
154
|
+
"tabWidth": 2,
|
|
155
|
+
"trailingComma": "all",
|
|
156
|
+
"printWidth": 100
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- [ ] **Step 6: 创建 vitest.config.ts**
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
import { defineConfig } from 'vitest/config';
|
|
164
|
+
|
|
165
|
+
export default defineConfig({
|
|
166
|
+
test: {
|
|
167
|
+
globals: true,
|
|
168
|
+
environment: 'node',
|
|
169
|
+
include: ['tests/**/*.test.ts', 'tests/**/*.test.tsx'],
|
|
170
|
+
coverage: {
|
|
171
|
+
provider: 'v8',
|
|
172
|
+
include: ['src/**/*.ts', 'src/**/*.tsx'],
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
- [ ] **Step 7: 创建 tsup.config.ts**
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { defineConfig } from 'tsup';
|
|
182
|
+
|
|
183
|
+
export default defineConfig({
|
|
184
|
+
entry: ['src/index.ts', 'src/cli/index.ts'],
|
|
185
|
+
format: ['esm'],
|
|
186
|
+
dts: true,
|
|
187
|
+
clean: true,
|
|
188
|
+
outDir: 'dist',
|
|
189
|
+
target: 'node20',
|
|
190
|
+
splitting: false,
|
|
191
|
+
sourcemap: true,
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
- [ ] **Step 8: 安装依赖**
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
cd e:\deeper-code && pnpm install
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
- [ ] **Step 9: 提交**
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
git add .
|
|
205
|
+
git commit -m "chore: initialize project scaffolding with configs"
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### Task 2: 核心常量与类型
|
|
211
|
+
|
|
212
|
+
**Files:**
|
|
213
|
+
- Create: `e:\deeper-code\src\core\constants.ts`
|
|
214
|
+
- Create: `e:\deeper-code\src\core\types.ts`
|
|
215
|
+
|
|
216
|
+
- [ ] **Step 1: 编写 constants.ts**
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
export const DEEPER_VERSION = '1.0.0';
|
|
220
|
+
|
|
221
|
+
export const DEFAULT_MODEL = 'deepseek-v4-pro';
|
|
222
|
+
export const DEFAULT_BASE_URL = 'https://api.deepseek.com';
|
|
223
|
+
export const DEFAULT_TEMPERATURE = 0.7;
|
|
224
|
+
export const DEFAULT_MAX_TOKENS = 100000;
|
|
225
|
+
export const DEFAULT_THINK_BUDGET = 32000;
|
|
226
|
+
|
|
227
|
+
export const DEFAULT_MAX_SUB_AGENTS = 5;
|
|
228
|
+
export const DEFAULT_MAX_RECURSION_DEPTH = 2;
|
|
229
|
+
export const DEFAULT_SUB_AGENT_TIMEOUT_MS = 120_000;
|
|
230
|
+
export const DEFAULT_AUTO_SUMMARIZE_THRESHOLD = 80_000;
|
|
231
|
+
export const DEFAULT_RESULT_SUMMARY_MAX_CHARS = 5_000;
|
|
232
|
+
|
|
233
|
+
export const CONFIG_DIR_NAME = '.deeper';
|
|
234
|
+
export const USER_CONFIG_PATH = '~/.deeper/config.json';
|
|
235
|
+
export const USER_MCP_PATH = '~/.deeper/mcp.json';
|
|
236
|
+
export const USER_SKILLS_DIR = '~/.deeper/skills';
|
|
237
|
+
export const MEMORY_FILE = 'memory.json';
|
|
238
|
+
|
|
239
|
+
export const DEEPER_ENV_PREFIX = 'DEEPER_';
|
|
240
|
+
|
|
241
|
+
export const TOOL_SAFETY_LEVELS = {
|
|
242
|
+
SAFE: 'safe' as const,
|
|
243
|
+
CONFIRM: 'confirm' as const,
|
|
244
|
+
DANGEROUS: 'dangerous' as const,
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export const SAFE_TOOLS = [
|
|
248
|
+
'read_file', 'list_dir', 'grep_search', 'glob_find', 'file_info',
|
|
249
|
+
'text_search', 'fuzzy_find', 'search_package', 'search_docs',
|
|
250
|
+
'check_status', 'list_terminals', 'token_count', 'process_list',
|
|
251
|
+
'system_info', 'log_viewer',
|
|
252
|
+
] as const;
|
|
253
|
+
|
|
254
|
+
export const DANGEROUS_TOOLS = [
|
|
255
|
+
'delete_file', 'kill_terminal', 'db_backup', 'db_restore',
|
|
256
|
+
'encrypt_file', 'decrypt_file',
|
|
257
|
+
] as const;
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
- [ ] **Step 2: 编写 types.ts**
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
export type DeeperConfig = {
|
|
264
|
+
model: {
|
|
265
|
+
provider: 'deepseek';
|
|
266
|
+
model: string;
|
|
267
|
+
api_key: string;
|
|
268
|
+
base_url: string;
|
|
269
|
+
temperature: number;
|
|
270
|
+
max_tokens: number;
|
|
271
|
+
think: {
|
|
272
|
+
enabled: boolean;
|
|
273
|
+
budget: number;
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
agent: {
|
|
277
|
+
max_sub_agents: number;
|
|
278
|
+
max_recursion_depth: number;
|
|
279
|
+
sub_agent_timeout_ms: number;
|
|
280
|
+
auto_approve_tools: string[];
|
|
281
|
+
confirm_dangerous: string[];
|
|
282
|
+
max_concurrent_tools: number;
|
|
283
|
+
};
|
|
284
|
+
context: {
|
|
285
|
+
max_tokens: number;
|
|
286
|
+
history_size: number;
|
|
287
|
+
auto_summarize_threshold: number;
|
|
288
|
+
file_context_limit: number;
|
|
289
|
+
};
|
|
290
|
+
ui: {
|
|
291
|
+
theme: 'dark' | 'light';
|
|
292
|
+
show_token_count: boolean;
|
|
293
|
+
show_tool_calls: boolean;
|
|
294
|
+
syntax_highlight: boolean;
|
|
295
|
+
animation_speed: 'fast' | 'normal' | 'slow';
|
|
296
|
+
compact_mode: boolean;
|
|
297
|
+
};
|
|
298
|
+
mcp: {
|
|
299
|
+
servers: Record<string, MCPServerConfig>;
|
|
300
|
+
auto_connect: boolean;
|
|
301
|
+
connection_timeout_ms: number;
|
|
302
|
+
};
|
|
303
|
+
skills: {
|
|
304
|
+
auto_load: boolean;
|
|
305
|
+
discovery_paths: string[];
|
|
306
|
+
};
|
|
307
|
+
privacy: {
|
|
308
|
+
telemetry: boolean;
|
|
309
|
+
log_level: 'debug' | 'info' | 'warn' | 'error';
|
|
310
|
+
max_log_files: number;
|
|
311
|
+
};
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
export type MCPServerConfig = {
|
|
315
|
+
type: 'stdio' | 'sse';
|
|
316
|
+
command?: string;
|
|
317
|
+
args?: string[];
|
|
318
|
+
env?: Record<string, string>;
|
|
319
|
+
url?: string;
|
|
320
|
+
headers?: Record<string, string>;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
export type AgentState = 'CREATED' | 'THINKING' | 'EXECUTING' | 'WAITING' | 'COMPLETED' | 'FAILED' | 'CANCELLED';
|
|
324
|
+
|
|
325
|
+
export type ToolCategory =
|
|
326
|
+
| 'filesystem'
|
|
327
|
+
| 'search'
|
|
328
|
+
| 'shell'
|
|
329
|
+
| 'network'
|
|
330
|
+
| 'code'
|
|
331
|
+
| 'database'
|
|
332
|
+
| 'data'
|
|
333
|
+
| 'security'
|
|
334
|
+
| 'project'
|
|
335
|
+
| 'ai'
|
|
336
|
+
| 'system';
|
|
337
|
+
|
|
338
|
+
export type JSONSchemaProperty = {
|
|
339
|
+
type: string;
|
|
340
|
+
description: string;
|
|
341
|
+
enum?: string[];
|
|
342
|
+
default?: unknown;
|
|
343
|
+
required?: boolean;
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
export type JSONSchema = {
|
|
347
|
+
type: string;
|
|
348
|
+
properties: Record<string, JSONSchemaProperty>;
|
|
349
|
+
required: string[];
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
export type ToolResult = {
|
|
353
|
+
success: boolean;
|
|
354
|
+
output: string;
|
|
355
|
+
error?: string;
|
|
356
|
+
metadata?: Record<string, unknown>;
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
export type Tool = {
|
|
360
|
+
name: string;
|
|
361
|
+
description: string;
|
|
362
|
+
category: ToolCategory;
|
|
363
|
+
parameters: JSONSchema;
|
|
364
|
+
execute(params: Record<string, unknown>): Promise<ToolResult>;
|
|
365
|
+
dangerous?: boolean;
|
|
366
|
+
requiresApproval?: boolean;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
export type AgentMessage = {
|
|
370
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
371
|
+
content: string | null;
|
|
372
|
+
tool_calls?: Array<{
|
|
373
|
+
id: string;
|
|
374
|
+
type: 'function';
|
|
375
|
+
function: { name: string; arguments: string };
|
|
376
|
+
}>;
|
|
377
|
+
tool_call_id?: string;
|
|
378
|
+
name?: string;
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
export type SkillManifest = {
|
|
382
|
+
name: string;
|
|
383
|
+
description: string;
|
|
384
|
+
version: string;
|
|
385
|
+
author: string;
|
|
386
|
+
triggers: string[];
|
|
387
|
+
tools: string[];
|
|
388
|
+
dependencies: string[];
|
|
389
|
+
};
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
- [ ] **Step 3: 验证 TypeScript 编译**
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
cd e:\deeper-code && pnpm typecheck
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
- [ ] **Step 4: 提交**
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
git add .
|
|
402
|
+
git commit -m "feat: add core constants and types"
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
---
|
|
406
|
+
|
|
407
|
+
### Task 3: 日志系统
|
|
408
|
+
|
|
409
|
+
**Files:**
|
|
410
|
+
- Create: `e:\deeper-code\src\core\logger.ts`
|
|
411
|
+
- Create: `e:\deeper-code\tests\unit\core\logger.test.ts`
|
|
412
|
+
|
|
413
|
+
- [ ] **Step 1: 编写测试**
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
417
|
+
import { createLogger, Logger } from '../../../src/core/logger';
|
|
418
|
+
|
|
419
|
+
describe('Logger', () => {
|
|
420
|
+
let logger: Logger;
|
|
421
|
+
let consoleLogSpy: ReturnType<typeof vi.spyOn>;
|
|
422
|
+
let consoleErrorSpy: ReturnType<typeof vi.spyOn>;
|
|
423
|
+
let consoleWarnSpy: ReturnType<typeof vi.spyOn>;
|
|
424
|
+
|
|
425
|
+
beforeEach(() => {
|
|
426
|
+
consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
427
|
+
consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
428
|
+
consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
429
|
+
logger = createLogger({ level: 'debug' });
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('ควร log debug 消息เมื่อ level เป็น debug', () => {
|
|
433
|
+
logger.debug('test debug');
|
|
434
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
it('ควร log info 消息', () => {
|
|
438
|
+
logger.info('test info');
|
|
439
|
+
expect(consoleLogSpy).toHaveBeenCalled();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('ควรไม่ log debug 消息เมื่อ level เป็น error', () => {
|
|
443
|
+
const errorOnly = createLogger({ level: 'error' });
|
|
444
|
+
errorOnly.debug('should not appear');
|
|
445
|
+
expect(consoleLogSpy).not.toHaveBeenCalled();
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
- [ ] **Step 2: 运行测试确认失败**
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/logger.test.ts
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
- [ ] **Step 3: 实现 logger.ts**
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
460
|
+
|
|
461
|
+
const LOG_LEVEL_PRIORITY: Record<LogLevel, number> = {
|
|
462
|
+
debug: 0,
|
|
463
|
+
info: 1,
|
|
464
|
+
warn: 2,
|
|
465
|
+
error: 3,
|
|
466
|
+
};
|
|
467
|
+
|
|
468
|
+
export type Logger = ReturnType<typeof createLogger>;
|
|
469
|
+
|
|
470
|
+
export function createLogger(opts: { level?: LogLevel } = {}) {
|
|
471
|
+
const levelKey: LogLevel = opts.level || 'info';
|
|
472
|
+
const threshold = LOG_LEVEL_PRIORITY[levelKey];
|
|
473
|
+
|
|
474
|
+
function format(level: string, msg: string, data?: unknown): string {
|
|
475
|
+
const ts = new Date().toISOString();
|
|
476
|
+
const base = `[${ts}] [${level.toUpperCase()}] ${msg}`;
|
|
477
|
+
return data ? `${base} ${JSON.stringify(data)}` : base;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
function shouldLog(level: LogLevel): boolean {
|
|
481
|
+
return LOG_LEVEL_PRIORITY[level] >= threshold;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
return {
|
|
485
|
+
debug(msg: string, data?: unknown) {
|
|
486
|
+
if (shouldLog('debug')) console.log(format('debug', msg, data));
|
|
487
|
+
},
|
|
488
|
+
info(msg: string, data?: unknown) {
|
|
489
|
+
if (shouldLog('info')) console.log(format('info', msg, data));
|
|
490
|
+
},
|
|
491
|
+
warn(msg: string, data?: unknown) {
|
|
492
|
+
if (shouldLog('warn')) console.warn(format('warn', msg, data));
|
|
493
|
+
},
|
|
494
|
+
error(msg: string, data?: unknown) {
|
|
495
|
+
if (shouldLog('error')) console.error(format('error', msg, data));
|
|
496
|
+
},
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
- [ ] **Step 4: 运行测试确认通过**
|
|
502
|
+
|
|
503
|
+
```bash
|
|
504
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/logger.test.ts
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
- [ ] **Step 5: 提交**
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
git add .
|
|
511
|
+
git commit -m "feat: add logger system"
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
---
|
|
515
|
+
|
|
516
|
+
### Task 4: 事件总线
|
|
517
|
+
|
|
518
|
+
**Files:**
|
|
519
|
+
- Create: `e:\deeper-code\src\core\eventbus.ts`
|
|
520
|
+
- Create: `e:\deeper-code\tests\unit\core\eventbus.test.ts`
|
|
521
|
+
|
|
522
|
+
- [ ] **Step 1: 编写测试**
|
|
523
|
+
|
|
524
|
+
```typescript
|
|
525
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
526
|
+
import { EventBus } from '../../../src/core/eventbus';
|
|
527
|
+
|
|
528
|
+
describe('EventBus', () => {
|
|
529
|
+
it('ควร subscribe และ emit event ได้', () => {
|
|
530
|
+
const bus = new EventBus();
|
|
531
|
+
const handler = vi.fn();
|
|
532
|
+
bus.on('test', handler);
|
|
533
|
+
bus.emit('test', { foo: 'bar' });
|
|
534
|
+
expect(handler).toHaveBeenCalledWith({ foo: 'bar' });
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
it('ควรไม่เรียก handler หลังจาก off', () => {
|
|
538
|
+
const bus = new EventBus();
|
|
539
|
+
const handler = vi.fn();
|
|
540
|
+
bus.on('test', handler);
|
|
541
|
+
bus.off('test', handler);
|
|
542
|
+
bus.emit('test', {});
|
|
543
|
+
expect(handler).not.toHaveBeenCalled();
|
|
544
|
+
});
|
|
545
|
+
|
|
546
|
+
it('ควร unsubscribe เมื่อใช้ returned function', () => {
|
|
547
|
+
const bus = new EventBus();
|
|
548
|
+
const handler = vi.fn();
|
|
549
|
+
const unsub = bus.on('test', handler);
|
|
550
|
+
unsub();
|
|
551
|
+
bus.emit('test', {});
|
|
552
|
+
expect(handler).not.toHaveBeenCalled();
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
it('ควรรองรับ multiple handlers', () => {
|
|
556
|
+
const bus = new EventBus();
|
|
557
|
+
const h1 = vi.fn();
|
|
558
|
+
const h2 = vi.fn();
|
|
559
|
+
bus.on('test', h1);
|
|
560
|
+
bus.on('test', h2);
|
|
561
|
+
bus.emit('test', {});
|
|
562
|
+
expect(h1).toHaveBeenCalled();
|
|
563
|
+
expect(h2).toHaveBeenCalled();
|
|
564
|
+
});
|
|
565
|
+
|
|
566
|
+
it('ควรไม่ throw เมื่อ emit event ที่ไม่มี listener', () => {
|
|
567
|
+
const bus = new EventBus();
|
|
568
|
+
expect(() => bus.emit('nonexistent', {})).not.toThrow();
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
it('ควร return false เมื่อ off event ที่ไม่มี listener', () => {
|
|
572
|
+
const bus = new EventBus();
|
|
573
|
+
const handler = vi.fn();
|
|
574
|
+
const result = bus.off('test', handler);
|
|
575
|
+
expect(result).toBe(false);
|
|
576
|
+
});
|
|
577
|
+
});
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
- [ ] **Step 2: 运行测试确认失败**
|
|
581
|
+
|
|
582
|
+
```bash
|
|
583
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/eventbus.test.ts
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
- [ ] **Step 3: 实现 eventbus.ts**
|
|
587
|
+
|
|
588
|
+
```typescript
|
|
589
|
+
type EventHandler<T = unknown> = (data: T) => void;
|
|
590
|
+
|
|
591
|
+
export class EventBus {
|
|
592
|
+
private handlers = new Map<string, Set<EventHandler>>();
|
|
593
|
+
|
|
594
|
+
on<T = unknown>(event: string, handler: EventHandler<T>): () => void {
|
|
595
|
+
if (!this.handlers.has(event)) {
|
|
596
|
+
this.handlers.set(event, new Set());
|
|
597
|
+
}
|
|
598
|
+
this.handlers.get(event)!.add(handler as EventHandler);
|
|
599
|
+
|
|
600
|
+
return () => this.off(event, handler as EventHandler);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
off<T = unknown>(event: string, handler: EventHandler<T>): boolean {
|
|
604
|
+
const handlers = this.handlers.get(event);
|
|
605
|
+
if (!handlers) return false;
|
|
606
|
+
handlers.delete(handler as EventHandler);
|
|
607
|
+
if (handlers.size === 0) this.handlers.delete(event);
|
|
608
|
+
return true;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
emit<T = unknown>(event: string, data: T): void {
|
|
612
|
+
const handlers = this.handlers.get(event);
|
|
613
|
+
if (!handlers) return;
|
|
614
|
+
for (const handler of handlers) {
|
|
615
|
+
try {
|
|
616
|
+
handler(data);
|
|
617
|
+
} catch (e) {
|
|
618
|
+
console.error(`[EventBus] Error handling event "${event}":`, e);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
clear(): void {
|
|
624
|
+
this.handlers.clear();
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
```
|
|
628
|
+
|
|
629
|
+
- [ ] **Step 4: 运行测试确认通过**
|
|
630
|
+
|
|
631
|
+
```bash
|
|
632
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/eventbus.test.ts
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
- [ ] **Step 5: 提交**
|
|
636
|
+
|
|
637
|
+
```bash
|
|
638
|
+
git add .
|
|
639
|
+
git commit -m "feat: add event bus"
|
|
640
|
+
```
|
|
641
|
+
|
|
642
|
+
---
|
|
643
|
+
|
|
644
|
+
### Task 5: 配置管理系统
|
|
645
|
+
|
|
646
|
+
**Files:**
|
|
647
|
+
- Create: `e:\deeper-code\src\core\config.ts`
|
|
648
|
+
- Create: `e:\deeper-code\tests\unit\core\config.test.ts`
|
|
649
|
+
|
|
650
|
+
- [ ] **Step 1: 编写测试**
|
|
651
|
+
|
|
652
|
+
```typescript
|
|
653
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
654
|
+
import { ConfigManager } from '../../../src/core/config';
|
|
655
|
+
import { DeeperConfig } from '../../../src/core/types';
|
|
656
|
+
import { DEFAULT_MODEL } from '../../../src/core/constants';
|
|
657
|
+
|
|
658
|
+
describe('ConfigManager', () => {
|
|
659
|
+
let configManager: ConfigManager;
|
|
660
|
+
|
|
661
|
+
beforeEach(() => {
|
|
662
|
+
configManager = new ConfigManager();
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
it('ควร return ค่าเริ่มต้นเมื่อไม่มี config', () => {
|
|
666
|
+
const config = configManager.getAll();
|
|
667
|
+
expect(config.model.model).toBe(DEFAULT_MODEL);
|
|
668
|
+
expect(config.model.base_url).toBe('https://api.deepseek.com');
|
|
669
|
+
expect(config.model.temperature).toBe(0.7);
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it('ควร merge env variables', () => {
|
|
673
|
+
vi.stubEnv('DEEPER_MODEL', 'deepseek-v4-flash');
|
|
674
|
+
const config = configManager.getAll();
|
|
675
|
+
expect(config.model.model).toBe('deepseek-v4-flash');
|
|
676
|
+
vi.unstubAllEnvs();
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
it('ควร merge custom config โดย override', () => {
|
|
680
|
+
configManager.merge({ model: { model: 'custom-model' } } as Partial<DeeperConfig>);
|
|
681
|
+
expect(configManager.getAll().model.model).toBe('custom-model');
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
it('get ควร return ค่าเฉพาะ path', () => {
|
|
685
|
+
expect(configManager.get('model.temperature')).toBe(0.7);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it('set ควร update ค่าที่ path ได้', () => {
|
|
689
|
+
configManager.set('model.temperature', 0.2);
|
|
690
|
+
expect(configManager.get('model.temperature')).toBe(0.2);
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
- [ ] **Step 2: 运行测试确认失败**
|
|
696
|
+
|
|
697
|
+
```bash
|
|
698
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/config.test.ts
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
- [ ] **Step 3: 实现 config.ts**
|
|
702
|
+
|
|
703
|
+
```typescript
|
|
704
|
+
import { DeeperConfig } from './types';
|
|
705
|
+
import {
|
|
706
|
+
DEFAULT_MODEL,
|
|
707
|
+
DEFAULT_BASE_URL,
|
|
708
|
+
DEFAULT_TEMPERATURE,
|
|
709
|
+
DEFAULT_MAX_TOKENS,
|
|
710
|
+
DEFAULT_THINK_BUDGET,
|
|
711
|
+
DEFAULT_MAX_SUB_AGENTS,
|
|
712
|
+
DEFAULT_MAX_RECURSION_DEPTH,
|
|
713
|
+
DEFAULT_SUB_AGENT_TIMEOUT_MS,
|
|
714
|
+
DEFAULT_AUTO_SUMMARIZE_THRESHOLD,
|
|
715
|
+
DEEPER_ENV_PREFIX,
|
|
716
|
+
} from './constants';
|
|
717
|
+
|
|
718
|
+
function getDefaultConfig(): DeeperConfig {
|
|
719
|
+
return {
|
|
720
|
+
model: {
|
|
721
|
+
provider: 'deepseek',
|
|
722
|
+
model: DEFAULT_MODEL,
|
|
723
|
+
api_key: '',
|
|
724
|
+
base_url: DEFAULT_BASE_URL,
|
|
725
|
+
temperature: DEFAULT_TEMPERATURE,
|
|
726
|
+
max_tokens: DEFAULT_MAX_TOKENS,
|
|
727
|
+
think: { enabled: true, budget: DEFAULT_THINK_BUDGET },
|
|
728
|
+
},
|
|
729
|
+
agent: {
|
|
730
|
+
max_sub_agents: DEFAULT_MAX_SUB_AGENTS,
|
|
731
|
+
max_recursion_depth: DEFAULT_MAX_RECURSION_DEPTH,
|
|
732
|
+
sub_agent_timeout_ms: DEFAULT_SUB_AGENT_TIMEOUT_MS,
|
|
733
|
+
auto_approve_tools: [],
|
|
734
|
+
confirm_dangerous: [],
|
|
735
|
+
max_concurrent_tools: 3,
|
|
736
|
+
},
|
|
737
|
+
context: {
|
|
738
|
+
max_tokens: DEFAULT_MAX_TOKENS,
|
|
739
|
+
history_size: 50,
|
|
740
|
+
auto_summarize_threshold: DEFAULT_AUTO_SUMMARIZE_THRESHOLD,
|
|
741
|
+
file_context_limit: 50000,
|
|
742
|
+
},
|
|
743
|
+
ui: {
|
|
744
|
+
theme: 'dark',
|
|
745
|
+
show_token_count: true,
|
|
746
|
+
show_tool_calls: true,
|
|
747
|
+
syntax_highlight: true,
|
|
748
|
+
animation_speed: 'fast',
|
|
749
|
+
compact_mode: false,
|
|
750
|
+
},
|
|
751
|
+
mcp: {
|
|
752
|
+
servers: {},
|
|
753
|
+
auto_connect: true,
|
|
754
|
+
connection_timeout_ms: 10000,
|
|
755
|
+
},
|
|
756
|
+
skills: {
|
|
757
|
+
auto_load: true,
|
|
758
|
+
discovery_paths: ['~/.deeper/skills', '.deeper/skills'],
|
|
759
|
+
},
|
|
760
|
+
privacy: {
|
|
761
|
+
telemetry: false,
|
|
762
|
+
log_level: 'info',
|
|
763
|
+
max_log_files: 10,
|
|
764
|
+
},
|
|
765
|
+
};
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function deepMerge(target: Record<string, unknown>, source: Record<string, unknown>): Record<string, unknown> {
|
|
769
|
+
const output = { ...target };
|
|
770
|
+
for (const key of Object.keys(source)) {
|
|
771
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
772
|
+
output[key] = deepMerge(
|
|
773
|
+
(target[key] as Record<string, unknown>) || {},
|
|
774
|
+
source[key] as Record<string, unknown>,
|
|
775
|
+
);
|
|
776
|
+
} else {
|
|
777
|
+
output[key] = source[key];
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
return output;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function readEnvConfig(): Partial<DeeperConfig> {
|
|
784
|
+
const env: Record<string, unknown> = {};
|
|
785
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
786
|
+
if (!key.startsWith(DEEPER_ENV_PREFIX) || !value) continue;
|
|
787
|
+
const configKey = key.slice(DEEPER_ENV_PREFIX.length).toLowerCase();
|
|
788
|
+
const keys = configKey.split('_');
|
|
789
|
+
let current: Record<string, unknown> = env;
|
|
790
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
791
|
+
if (!current[keys[i]]) current[keys[i]] = {};
|
|
792
|
+
current = current[keys[i]] as Record<string, unknown>;
|
|
793
|
+
}
|
|
794
|
+
current[keys[keys.length - 1]] = value;
|
|
795
|
+
}
|
|
796
|
+
return env as unknown as Partial<DeeperConfig>;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
export class ConfigManager {
|
|
800
|
+
private config: DeeperConfig;
|
|
801
|
+
|
|
802
|
+
constructor(initial?: Partial<DeeperConfig>) {
|
|
803
|
+
this.config = getDefaultConfig();
|
|
804
|
+
const envConfig = readEnvConfig();
|
|
805
|
+
this.config = deepMerge(this.config, envConfig as Record<string, unknown>) as DeeperConfig;
|
|
806
|
+
if (initial) {
|
|
807
|
+
this.merge(initial);
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
merge(partial: Partial<DeeperConfig>): void {
|
|
812
|
+
this.config = deepMerge(this.config as Record<string, unknown>, partial as Record<string, unknown>) as DeeperConfig;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
getAll(): DeeperConfig {
|
|
816
|
+
return { ...this.config };
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
get(path: string): unknown {
|
|
820
|
+
const keys = path.split('.');
|
|
821
|
+
let current: unknown = this.config;
|
|
822
|
+
for (const key of keys) {
|
|
823
|
+
if (current && typeof current === 'object') {
|
|
824
|
+
current = (current as Record<string, unknown>)[key];
|
|
825
|
+
} else {
|
|
826
|
+
return undefined;
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return current;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
set(path: string, value: unknown): void {
|
|
833
|
+
const keys = path.split('.');
|
|
834
|
+
let current: Record<string, unknown> = this.config as unknown as Record<string, unknown>;
|
|
835
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
836
|
+
if (!current[keys[i]] || typeof current[keys[i]] !== 'object') {
|
|
837
|
+
current[keys[i]] = {};
|
|
838
|
+
}
|
|
839
|
+
current = current[keys[i]] as Record<string, unknown>;
|
|
840
|
+
}
|
|
841
|
+
current[keys[keys.length - 1]] = value;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
- [ ] **Step 4: 运行测试确认通过**
|
|
847
|
+
|
|
848
|
+
```bash
|
|
849
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/config.test.ts
|
|
850
|
+
```
|
|
851
|
+
|
|
852
|
+
- [ ] **Step 5: 提交**
|
|
853
|
+
|
|
854
|
+
```bash
|
|
855
|
+
git add .
|
|
856
|
+
git commit -m "feat: add config manager"
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
---
|
|
860
|
+
|
|
861
|
+
### Task 6: 存储系统
|
|
862
|
+
|
|
863
|
+
**Files:**
|
|
864
|
+
- Create: `e:\deeper-code\src\core\storage.ts`
|
|
865
|
+
- Create: `e:\deeper-code\tests\unit\core\storage.test.ts`
|
|
866
|
+
|
|
867
|
+
- [ ] **Step 1: 编写测试**
|
|
868
|
+
|
|
869
|
+
```typescript
|
|
870
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
871
|
+
import { writeFileSync, mkdirSync, rmSync, existsSync } from 'fs';
|
|
872
|
+
import { join } from 'path';
|
|
873
|
+
import { os } from 'os';
|
|
874
|
+
import { StorageManager } from '../../../src/core/storage';
|
|
875
|
+
|
|
876
|
+
describe('StorageManager', () => {
|
|
877
|
+
const testDir = join(os.tmpdir(), 'deeper-test-storage-' + Date.now());
|
|
878
|
+
|
|
879
|
+
beforeEach(() => {
|
|
880
|
+
mkdirSync(testDir, { recursive: true });
|
|
881
|
+
});
|
|
882
|
+
|
|
883
|
+
afterEach(() => {
|
|
884
|
+
if (existsSync(testDir)) rmSync(testDir, { recursive: true, force: true });
|
|
885
|
+
});
|
|
886
|
+
|
|
887
|
+
it('ควร save และ load ข้อมูลได้', () => {
|
|
888
|
+
const storage = new StorageManager(testDir);
|
|
889
|
+
const data = { name: 'test', value: 123 };
|
|
890
|
+
storage.save('test.json', data);
|
|
891
|
+
const loaded = storage.load<typeof data>('test.json');
|
|
892
|
+
expect(loaded).toEqual(data);
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
it('ควร return default เมื่อ file ไม่มี', () => {
|
|
896
|
+
const storage = new StorageManager(testDir);
|
|
897
|
+
const result = storage.load('nonexistent.json', { default: true });
|
|
898
|
+
expect(result).toEqual({ default: true });
|
|
899
|
+
});
|
|
900
|
+
|
|
901
|
+
it('ควร delete ไฟล์ได้', () => {
|
|
902
|
+
const storage = new StorageManager(testDir);
|
|
903
|
+
storage.save('temp.json', { a: 1 });
|
|
904
|
+
expect(storage.exists('temp.json')).toBe(true);
|
|
905
|
+
storage.delete('temp.json');
|
|
906
|
+
expect(storage.exists('temp.json')).toBe(false);
|
|
907
|
+
});
|
|
908
|
+
|
|
909
|
+
it('exists ควร return false สำหรับไฟล์ที่ไม่มี', () => {
|
|
910
|
+
const storage = new StorageManager(testDir);
|
|
911
|
+
expect(storage.exists('nope.json')).toBe(false);
|
|
912
|
+
});
|
|
913
|
+
});
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
- [ ] **Step 2: 运行测试确认失败**
|
|
917
|
+
|
|
918
|
+
```bash
|
|
919
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/storage.test.ts
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
- [ ] **Step 3: 实现 storage.ts**
|
|
923
|
+
|
|
924
|
+
```typescript
|
|
925
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, unlinkSync } from 'fs';
|
|
926
|
+
import { join, dirname } from 'path';
|
|
927
|
+
|
|
928
|
+
export class StorageManager {
|
|
929
|
+
constructor(private baseDir: string) {
|
|
930
|
+
mkdirSync(baseDir, { recursive: true });
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
save<T = unknown>(filename: string, data: T): void {
|
|
934
|
+
const filePath = join(this.baseDir, filename);
|
|
935
|
+
const dir = dirname(filePath);
|
|
936
|
+
mkdirSync(dir, { recursive: true });
|
|
937
|
+
writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf-8');
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
load<T = unknown>(filename: string, defaultValue?: T): T {
|
|
941
|
+
const filePath = join(this.baseDir, filename);
|
|
942
|
+
if (!existsSync(filePath)) {
|
|
943
|
+
if (defaultValue !== undefined) return defaultValue;
|
|
944
|
+
throw new Error(`File not found: ${filePath}`);
|
|
945
|
+
}
|
|
946
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
947
|
+
return JSON.parse(content) as T;
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
exists(filename: string): boolean {
|
|
951
|
+
return existsSync(join(this.baseDir, filename));
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
delete(filename: string): void {
|
|
955
|
+
const filePath = join(this.baseDir, filename);
|
|
956
|
+
if (existsSync(filePath)) {
|
|
957
|
+
unlinkSync(filePath);
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
```
|
|
962
|
+
|
|
963
|
+
- [ ] **Step 4: 运行测试确认通过**
|
|
964
|
+
|
|
965
|
+
```bash
|
|
966
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/storage.test.ts
|
|
967
|
+
```
|
|
968
|
+
|
|
969
|
+
- [ ] **Step 5: 提交**
|
|
970
|
+
|
|
971
|
+
```bash
|
|
972
|
+
git add .
|
|
973
|
+
git commit -m "feat: add storage manager"
|
|
974
|
+
```
|
|
975
|
+
|
|
976
|
+
---
|
|
977
|
+
|
|
978
|
+
### Task 7: 进程管理工具
|
|
979
|
+
|
|
980
|
+
**Files:**
|
|
981
|
+
- Create: `e:\deeper-code\src\core\process.ts`
|
|
982
|
+
|
|
983
|
+
- [ ] **Step 1: 实现 process.ts**
|
|
984
|
+
|
|
985
|
+
```typescript
|
|
986
|
+
import { exec, execSync, ChildProcess, spawn, SpawnOptions } from 'child_process';
|
|
987
|
+
import { promisify } from 'util';
|
|
988
|
+
|
|
989
|
+
const execPromise = promisify(exec);
|
|
990
|
+
|
|
991
|
+
export type CommandResult = {
|
|
992
|
+
stdout: string;
|
|
993
|
+
stderr: string;
|
|
994
|
+
code: number | null;
|
|
995
|
+
killed: boolean;
|
|
996
|
+
};
|
|
997
|
+
|
|
998
|
+
export type RunningProcess = {
|
|
999
|
+
id: string;
|
|
1000
|
+
process: ChildProcess;
|
|
1001
|
+
startTime: number;
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
export class ProcessManager {
|
|
1005
|
+
private processes = new Map<string, RunningProcess>();
|
|
1006
|
+
private idCounter = 0;
|
|
1007
|
+
|
|
1008
|
+
async run(command: string, cwd?: string, timeoutMs?: number): Promise<CommandResult> {
|
|
1009
|
+
try {
|
|
1010
|
+
const { stdout, stderr } = await execPromise(command, {
|
|
1011
|
+
cwd,
|
|
1012
|
+
timeout: timeoutMs,
|
|
1013
|
+
maxBuffer: 50 * 1024 * 1024,
|
|
1014
|
+
encoding: 'utf-8',
|
|
1015
|
+
});
|
|
1016
|
+
return { stdout, stderr, code: 0, killed: false };
|
|
1017
|
+
} catch (e: unknown) {
|
|
1018
|
+
const err = e as { stdout?: string; stderr?: string; code?: number; killed?: boolean };
|
|
1019
|
+
return {
|
|
1020
|
+
stdout: err.stdout || '',
|
|
1021
|
+
stderr: err.stderr || (e as Error).message || '',
|
|
1022
|
+
code: err.code ?? 1,
|
|
1023
|
+
killed: err.killed ?? false,
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
spawn(command: string, args: string[], options?: SpawnOptions): string {
|
|
1029
|
+
const id = `proc_${++this.idCounter}`;
|
|
1030
|
+
const child = spawn(command, args, {
|
|
1031
|
+
...options,
|
|
1032
|
+
stdio: options?.stdio || 'pipe',
|
|
1033
|
+
});
|
|
1034
|
+
this.processes.set(id, { id, process: child, startTime: Date.now() });
|
|
1035
|
+
return id;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
getProcess(id: string): RunningProcess | undefined {
|
|
1039
|
+
return this.processes.get(id);
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
async kill(id: string, signal: NodeJS.Signals = 'SIGTERM'): Promise<void> {
|
|
1043
|
+
const proc = this.processes.get(id);
|
|
1044
|
+
if (!proc) return;
|
|
1045
|
+
if (proc.process.exitCode === null && proc.process.signalCode === null) {
|
|
1046
|
+
proc.process.kill(signal);
|
|
1047
|
+
}
|
|
1048
|
+
this.processes.delete(id);
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
list(): RunningProcess[] {
|
|
1052
|
+
return Array.from(this.processes.values());
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
cleanup(): void {
|
|
1056
|
+
for (const [id, proc] of this.processes) {
|
|
1057
|
+
if (proc.process.exitCode === null && proc.process.signalCode === null) {
|
|
1058
|
+
proc.process.kill('SIGTERM');
|
|
1059
|
+
}
|
|
1060
|
+
this.processes.delete(id);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
export const processManager = new ProcessManager();
|
|
1066
|
+
```
|
|
1067
|
+
|
|
1068
|
+
- [ ] **Step 2: 提交**
|
|
1069
|
+
|
|
1070
|
+
```bash
|
|
1071
|
+
git add .
|
|
1072
|
+
git commit -m "feat: add process manager"
|
|
1073
|
+
```
|
|
1074
|
+
|
|
1075
|
+
---
|
|
1076
|
+
|
|
1077
|
+
### Task 8: 加密工具与沙箱
|
|
1078
|
+
|
|
1079
|
+
**Files:**
|
|
1080
|
+
- Create: `e:\deeper-code\src\core\crypto.ts`
|
|
1081
|
+
- Create: `e:\deeper-code\src\core\sandbox.ts`
|
|
1082
|
+
- Create: `e:\deeper-code\tests\unit\core\crypto.test.ts`
|
|
1083
|
+
|
|
1084
|
+
- [ ] **Step 1: 实现 crypto.ts**
|
|
1085
|
+
|
|
1086
|
+
```typescript
|
|
1087
|
+
import { createHash, randomBytes, createCipheriv, createDecipheriv } from 'crypto';
|
|
1088
|
+
|
|
1089
|
+
const ALGORITHM = 'aes-256-gcm';
|
|
1090
|
+
const IV_LENGTH = 16;
|
|
1091
|
+
const AUTH_TAG_LENGTH = 16;
|
|
1092
|
+
|
|
1093
|
+
export function hash(content: string, algorithm: 'sha256' | 'sha512' | 'md5' = 'sha256'): string {
|
|
1094
|
+
return createHash(algorithm).update(content).digest('hex');
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
export function generateId(length = 8): string {
|
|
1098
|
+
return randomBytes(length).toString('hex').slice(0, length * 2);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
export function encrypt(text: string, key: string): { encrypted: string; iv: string; authTag: string } {
|
|
1102
|
+
const iv = randomBytes(IV_LENGTH);
|
|
1103
|
+
const cipherKey = createHash('sha256').update(key).digest();
|
|
1104
|
+
const cipher = createCipheriv(ALGORITHM, cipherKey, iv);
|
|
1105
|
+
let encrypted = cipher.update(text, 'utf-8', 'hex');
|
|
1106
|
+
encrypted += cipher.final('hex');
|
|
1107
|
+
const authTag = cipher.getAuthTag().toString('hex');
|
|
1108
|
+
return { encrypted, iv: iv.toString('hex'), authTag };
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
export function decrypt(encrypted: string, key: string, ivHex: string, authTagHex: string): string {
|
|
1112
|
+
const iv = Buffer.from(ivHex, 'hex');
|
|
1113
|
+
const authTag = Buffer.from(authTagHex, 'hex');
|
|
1114
|
+
const cipherKey = createHash('sha256').update(key).digest();
|
|
1115
|
+
const decipher = createDecipheriv(ALGORITHM, cipherKey, iv);
|
|
1116
|
+
decipher.setAuthTag(authTag);
|
|
1117
|
+
let decrypted = decipher.update(encrypted, 'hex', 'utf-8');
|
|
1118
|
+
decrypted += decipher.final('utf-8');
|
|
1119
|
+
return decrypted;
|
|
1120
|
+
}
|
|
1121
|
+
```
|
|
1122
|
+
|
|
1123
|
+
- [ ] **Step 2: 实现 sandbox.ts**
|
|
1124
|
+
|
|
1125
|
+
```typescript
|
|
1126
|
+
import ivm from 'isolated-vm';
|
|
1127
|
+
|
|
1128
|
+
export type SandboxOptions = {
|
|
1129
|
+
timeoutMs?: number;
|
|
1130
|
+
memoryLimitMb?: number;
|
|
1131
|
+
};
|
|
1132
|
+
|
|
1133
|
+
export async function runInSandbox(code: string, context: Record<string, unknown> = {}, options: SandboxOptions = {}): Promise<unknown> {
|
|
1134
|
+
const isolate = new ivm.Isolate({ memoryLimit: (options.memoryLimitMb || 128) * 1024 * 1024 });
|
|
1135
|
+
const isolateContext = await isolate.createContext();
|
|
1136
|
+
const jail = isolateContext.global;
|
|
1137
|
+
await jail.set('global', jail.derefInto());
|
|
1138
|
+
|
|
1139
|
+
for (const [key, value] of Object.entries(context)) {
|
|
1140
|
+
await jail.set(key, value, { copy: true });
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
const script = await isolate.compileScript(code);
|
|
1144
|
+
const result = await script.run(isolateContext, {
|
|
1145
|
+
timeout: options.timeoutMs || 5000,
|
|
1146
|
+
});
|
|
1147
|
+
|
|
1148
|
+
isolate.dispose();
|
|
1149
|
+
return result;
|
|
1150
|
+
}
|
|
1151
|
+
```
|
|
1152
|
+
|
|
1153
|
+
- [ ] **Step 3: 编写并运行测试**
|
|
1154
|
+
|
|
1155
|
+
```typescript
|
|
1156
|
+
import { describe, it, expect } from 'vitest';
|
|
1157
|
+
import { hash, generateId, encrypt, decrypt } from '../../../src/core/crypto';
|
|
1158
|
+
|
|
1159
|
+
describe('crypto', () => {
|
|
1160
|
+
it('hash ควร return sha256', () => {
|
|
1161
|
+
const result = hash('hello');
|
|
1162
|
+
expect(result).toHaveLength(64);
|
|
1163
|
+
expect(typeof result).toBe('string');
|
|
1164
|
+
});
|
|
1165
|
+
|
|
1166
|
+
it('generateId ควร return id ที่มีความยาวถูกต้อง', () => {
|
|
1167
|
+
const id = generateId(8);
|
|
1168
|
+
expect(id).toHaveLength(16);
|
|
1169
|
+
});
|
|
1170
|
+
|
|
1171
|
+
it('encrypt และ decrypt ควรทำงานได้ครบวงจร', () => {
|
|
1172
|
+
const { encrypted, iv, authTag } = encrypt('secret message', 'my-key');
|
|
1173
|
+
const decrypted = decrypt(encrypted, 'my-key', iv, authTag);
|
|
1174
|
+
expect(decrypted).toBe('secret message');
|
|
1175
|
+
});
|
|
1176
|
+
});
|
|
1177
|
+
```
|
|
1178
|
+
|
|
1179
|
+
- [ ] **Step 4: 运行测试确认通过**
|
|
1180
|
+
|
|
1181
|
+
```bash
|
|
1182
|
+
cd e:\deeper-code && pnpm test -- tests/unit/core/crypto.test.ts
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
- [ ] **Step 5: 提交**
|
|
1186
|
+
|
|
1187
|
+
```bash
|
|
1188
|
+
git add .
|
|
1189
|
+
git commit -m "feat: add crypto utilities and sandbox"
|
|
1190
|
+
```
|
|
1191
|
+
|
|
1192
|
+
---
|
|
1193
|
+
|
|
1194
|
+
## Phase 2: DeepSeek 模型客户端
|
|
1195
|
+
|
|
1196
|
+
### Task 9: 模型层类型定义
|
|
1197
|
+
|
|
1198
|
+
**Files:**
|
|
1199
|
+
- Create: `e:\deeper-code\src\model\types.ts`
|
|
1200
|
+
|
|
1201
|
+
- [ ] **Step 1: 实现 types.ts**
|
|
1202
|
+
|
|
1203
|
+
```typescript
|
|
1204
|
+
export type DeepSeekMessage = {
|
|
1205
|
+
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
1206
|
+
content: string | null;
|
|
1207
|
+
reasoning_content?: string;
|
|
1208
|
+
tool_calls?: Array<{
|
|
1209
|
+
id: string;
|
|
1210
|
+
type: 'function';
|
|
1211
|
+
function: { name: string; arguments: string };
|
|
1212
|
+
}>;
|
|
1213
|
+
tool_call_id?: string;
|
|
1214
|
+
name?: string;
|
|
1215
|
+
};
|
|
1216
|
+
|
|
1217
|
+
export type DeepSeekTool = {
|
|
1218
|
+
type: 'function';
|
|
1219
|
+
function: {
|
|
1220
|
+
name: string;
|
|
1221
|
+
description: string;
|
|
1222
|
+
parameters: object;
|
|
1223
|
+
};
|
|
1224
|
+
};
|
|
1225
|
+
|
|
1226
|
+
export type DeepSeekRequest = {
|
|
1227
|
+
model: string;
|
|
1228
|
+
messages: DeepSeekMessage[];
|
|
1229
|
+
tools?: DeepSeekTool[];
|
|
1230
|
+
temperature?: number;
|
|
1231
|
+
max_tokens?: number;
|
|
1232
|
+
stream?: boolean;
|
|
1233
|
+
thinking?: {
|
|
1234
|
+
type: 'enabled';
|
|
1235
|
+
budget_tokens: number;
|
|
1236
|
+
};
|
|
1237
|
+
};
|
|
1238
|
+
|
|
1239
|
+
export type DeepSeekChoice = {
|
|
1240
|
+
index: number;
|
|
1241
|
+
message: DeepSeekMessage;
|
|
1242
|
+
finish_reason: 'stop' | 'tool_calls' | 'length' | 'content_filter' | null;
|
|
1243
|
+
};
|
|
1244
|
+
|
|
1245
|
+
export type DeepSeekUsage = {
|
|
1246
|
+
prompt_tokens: number;
|
|
1247
|
+
completion_tokens: number;
|
|
1248
|
+
total_tokens: number;
|