@chiway/contextweaver 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/LICENSE +21 -0
- package/README.md +384 -0
- package/dist/SearchService-533KL2HP.js +1654 -0
- package/dist/chunk-2CY5SYBI.js +1364 -0
- package/dist/chunk-AMQQK4P7.js +171 -0
- package/dist/chunk-B6OWNBOD.js +545 -0
- package/dist/chunk-EZG4H4MN.js +286 -0
- package/dist/chunk-HR5KUQSM.js +906 -0
- package/dist/chunk-RJURH22T.js +266 -0
- package/dist/codebaseRetrieval-IC44RHCL.js +12 -0
- package/dist/config-BWZ6CU3W.js +19 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +157 -0
- package/dist/lock-DVY3KJSK.js +128 -0
- package/dist/scanner-SZ2BDYDS.js +11 -0
- package/dist/server-PPQUHCUB.js +147 -0
- package/package.json +79 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
// src/config.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import dotenv from "dotenv";
|
|
6
|
+
var isDev = process.env.NODE_ENV === "dev";
|
|
7
|
+
var isMcpMode = process.argv.includes("mcp");
|
|
8
|
+
function loadEnv() {
|
|
9
|
+
const candidates = isDev ? [
|
|
10
|
+
path.join(process.cwd(), ".env"),
|
|
11
|
+
// 1. 当前目录(开发用)
|
|
12
|
+
path.join(os.homedir(), ".contextweaver", ".env")
|
|
13
|
+
// 2. 用户配置目录(回退)
|
|
14
|
+
] : [
|
|
15
|
+
path.join(os.homedir(), ".contextweaver", ".env")
|
|
16
|
+
// 生产环境只用用户配置
|
|
17
|
+
];
|
|
18
|
+
const envPath = candidates.find((p) => fs.existsSync(p));
|
|
19
|
+
if (envPath) {
|
|
20
|
+
const result = dotenv.config({ path: envPath, quiet: true });
|
|
21
|
+
if (result.error) {
|
|
22
|
+
console.error(`[config] \u52A0\u8F7D\u73AF\u5883\u53D8\u91CF\u5931\u8D25: ${result.error.message}`);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
loadEnv();
|
|
28
|
+
var DEFAULT_API_KEY_PLACEHOLDER = "your-api-key-here";
|
|
29
|
+
function checkEmbeddingEnv() {
|
|
30
|
+
const missingVars = [];
|
|
31
|
+
const apiKey = process.env.EMBEDDINGS_API_KEY;
|
|
32
|
+
if (!apiKey || apiKey === DEFAULT_API_KEY_PLACEHOLDER) {
|
|
33
|
+
missingVars.push("EMBEDDINGS_API_KEY");
|
|
34
|
+
}
|
|
35
|
+
if (!process.env.EMBEDDINGS_BASE_URL) {
|
|
36
|
+
missingVars.push("EMBEDDINGS_BASE_URL");
|
|
37
|
+
}
|
|
38
|
+
if (!process.env.EMBEDDINGS_MODEL) {
|
|
39
|
+
missingVars.push("EMBEDDINGS_MODEL");
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
isValid: missingVars.length === 0,
|
|
43
|
+
missingVars
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function checkRerankerEnv() {
|
|
47
|
+
const missingVars = [];
|
|
48
|
+
const apiKey = process.env.RERANK_API_KEY;
|
|
49
|
+
if (!apiKey || apiKey === DEFAULT_API_KEY_PLACEHOLDER) {
|
|
50
|
+
missingVars.push("RERANK_API_KEY");
|
|
51
|
+
}
|
|
52
|
+
if (!process.env.RERANK_BASE_URL) {
|
|
53
|
+
missingVars.push("RERANK_BASE_URL");
|
|
54
|
+
}
|
|
55
|
+
if (!process.env.RERANK_MODEL) {
|
|
56
|
+
missingVars.push("RERANK_MODEL");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
isValid: missingVars.length === 0,
|
|
60
|
+
missingVars
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function getEmbeddingConfig() {
|
|
64
|
+
const apiKey = process.env.EMBEDDINGS_API_KEY;
|
|
65
|
+
const baseUrl = process.env.EMBEDDINGS_BASE_URL;
|
|
66
|
+
const model = process.env.EMBEDDINGS_MODEL;
|
|
67
|
+
const maxConcurrency = parseInt(process.env.EMBEDDINGS_MAX_CONCURRENCY || "10", 10);
|
|
68
|
+
if (!apiKey) {
|
|
69
|
+
throw new Error("EMBEDDINGS_API_KEY \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
70
|
+
}
|
|
71
|
+
if (!baseUrl) {
|
|
72
|
+
throw new Error("EMBEDDINGS_BASE_URL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
73
|
+
}
|
|
74
|
+
if (!model) {
|
|
75
|
+
throw new Error("EMBEDDINGS_MODEL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
76
|
+
}
|
|
77
|
+
const dimensions = parseInt(process.env.EMBEDDINGS_DIMENSIONS || "1024", 10);
|
|
78
|
+
return {
|
|
79
|
+
apiKey,
|
|
80
|
+
baseUrl,
|
|
81
|
+
model,
|
|
82
|
+
maxConcurrency: Number.isNaN(maxConcurrency) ? 4 : maxConcurrency,
|
|
83
|
+
dimensions: Number.isNaN(dimensions) ? 1024 : dimensions
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function getRerankerConfig() {
|
|
87
|
+
const apiKey = process.env.RERANK_API_KEY;
|
|
88
|
+
const baseUrl = process.env.RERANK_BASE_URL;
|
|
89
|
+
const model = process.env.RERANK_MODEL;
|
|
90
|
+
const topN = parseInt(process.env.RERANK_TOP_N || "10", 10);
|
|
91
|
+
if (!apiKey) {
|
|
92
|
+
throw new Error("RERANK_API_KEY \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
93
|
+
}
|
|
94
|
+
if (!baseUrl) {
|
|
95
|
+
throw new Error("RERANK_BASE_URL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
96
|
+
}
|
|
97
|
+
if (!model) {
|
|
98
|
+
throw new Error("RERANK_MODEL \u73AF\u5883\u53D8\u91CF\u672A\u8BBE\u7F6E");
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
apiKey,
|
|
102
|
+
baseUrl,
|
|
103
|
+
model,
|
|
104
|
+
topN: Number.isNaN(topN) ? 10 : topN
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
var DEFAULT_EXCLUDE_PATTERNS = [
|
|
108
|
+
// --- 1. 依赖与环境 (绝对黑名单) ---
|
|
109
|
+
"node_modules",
|
|
110
|
+
"bower_components",
|
|
111
|
+
".venv",
|
|
112
|
+
"venv",
|
|
113
|
+
// Python 虚拟环境
|
|
114
|
+
".env.*",
|
|
115
|
+
// 环境变量文件 (.env.local, .env.production 等)
|
|
116
|
+
// --- 2. 锁文件 (Token 杀手,且语义密度极低) ---
|
|
117
|
+
"package-lock.json",
|
|
118
|
+
"yarn.lock",
|
|
119
|
+
"pnpm-lock.yaml",
|
|
120
|
+
"bun.lockb",
|
|
121
|
+
"poetry.lock",
|
|
122
|
+
"Gemfile.lock",
|
|
123
|
+
"composer.lock",
|
|
124
|
+
"Cargo.lock",
|
|
125
|
+
// --- 3. 版本控制与 IDE ---
|
|
126
|
+
".git",
|
|
127
|
+
".svn",
|
|
128
|
+
".hg",
|
|
129
|
+
".idea",
|
|
130
|
+
".vscode",
|
|
131
|
+
".vs",
|
|
132
|
+
// --- 4. 构建产物与缓存 ---
|
|
133
|
+
// 通用构建输出
|
|
134
|
+
"dist",
|
|
135
|
+
"build",
|
|
136
|
+
"out",
|
|
137
|
+
"target",
|
|
138
|
+
// 编译产物
|
|
139
|
+
"*.pyc",
|
|
140
|
+
"*.pyo",
|
|
141
|
+
"*.pyd",
|
|
142
|
+
"*.so",
|
|
143
|
+
"*.dll",
|
|
144
|
+
"*.exe",
|
|
145
|
+
"*.bin",
|
|
146
|
+
"*.wasm",
|
|
147
|
+
// 现代前端框架产物
|
|
148
|
+
".next",
|
|
149
|
+
".nuxt",
|
|
150
|
+
".output",
|
|
151
|
+
".svelte-kit",
|
|
152
|
+
// Bundler 缓存
|
|
153
|
+
".turbo",
|
|
154
|
+
".parcel-cache",
|
|
155
|
+
".webpack",
|
|
156
|
+
".esbuild",
|
|
157
|
+
".rollup.cache",
|
|
158
|
+
// 测试覆盖率
|
|
159
|
+
"coverage",
|
|
160
|
+
".nyc_output",
|
|
161
|
+
// Python 缓存
|
|
162
|
+
"__pycache__",
|
|
163
|
+
".pytest_cache",
|
|
164
|
+
".mypy_cache",
|
|
165
|
+
".tox",
|
|
166
|
+
".eggs",
|
|
167
|
+
"*.egg-info",
|
|
168
|
+
// --- 5. 纯噪音文件 (无文本语义) ---
|
|
169
|
+
// 压缩文件与 SourceMap
|
|
170
|
+
"*.min.js",
|
|
171
|
+
"*.min.css",
|
|
172
|
+
"*.map",
|
|
173
|
+
// 图片与多媒体
|
|
174
|
+
"*.svg",
|
|
175
|
+
"*.png",
|
|
176
|
+
"*.jpg",
|
|
177
|
+
"*.jpeg",
|
|
178
|
+
"*.gif",
|
|
179
|
+
"*.ico",
|
|
180
|
+
"*.webp",
|
|
181
|
+
"*.bmp",
|
|
182
|
+
"*.pdf",
|
|
183
|
+
"*.mp3",
|
|
184
|
+
"*.mp4",
|
|
185
|
+
"*.wav",
|
|
186
|
+
"*.webm",
|
|
187
|
+
"*.ogg",
|
|
188
|
+
"*.flac",
|
|
189
|
+
// 字体文件
|
|
190
|
+
"*.woff",
|
|
191
|
+
"*.woff2",
|
|
192
|
+
"*.ttf",
|
|
193
|
+
"*.eot",
|
|
194
|
+
"*.otf",
|
|
195
|
+
// 压缩包
|
|
196
|
+
"*.zip",
|
|
197
|
+
"*.tar",
|
|
198
|
+
"*.gz",
|
|
199
|
+
"*.rar",
|
|
200
|
+
"*.7z",
|
|
201
|
+
// 系统垃圾
|
|
202
|
+
".DS_Store",
|
|
203
|
+
"Thumbs.db",
|
|
204
|
+
// --- 6. 测试噪音 (保留 *.test.ts,但剔除这些) ---
|
|
205
|
+
// Jest 快照
|
|
206
|
+
"__snapshots__",
|
|
207
|
+
"*.snap",
|
|
208
|
+
// 测试夹具与数据
|
|
209
|
+
"test/fixtures",
|
|
210
|
+
"tests/fixtures",
|
|
211
|
+
"__fixtures__",
|
|
212
|
+
"test/data",
|
|
213
|
+
"tests/data",
|
|
214
|
+
"testdata",
|
|
215
|
+
"test-data",
|
|
216
|
+
"testutils",
|
|
217
|
+
// Mock 数据
|
|
218
|
+
"mock",
|
|
219
|
+
"mocks",
|
|
220
|
+
"__mocks__",
|
|
221
|
+
"stub",
|
|
222
|
+
"stubs",
|
|
223
|
+
// --- 7. 第三方与生成文件 ---
|
|
224
|
+
// 第三方依赖目录
|
|
225
|
+
"vendor",
|
|
226
|
+
"vendors",
|
|
227
|
+
"third_party",
|
|
228
|
+
"thirdparty",
|
|
229
|
+
"3rdparty",
|
|
230
|
+
"external",
|
|
231
|
+
"externals",
|
|
232
|
+
// 生成文件
|
|
233
|
+
"generated",
|
|
234
|
+
"gen",
|
|
235
|
+
"auto-generated",
|
|
236
|
+
"*.generated.ts",
|
|
237
|
+
"*.generated.js",
|
|
238
|
+
"*.pb.go",
|
|
239
|
+
"*.pb.ts",
|
|
240
|
+
// protobuf 生成
|
|
241
|
+
// --- 8. 日志与临时文件 ---
|
|
242
|
+
"*.log",
|
|
243
|
+
".cache",
|
|
244
|
+
".tmp",
|
|
245
|
+
"tmp"
|
|
246
|
+
];
|
|
247
|
+
function getExcludePatterns() {
|
|
248
|
+
const envPatterns = process.env.IGNORE_PATTERNS;
|
|
249
|
+
const patterns = [...DEFAULT_EXCLUDE_PATTERNS];
|
|
250
|
+
if (envPatterns) {
|
|
251
|
+
const additional = envPatterns.split(",").map((p) => p.trim()).filter(Boolean);
|
|
252
|
+
patterns.push(...additional);
|
|
253
|
+
}
|
|
254
|
+
return patterns;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export {
|
|
258
|
+
isDev,
|
|
259
|
+
isMcpMode,
|
|
260
|
+
checkEmbeddingEnv,
|
|
261
|
+
checkRerankerEnv,
|
|
262
|
+
getEmbeddingConfig,
|
|
263
|
+
getRerankerConfig,
|
|
264
|
+
getExcludePatterns
|
|
265
|
+
};
|
|
266
|
+
//# sourceMappingURL=chunk-RJURH22T.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
codebaseRetrievalSchema,
|
|
3
|
+
handleCodebaseRetrieval
|
|
4
|
+
} from "./chunk-EZG4H4MN.js";
|
|
5
|
+
import "./chunk-B6OWNBOD.js";
|
|
6
|
+
import "./chunk-AMQQK4P7.js";
|
|
7
|
+
import "./chunk-RJURH22T.js";
|
|
8
|
+
export {
|
|
9
|
+
codebaseRetrievalSchema,
|
|
10
|
+
handleCodebaseRetrieval
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=codebaseRetrieval-IC44RHCL.js.map
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {
|
|
2
|
+
checkEmbeddingEnv,
|
|
3
|
+
checkRerankerEnv,
|
|
4
|
+
getEmbeddingConfig,
|
|
5
|
+
getExcludePatterns,
|
|
6
|
+
getRerankerConfig,
|
|
7
|
+
isDev,
|
|
8
|
+
isMcpMode
|
|
9
|
+
} from "./chunk-RJURH22T.js";
|
|
10
|
+
export {
|
|
11
|
+
checkEmbeddingEnv,
|
|
12
|
+
checkRerankerEnv,
|
|
13
|
+
getEmbeddingConfig,
|
|
14
|
+
getExcludePatterns,
|
|
15
|
+
getRerankerConfig,
|
|
16
|
+
isDev,
|
|
17
|
+
isMcpMode
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=config-BWZ6CU3W.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
scan
|
|
4
|
+
} from "./chunk-2CY5SYBI.js";
|
|
5
|
+
import "./chunk-HR5KUQSM.js";
|
|
6
|
+
import {
|
|
7
|
+
generateProjectId
|
|
8
|
+
} from "./chunk-B6OWNBOD.js";
|
|
9
|
+
import {
|
|
10
|
+
logger
|
|
11
|
+
} from "./chunk-AMQQK4P7.js";
|
|
12
|
+
import "./chunk-RJURH22T.js";
|
|
13
|
+
|
|
14
|
+
// src/index.ts
|
|
15
|
+
import { promises as fs } from "fs";
|
|
16
|
+
import os from "os";
|
|
17
|
+
import path from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
19
|
+
import cac from "cac";
|
|
20
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
var pkgPath = path.resolve(__dirname, "../package.json");
|
|
22
|
+
var pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
|
|
23
|
+
var cli = cac("contextweaver");
|
|
24
|
+
if (process.argv.includes("-v") || process.argv.includes("--version")) {
|
|
25
|
+
console.log(pkg.version);
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
cli.command("init", "\u521D\u59CB\u5316 ContextWeaver \u914D\u7F6E").action(async () => {
|
|
29
|
+
const configDir = path.join(os.homedir(), ".contextweaver");
|
|
30
|
+
const envFile = path.join(configDir, ".env");
|
|
31
|
+
logger.info("\u5F00\u59CB\u521D\u59CB\u5316 ContextWeaver...");
|
|
32
|
+
try {
|
|
33
|
+
await fs.mkdir(configDir, { recursive: true });
|
|
34
|
+
logger.info(`\u521B\u5EFA\u914D\u7F6E\u76EE\u5F55: ${configDir}`);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const error = err;
|
|
37
|
+
if (error.code !== "EEXIST") {
|
|
38
|
+
logger.error({ err, stack: error.stack }, `\u521B\u5EFA\u914D\u7F6E\u76EE\u5F55\u5931\u8D25: ${error.message}`);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
logger.info(`\u914D\u7F6E\u76EE\u5F55\u5DF2\u5B58\u5728: ${configDir}`);
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
await fs.access(envFile);
|
|
45
|
+
logger.warn(`.env \u6587\u4EF6\u5DF2\u5B58\u5728: ${envFile}`);
|
|
46
|
+
logger.info("\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
|
|
47
|
+
return;
|
|
48
|
+
} catch {
|
|
49
|
+
}
|
|
50
|
+
const defaultEnvContent = `# ContextWeaver \u793A\u4F8B\u73AF\u5883\u53D8\u91CF\u914D\u7F6E\u6587\u4EF6
|
|
51
|
+
|
|
52
|
+
# Embedding API \u914D\u7F6E\uFF08\u5FC5\u9700\uFF09
|
|
53
|
+
EMBEDDINGS_API_KEY=your-api-key-here
|
|
54
|
+
EMBEDDINGS_BASE_URL=https://api.siliconflow.cn/v1/embeddings
|
|
55
|
+
EMBEDDINGS_MODEL=BAAI/bge-m3
|
|
56
|
+
EMBEDDINGS_MAX_CONCURRENCY=10
|
|
57
|
+
EMBEDDINGS_DIMENSIONS=1024
|
|
58
|
+
|
|
59
|
+
# Reranker \u914D\u7F6E\uFF08\u5FC5\u9700\uFF09
|
|
60
|
+
RERANK_API_KEY=your-api-key-here
|
|
61
|
+
RERANK_BASE_URL=https://api.siliconflow.cn/v1/rerank
|
|
62
|
+
RERANK_MODEL=BAAI/bge-reranker-v2-m3
|
|
63
|
+
RERANK_TOP_N=20
|
|
64
|
+
|
|
65
|
+
# \u7D22\u5F15\u5FFD\u7565\u6A21\u5F0F\uFF08\u53EF\u9009\uFF0C\u9017\u53F7\u5206\u9694\uFF0C\u9ED8\u8BA4\u5DF2\u5305\u542B\u5E38\u89C1\u5FFD\u7565\u9879\uFF09
|
|
66
|
+
# IGNORE_PATTERNS=.venv,node_modules
|
|
67
|
+
`;
|
|
68
|
+
try {
|
|
69
|
+
await fs.writeFile(envFile, defaultEnvContent);
|
|
70
|
+
logger.info(`\u521B\u5EFA .env \u6587\u4EF6: ${envFile}`);
|
|
71
|
+
} catch (err) {
|
|
72
|
+
const error = err;
|
|
73
|
+
logger.error({ err, stack: error.stack }, `\u521B\u5EFA .env \u6587\u4EF6\u5931\u8D25: ${error.message}`);
|
|
74
|
+
process.exit(1);
|
|
75
|
+
}
|
|
76
|
+
logger.info("\u4E0B\u4E00\u6B65\u64CD\u4F5C:");
|
|
77
|
+
logger.info(` 1. \u7F16\u8F91\u914D\u7F6E\u6587\u4EF6: ${envFile}`);
|
|
78
|
+
logger.info(" 2. \u586B\u5199\u4F60\u7684 API Key \u548C\u5176\u4ED6\u914D\u7F6E");
|
|
79
|
+
logger.info("\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
|
|
80
|
+
});
|
|
81
|
+
cli.command("index [path]", "\u626B\u63CF\u4EE3\u7801\u5E93\u5E76\u5EFA\u7ACB\u7D22\u5F15").option("-f, --force", "\u5F3A\u5236\u91CD\u65B0\u7D22\u5F15").action(async (targetPath, options) => {
|
|
82
|
+
const rootPath = targetPath ? path.resolve(targetPath) : process.cwd();
|
|
83
|
+
const projectId = generateProjectId(rootPath);
|
|
84
|
+
logger.info(`\u5F00\u59CB\u626B\u63CF: ${rootPath}`);
|
|
85
|
+
logger.info(`\u9879\u76EE ID: ${projectId}`);
|
|
86
|
+
if (options.force) {
|
|
87
|
+
logger.info("\u5F3A\u5236\u91CD\u65B0\u7D22\u5F15: \u662F");
|
|
88
|
+
}
|
|
89
|
+
const startTime = Date.now();
|
|
90
|
+
try {
|
|
91
|
+
const { withLock } = await import("./lock-DVY3KJSK.js");
|
|
92
|
+
let lastLoggedPercent = 0;
|
|
93
|
+
const stats = await withLock(
|
|
94
|
+
projectId,
|
|
95
|
+
"index",
|
|
96
|
+
async () => scan(rootPath, {
|
|
97
|
+
force: options.force,
|
|
98
|
+
onProgress: (current, total, message) => {
|
|
99
|
+
if (total !== void 0) {
|
|
100
|
+
const percent = Math.floor(current / total * 100);
|
|
101
|
+
if (percent >= lastLoggedPercent + 30 && percent < 100) {
|
|
102
|
+
logger.info(`\u7D22\u5F15\u8FDB\u5EA6: ${percent}% - ${message || ""}`);
|
|
103
|
+
lastLoggedPercent = Math.floor(percent / 30) * 30;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}),
|
|
108
|
+
10 * 60 * 1e3
|
|
109
|
+
);
|
|
110
|
+
process.stdout.write("\n");
|
|
111
|
+
const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
|
|
112
|
+
logger.info(`\u7D22\u5F15\u5B8C\u6210 (${duration}s)`);
|
|
113
|
+
logger.info(
|
|
114
|
+
`\u603B\u6570:${stats.totalFiles} \u65B0\u589E:${stats.added} \u4FEE\u6539:${stats.modified} \u672A\u53D8:${stats.unchanged} \u5220\u9664:${stats.deleted} \u8DF3\u8FC7:${stats.skipped} \u9519\u8BEF:${stats.errors}`
|
|
115
|
+
);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
const error = err;
|
|
118
|
+
logger.error({ err, stack: error.stack }, `\u7D22\u5F15\u5931\u8D25: ${error.message}`);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
cli.command("mcp", "\u542F\u52A8 MCP \u670D\u52A1\u5668").action(async () => {
|
|
123
|
+
const { startMcpServer } = await import("./server-PPQUHCUB.js");
|
|
124
|
+
try {
|
|
125
|
+
await startMcpServer();
|
|
126
|
+
} catch (err) {
|
|
127
|
+
const error = err;
|
|
128
|
+
logger.error(
|
|
129
|
+
{ error: error.message, stack: error.stack },
|
|
130
|
+
`MCP \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: ${error.message}`
|
|
131
|
+
);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
cli.command("search", "\u672C\u5730\u68C0\u7D22\uFF08\u53C2\u6570\u5BF9\u9F50 MCP\uFF09").option("--repo-path <path>", "\u4EE3\u7801\u5E93\u6839\u76EE\u5F55\uFF08\u9ED8\u8BA4\u5F53\u524D\u76EE\u5F55\uFF09").option("--information-request <text>", "\u81EA\u7136\u8BED\u8A00\u95EE\u9898\u63CF\u8FF0\uFF08\u5FC5\u586B\uFF09").option("--technical-terms <terms>", "\u7CBE\u786E\u672F\u8BED\uFF08\u9017\u53F7\u5206\u9694\uFF09").action(
|
|
136
|
+
async (options) => {
|
|
137
|
+
const repoPath = options.repoPath ? path.resolve(options.repoPath) : process.cwd();
|
|
138
|
+
const informationRequest = options.informationRequest;
|
|
139
|
+
if (!informationRequest) {
|
|
140
|
+
logger.error("\u7F3A\u5C11 --information-request");
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
const technicalTerms = (options.technicalTerms || "").split(",").map((t) => t.trim()).filter(Boolean);
|
|
144
|
+
const { handleCodebaseRetrieval } = await import("./codebaseRetrieval-IC44RHCL.js");
|
|
145
|
+
const response = await handleCodebaseRetrieval({
|
|
146
|
+
repo_path: repoPath,
|
|
147
|
+
information_request: informationRequest,
|
|
148
|
+
technical_terms: technicalTerms.length > 0 ? technicalTerms : void 0
|
|
149
|
+
});
|
|
150
|
+
const text = response.content.map((item) => item.text).join("\n");
|
|
151
|
+
process.stdout.write(`${text}
|
|
152
|
+
`);
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
cli.help();
|
|
156
|
+
cli.parse();
|
|
157
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {
|
|
2
|
+
logger
|
|
3
|
+
} from "./chunk-AMQQK4P7.js";
|
|
4
|
+
import "./chunk-RJURH22T.js";
|
|
5
|
+
|
|
6
|
+
// src/utils/lock.ts
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import os from "os";
|
|
9
|
+
import path from "path";
|
|
10
|
+
var BASE_DIR = path.join(os.homedir(), ".contextweaver");
|
|
11
|
+
var LOCK_CHECK_INTERVAL_MS = 100;
|
|
12
|
+
var LOCK_WRITE_GRACE_MS = 2e3;
|
|
13
|
+
function getLockAgeMs(lockPath) {
|
|
14
|
+
try {
|
|
15
|
+
const stats = fs.statSync(lockPath);
|
|
16
|
+
return Date.now() - stats.mtimeMs;
|
|
17
|
+
} catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function getLockFilePath(projectId) {
|
|
22
|
+
return path.join(BASE_DIR, projectId, "index.lock");
|
|
23
|
+
}
|
|
24
|
+
function isLockValid(lockPath) {
|
|
25
|
+
try {
|
|
26
|
+
if (!fs.existsSync(lockPath)) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const content = fs.readFileSync(lockPath, "utf-8");
|
|
30
|
+
const lockInfo = JSON.parse(content);
|
|
31
|
+
try {
|
|
32
|
+
process.kill(lockInfo.pid, 0);
|
|
33
|
+
return true;
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const error = err;
|
|
36
|
+
if (error.code === "EPERM") {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
logger.warn({ pid: lockInfo.pid }, "\u6301\u6709\u9501\u7684\u8FDB\u7A0B\u5DF2\u6B7B\u4EA1");
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
} catch (err) {
|
|
43
|
+
const ageMs = getLockAgeMs(lockPath);
|
|
44
|
+
if (ageMs !== null && ageMs <= LOCK_WRITE_GRACE_MS) {
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
const error = err;
|
|
48
|
+
logger.debug({ error: error.message }, "\u8BFB\u53D6\u9501\u6587\u4EF6\u5931\u8D25");
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function acquireLock(projectId, operation, timeoutMs = 3e4) {
|
|
53
|
+
const lockPath = getLockFilePath(projectId);
|
|
54
|
+
const dir = path.dirname(lockPath);
|
|
55
|
+
if (!fs.existsSync(dir)) {
|
|
56
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
const startTime = Date.now();
|
|
59
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
60
|
+
try {
|
|
61
|
+
const lockInfo = {
|
|
62
|
+
pid: process.pid,
|
|
63
|
+
timestamp: Date.now(),
|
|
64
|
+
operation
|
|
65
|
+
};
|
|
66
|
+
fs.writeFileSync(lockPath, JSON.stringify(lockInfo), { flag: "wx" });
|
|
67
|
+
logger.debug({ projectId: projectId.slice(0, 10), operation }, "\u83B7\u53D6\u9501\u6210\u529F");
|
|
68
|
+
return true;
|
|
69
|
+
} catch (err) {
|
|
70
|
+
const error = err;
|
|
71
|
+
if (error.code === "EEXIST") {
|
|
72
|
+
if (!isLockValid(lockPath)) {
|
|
73
|
+
try {
|
|
74
|
+
fs.unlinkSync(lockPath);
|
|
75
|
+
logger.warn({ projectId: projectId.slice(0, 10) }, "\u79FB\u9664\u5931\u6548\u9501");
|
|
76
|
+
continue;
|
|
77
|
+
} catch (unlinkErr) {
|
|
78
|
+
const unlinkError = unlinkErr;
|
|
79
|
+
if (unlinkError.code !== "ENOENT") {
|
|
80
|
+
logger.debug({ error: unlinkError.message }, "\u79FB\u9664\u5931\u6548\u9501\u5931\u8D25\uFF0C\u91CD\u8BD5\u4E2D...");
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
} else {
|
|
84
|
+
logger.debug({ projectId: projectId.slice(0, 10) }, "\u7B49\u5F85\u9501\u91CA\u653E...");
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
logger.debug({ error: error.message }, "\u83B7\u53D6\u9501\u5931\u8D25\uFF0C\u91CD\u8BD5\u4E2D...");
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_CHECK_INTERVAL_MS));
|
|
91
|
+
}
|
|
92
|
+
logger.warn({ projectId: projectId.slice(0, 10), timeoutMs }, "\u83B7\u53D6\u9501\u8D85\u65F6");
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
function releaseLock(projectId) {
|
|
96
|
+
const lockPath = getLockFilePath(projectId);
|
|
97
|
+
try {
|
|
98
|
+
if (!fs.existsSync(lockPath)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
const content = fs.readFileSync(lockPath, "utf-8");
|
|
102
|
+
const lockInfo = JSON.parse(content);
|
|
103
|
+
if (lockInfo.pid === process.pid) {
|
|
104
|
+
fs.unlinkSync(lockPath);
|
|
105
|
+
logger.debug({ projectId: projectId.slice(0, 10) }, "\u91CA\u653E\u9501\u6210\u529F");
|
|
106
|
+
} else {
|
|
107
|
+
logger.warn({ ownPid: process.pid, lockPid: lockInfo.pid }, "\u5C1D\u8BD5\u91CA\u653E\u975E\u81EA\u5DF1\u6301\u6709\u7684\u9501");
|
|
108
|
+
}
|
|
109
|
+
} catch (err) {
|
|
110
|
+
const error = err;
|
|
111
|
+
logger.debug({ error: error.message }, "\u91CA\u653E\u9501\u65F6\u51FA\u9519");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function withLock(projectId, operation, fn, timeoutMs = 3e4) {
|
|
115
|
+
const acquired = await acquireLock(projectId, operation, timeoutMs);
|
|
116
|
+
if (!acquired) {
|
|
117
|
+
throw new Error(`\u65E0\u6CD5\u83B7\u53D6\u9879\u76EE\u9501 (${projectId.slice(0, 10)})\uFF0C\u5176\u4ED6\u8FDB\u7A0B\u6B63\u5728\u64CD\u4F5C\u7D22\u5F15`);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
return await fn();
|
|
121
|
+
} finally {
|
|
122
|
+
releaseLock(projectId);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export {
|
|
126
|
+
withLock
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=lock-DVY3KJSK.js.map
|