@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.
@@ -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
@@ -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
@@ -0,0 +1,11 @@
1
+ import {
2
+ scan
3
+ } from "./chunk-2CY5SYBI.js";
4
+ import "./chunk-HR5KUQSM.js";
5
+ import "./chunk-B6OWNBOD.js";
6
+ import "./chunk-AMQQK4P7.js";
7
+ import "./chunk-RJURH22T.js";
8
+ export {
9
+ scan
10
+ };
11
+ //# sourceMappingURL=scanner-SZ2BDYDS.js.map