auto-coder-web 0.1.90__py3-none-any.whl → 0.1.91__py3-none-any.whl
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.
- auto_coder_web/common_router/completions_router.py +46 -52
- auto_coder_web/common_router/file_router.py +66 -0
- auto_coder_web/version.py +1 -1
- auto_coder_web/web/assets/{main-D9kwl53k.css → main-edBorJ-r.css} +1 -1
- auto_coder_web/web/assets/main.js +295 -295
- auto_coder_web/web/index.html +1 -1
- {auto_coder_web-0.1.90.dist-info → auto_coder_web-0.1.91.dist-info}/METADATA +2 -2
- {auto_coder_web-0.1.90.dist-info → auto_coder_web-0.1.91.dist-info}/RECORD +11 -11
- {auto_coder_web-0.1.90.dist-info → auto_coder_web-0.1.91.dist-info}/WHEEL +0 -0
- {auto_coder_web-0.1.90.dist-info → auto_coder_web-0.1.91.dist-info}/entry_points.txt +0 -0
- {auto_coder_web-0.1.90.dist-info → auto_coder_web-0.1.91.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
import glob
|
3
3
|
import json
|
4
|
+
import fnmatch
|
4
5
|
from typing import List
|
5
6
|
from pydantic import BaseModel
|
6
7
|
from fastapi import APIRouter, Query, Request, Depends
|
@@ -13,12 +14,15 @@ from autocoder.index.symbols_utils import (
|
|
13
14
|
)
|
14
15
|
|
15
16
|
from autocoder.auto_coder_runner import get_memory
|
17
|
+
from autocoder.common.ignorefiles.ignore_file_utils import should_ignore
|
18
|
+
from autocoder.common.directory_cache.cache import DirectoryCache, initialize_cache
|
16
19
|
import json
|
17
20
|
import asyncio
|
18
21
|
import aiofiles
|
19
22
|
import aiofiles.os
|
23
|
+
import logging
|
20
24
|
|
21
|
-
|
25
|
+
logger = logging.getLogger(__name__)
|
22
26
|
router = APIRouter()
|
23
27
|
|
24
28
|
class SymbolItem(BaseModel):
|
@@ -35,67 +39,57 @@ async def get_project_path(request: Request):
|
|
35
39
|
"""获取项目路径作为依赖"""
|
36
40
|
return request.app.state.project_path
|
37
41
|
|
38
|
-
def find_files_in_project(patterns: List[str], project_path: str) -> List[str]:
|
42
|
+
async def find_files_in_project(patterns: List[str], project_path: str) -> List[str]:
|
39
43
|
memory = get_memory()
|
40
|
-
default_exclude_dirs = [".git", "node_modules", "dist", "build", "__pycache__", ".venv", ".auto-coder"]
|
41
44
|
active_file_list = memory["current_files"]["files"]
|
42
|
-
final_exclude_dirs = default_exclude_dirs + memory.get("exclude_dirs", [])
|
43
45
|
project_root = project_path
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
rel_path = path
|
54
|
-
|
55
|
-
# 检查文件或目录本身是否以.开头
|
56
|
-
if os.path.basename(rel_path).startswith('.'):
|
57
|
-
return True
|
58
|
-
|
59
|
-
# 检查路径中是否包含排除目录
|
60
|
-
path_parts = rel_path.split(os.sep)
|
61
|
-
return any(part in final_exclude_dirs or part.startswith('.') for part in path_parts)
|
62
|
-
|
47
|
+
# 确保目录缓存已初始化
|
48
|
+
try:
|
49
|
+
cache = DirectoryCache.get_instance(project_root)
|
50
|
+
except ValueError:
|
51
|
+
# 如果缓存未初始化,则初始化它
|
52
|
+
initialize_cache(project_root)
|
53
|
+
cache = DirectoryCache.get_instance()
|
54
|
+
|
63
55
|
# 如果没有提供有效模式,返回过滤后的活动文件列表
|
64
56
|
if not patterns or (len(patterns) == 1 and patterns[0] == ""):
|
65
|
-
|
57
|
+
# 使用缓存中的所有文件
|
58
|
+
all_files = await cache.query([])
|
59
|
+
# 合并活动文件列表和缓存文件
|
60
|
+
combined_files = set(all_files)
|
61
|
+
combined_files.update([f for f in active_file_list if not should_ignore(f)])
|
62
|
+
return list(combined_files)
|
66
63
|
|
67
64
|
matched_files = set() # 使用集合避免重复
|
68
|
-
|
65
|
+
|
66
|
+
# 1. 首先从活动文件列表中匹配,这通常是最近编辑的文件
|
69
67
|
for pattern in patterns:
|
70
|
-
# 1. 从活动文件列表中匹配
|
71
68
|
for file_path in active_file_list:
|
72
|
-
if not
|
69
|
+
if not should_ignore(file_path) and pattern.lower() in os.path.basename(file_path).lower():
|
73
70
|
matched_files.add(file_path)
|
74
|
-
|
75
|
-
|
71
|
+
|
72
|
+
# 2. 使用DirectoryCache进行高效查询
|
73
|
+
cache_patterns = []
|
74
|
+
for pattern in patterns:
|
75
|
+
# 处理通配符模式
|
76
76
|
if "*" in pattern or "?" in pattern:
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
file_path = os.path.join(root, file)
|
94
|
-
if not should_exclude_path(file_path):
|
95
|
-
matched_files.add(file_path)
|
96
|
-
|
97
|
-
# 4. 如果pattern本身是文件路径
|
98
|
-
if os.path.exists(pattern) and os.path.isfile(pattern) and not should_exclude_path(pattern):
|
77
|
+
cache_patterns.append(pattern)
|
78
|
+
else:
|
79
|
+
# 对于非通配符模式,我们添加一个通配符以进行部分匹配
|
80
|
+
cache_patterns.append(f"*{pattern}*")
|
81
|
+
|
82
|
+
# 执行缓存查询
|
83
|
+
if cache_patterns:
|
84
|
+
try:
|
85
|
+
cache_results = await cache.query(cache_patterns)
|
86
|
+
matched_files.update(cache_results)
|
87
|
+
except Exception as e:
|
88
|
+
logger.error(f"Error querying directory cache: {e}", exc_info=True)
|
89
|
+
|
90
|
+
# 3. 如果pattern本身是文件路径,直接添加
|
91
|
+
for pattern in patterns:
|
92
|
+
if os.path.exists(pattern) and os.path.isfile(pattern) and not should_ignore(pattern):
|
99
93
|
matched_files.add(os.path.abspath(pattern))
|
100
94
|
|
101
95
|
return list(matched_files)
|
@@ -153,11 +147,11 @@ async def get_file_completions(
|
|
153
147
|
):
|
154
148
|
"""获取文件名补全"""
|
155
149
|
patterns = [name]
|
156
|
-
|
150
|
+
# 直接调用异步函数,不需要使用asyncio.to_thread
|
151
|
+
matches = await find_files_in_project(patterns, project_path)
|
157
152
|
completions = []
|
158
153
|
project_root = project_path
|
159
154
|
for file_name in matches:
|
160
|
-
# path_parts = file_name.split(os.sep)
|
161
155
|
# 只显示最后三层路径,让显示更简洁
|
162
156
|
display_name = os.path.basename(file_name)
|
163
157
|
relative_path = os.path.relpath(file_name, project_root)
|
@@ -14,6 +14,9 @@ import pathspec
|
|
14
14
|
|
15
15
|
router = APIRouter()
|
16
16
|
|
17
|
+
class CreateFileRequest(BaseModel):
|
18
|
+
content: str = ""
|
19
|
+
|
17
20
|
DEFAULT_IGNORED_DIRS = ['.git', '.auto-coder', 'node_modules', '.mvn', '.idea', '__pycache__', '.venv', 'venv', 'dist', 'build', '.gradle']
|
18
21
|
|
19
22
|
def load_ignore_spec(source_dir: str) -> Optional[pathspec.PathSpec]:
|
@@ -196,6 +199,69 @@ async def list_files_in_directory(
|
|
196
199
|
return result
|
197
200
|
|
198
201
|
|
202
|
+
@router.post("/api/file/{path:path}")
|
203
|
+
async def create_file(
|
204
|
+
path: str,
|
205
|
+
request: Request,
|
206
|
+
project_path: str = Depends(get_project_path)
|
207
|
+
):
|
208
|
+
"""
|
209
|
+
Create a new file at the specified path with optional initial content.
|
210
|
+
"""
|
211
|
+
try:
|
212
|
+
data = await request.json()
|
213
|
+
content = data.get("content", "")
|
214
|
+
|
215
|
+
full_path = os.path.join(project_path, path)
|
216
|
+
dir_path = os.path.dirname(full_path)
|
217
|
+
|
218
|
+
# Check if file already exists
|
219
|
+
if await aiofiles.os.path.exists(full_path):
|
220
|
+
raise HTTPException(status_code=409, detail=f"File already exists at {path}")
|
221
|
+
|
222
|
+
# Ensure the directory exists asynchronously
|
223
|
+
if not await aiofiles.os.path.exists(dir_path):
|
224
|
+
await aiofiles.os.makedirs(dir_path, exist_ok=True)
|
225
|
+
elif not await aiofiles.os.path.isdir(dir_path):
|
226
|
+
raise HTTPException(status_code=400, detail=f"Path conflict: {dir_path} exists but is not a directory.")
|
227
|
+
|
228
|
+
# Write the file content asynchronously
|
229
|
+
async with aiofiles.open(full_path, 'w', encoding='utf-8') as f:
|
230
|
+
await f.write(content)
|
231
|
+
|
232
|
+
return {"message": f"Successfully created {path}"}
|
233
|
+
except HTTPException as http_exc: # Re-raise HTTP exceptions
|
234
|
+
raise http_exc
|
235
|
+
except Exception as e:
|
236
|
+
raise HTTPException(status_code=500, detail=str(e))
|
237
|
+
|
238
|
+
@router.post("/api/directory/{path:path}")
|
239
|
+
async def create_directory(
|
240
|
+
path: str,
|
241
|
+
project_path: str = Depends(get_project_path)
|
242
|
+
):
|
243
|
+
"""
|
244
|
+
Create a new directory at the specified path.
|
245
|
+
"""
|
246
|
+
try:
|
247
|
+
full_path = os.path.join(project_path, path)
|
248
|
+
|
249
|
+
# Check if directory already exists
|
250
|
+
if await aiofiles.os.path.exists(full_path):
|
251
|
+
if await aiofiles.os.path.isdir(full_path):
|
252
|
+
return {"message": f"Directory already exists at {path}"}
|
253
|
+
else:
|
254
|
+
raise HTTPException(status_code=409, detail=f"A file already exists at {path}")
|
255
|
+
|
256
|
+
# Create the directory and any necessary parent directories
|
257
|
+
await aiofiles.os.makedirs(full_path, exist_ok=True)
|
258
|
+
|
259
|
+
return {"message": f"Successfully created directory {path}"}
|
260
|
+
except HTTPException as http_exc: # Re-raise HTTP exceptions
|
261
|
+
raise http_exc
|
262
|
+
except Exception as e:
|
263
|
+
raise HTTPException(status_code=500, detail=str(e))
|
264
|
+
|
199
265
|
@router.get("/api/search-in-files")
|
200
266
|
async def search_in_files(
|
201
267
|
query: str = Query(..., description="Search text"),
|
auto_coder_web/version.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
__version__ = "0.1.
|
1
|
+
__version__ = "0.1.91"
|