@lyy0709/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,170 @@
1
+ import {
2
+ isDev,
3
+ isMcpMode
4
+ } from "./chunk-V2USKRIC.js";
5
+
6
+ // src/utils/logger.ts
7
+ import fs from "fs";
8
+ import os from "os";
9
+ import path from "path";
10
+ import { Writable } from "stream";
11
+ import pino from "pino";
12
+ var logLevel = isDev ? "debug" : "info";
13
+ var logDir = path.join(os.homedir(), ".contextweaver", "logs");
14
+ var LOG_RETENTION_DAYS = 7;
15
+ function ensureLogDir(dir) {
16
+ if (!fs.existsSync(dir)) {
17
+ fs.mkdirSync(dir, { recursive: true });
18
+ }
19
+ }
20
+ function getLogFileName() {
21
+ const now = /* @__PURE__ */ new Date();
22
+ const dateStr = now.toISOString().split("T")[0];
23
+ return `app.${dateStr}.log`;
24
+ }
25
+ function formatTime() {
26
+ const now = /* @__PURE__ */ new Date();
27
+ const pad = (n) => n.toString().padStart(2, "0");
28
+ return `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())} ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
29
+ }
30
+ function getLevelLabel(level) {
31
+ const labels = {
32
+ 10: "TRACE",
33
+ 20: "DEBUG",
34
+ 30: "INFO",
35
+ 40: "WARN",
36
+ 50: "ERROR",
37
+ 60: "FATAL"
38
+ };
39
+ return labels[level] || "INFO";
40
+ }
41
+ function cleanupOldLogs(dir) {
42
+ try {
43
+ if (!fs.existsSync(dir)) return;
44
+ const files = fs.readdirSync(dir);
45
+ const now = Date.now();
46
+ const maxAge = LOG_RETENTION_DAYS * 24 * 60 * 60 * 1e3;
47
+ const logPattern = /^app\.(\d{4}-\d{2}-\d{2})\.log$/;
48
+ for (const file of files) {
49
+ const match = file.match(logPattern);
50
+ if (!match) continue;
51
+ const dateStr = match[1];
52
+ const fileDate = new Date(dateStr).getTime();
53
+ if (Number.isNaN(fileDate)) continue;
54
+ if (now - fileDate > maxAge) {
55
+ const filePath = path.join(dir, file);
56
+ try {
57
+ fs.unlinkSync(filePath);
58
+ console.error(`[Logger] \u6E05\u7406\u8FC7\u671F\u65E5\u5FD7: ${file}`);
59
+ } catch {
60
+ }
61
+ }
62
+ }
63
+ } catch {
64
+ }
65
+ }
66
+ function createFormattedStream(filePath) {
67
+ const writeStream = fs.createWriteStream(filePath, { flags: "a" });
68
+ return new Writable({
69
+ write(chunk, _encoding, callback) {
70
+ try {
71
+ const log = JSON.parse(chunk.toString());
72
+ const time = formatTime();
73
+ const level = getLevelLabel(log.level);
74
+ const msg = log.msg || "";
75
+ const { level: _l, time: _t, pid: _p, hostname: _h, name: _n, msg: _m, ...extra } = log;
76
+ let line = `${time} [${level}] ${msg}`;
77
+ if (Object.keys(extra).length > 0) {
78
+ line += ` ${JSON.stringify(extra)}`;
79
+ }
80
+ writeStream.write(`${line}
81
+ `, callback);
82
+ } catch {
83
+ writeStream.write(chunk.toString(), callback);
84
+ }
85
+ }
86
+ });
87
+ }
88
+ function createConsoleStream() {
89
+ const colors = {
90
+ 10: "\x1B[90m",
91
+ // TRACE - 灰色
92
+ 20: "\x1B[36m",
93
+ // DEBUG - 青色
94
+ 30: "\x1B[32m",
95
+ // INFO - 绿色
96
+ 40: "\x1B[33m",
97
+ // WARN - 黄色
98
+ 50: "\x1B[31m",
99
+ // ERROR - 红色
100
+ 60: "\x1B[35m"
101
+ // FATAL - 品红
102
+ };
103
+ const reset = "\x1B[0m";
104
+ return new Writable({
105
+ write(chunk, _encoding, callback) {
106
+ try {
107
+ const log = JSON.parse(chunk.toString());
108
+ const time = formatTime();
109
+ const level = getLevelLabel(log.level);
110
+ const color = colors[log.level] || "";
111
+ const msg = log.msg || "";
112
+ const { level: _l, time: _t, pid: _p, hostname: _h, name: _n, msg: _m, ...extra } = log;
113
+ let line = `${color}${time} [${level}]${reset} ${msg}`;
114
+ if (Object.keys(extra).length > 0) {
115
+ const extraStr = JSON.stringify(extra);
116
+ line += ` ${color}${extraStr}${reset}`;
117
+ }
118
+ process.stdout.write(`${line}
119
+ `, callback);
120
+ } catch {
121
+ process.stdout.write(chunk.toString(), callback);
122
+ }
123
+ }
124
+ });
125
+ }
126
+ function createDevLogger() {
127
+ ensureLogDir(logDir);
128
+ cleanupOldLogs(logDir);
129
+ const logPath = path.join(logDir, getLogFileName());
130
+ const logStream = createFormattedStream(logPath);
131
+ const consoleStream = createConsoleStream();
132
+ return pino(
133
+ {
134
+ level: logLevel,
135
+ name: "contextweaver"
136
+ },
137
+ // MCP 模式下禁用控制台输出,避免污染 STDIO 协议流
138
+ isMcpMode ? logStream : pino.multistream([
139
+ { stream: logStream, level: logLevel },
140
+ { stream: consoleStream, level: logLevel }
141
+ ])
142
+ );
143
+ }
144
+ function createProdLogger() {
145
+ ensureLogDir(logDir);
146
+ cleanupOldLogs(logDir);
147
+ const logPath = path.join(logDir, getLogFileName());
148
+ const logStream = createFormattedStream(logPath);
149
+ const consoleStream = createConsoleStream();
150
+ return pino(
151
+ {
152
+ level: logLevel,
153
+ name: "contextweaver"
154
+ },
155
+ // MCP 模式下禁用控制台输出,避免污染 STDIO 协议流
156
+ isMcpMode ? logStream : pino.multistream([
157
+ { stream: logStream, level: logLevel },
158
+ { stream: consoleStream, level: logLevel }
159
+ ])
160
+ );
161
+ }
162
+ var logger = isDev ? createDevLogger() : createProdLogger();
163
+ function isDebugEnabled() {
164
+ return logger.isLevelEnabled("debug");
165
+ }
166
+
167
+ export {
168
+ logger,
169
+ isDebugEnabled
170
+ };
@@ -0,0 +1,42 @@
1
+ // src/enhancer/adapters/claude.ts
2
+ var ClaudeAdapter = class {
3
+ config;
4
+ constructor(config) {
5
+ this.config = config;
6
+ }
7
+ async chat(messages) {
8
+ const system = messages.filter((m) => m.role === "system").map((m) => m.content).join("\n");
9
+ const userAssistantMessages = messages.filter((m) => m.role !== "system").map((m) => ({
10
+ role: m.role === "assistant" ? "assistant" : "user",
11
+ content: [{ type: "text", text: m.content }]
12
+ }));
13
+ const response = await fetch(this.config.baseUrl, {
14
+ method: "POST",
15
+ headers: {
16
+ "x-api-key": this.config.apiKey,
17
+ "anthropic-version": "2023-06-01",
18
+ "Content-Type": "application/json"
19
+ },
20
+ body: JSON.stringify({
21
+ model: this.config.model,
22
+ system,
23
+ messages: userAssistantMessages,
24
+ max_tokens: 4096,
25
+ temperature: 0.7
26
+ })
27
+ });
28
+ const data = await response.json();
29
+ if (!response.ok || data.error) {
30
+ const errorMsg = data.error?.message || `HTTP ${response.status}`;
31
+ throw new Error(`Claude API \u9519\u8BEF: ${errorMsg}`);
32
+ }
33
+ const content = data.content?.[0]?.text;
34
+ if (!content) {
35
+ throw new Error("Claude API \u8FD4\u56DE\u4E3A\u7A7A");
36
+ }
37
+ return content;
38
+ }
39
+ };
40
+ export {
41
+ ClaudeAdapter
42
+ };
@@ -0,0 +1,11 @@
1
+ import {
2
+ codebaseRetrievalSchema,
3
+ handleCodebaseRetrieval
4
+ } from "./chunk-XVKMTPCT.js";
5
+ import "./chunk-LPFRFKFW.js";
6
+ import "./chunk-YVLGQTLG.js";
7
+ import "./chunk-V2USKRIC.js";
8
+ export {
9
+ codebaseRetrievalSchema,
10
+ handleCodebaseRetrieval
11
+ };
@@ -0,0 +1,22 @@
1
+ import {
2
+ checkEmbeddingEnv,
3
+ checkEnhancerEnv,
4
+ checkRerankerEnv,
5
+ getEmbeddingConfig,
6
+ getEnhancerConfig,
7
+ getExcludePatterns,
8
+ getRerankerConfig,
9
+ isDev,
10
+ isMcpMode
11
+ } from "./chunk-V2USKRIC.js";
12
+ export {
13
+ checkEmbeddingEnv,
14
+ checkEnhancerEnv,
15
+ checkRerankerEnv,
16
+ getEmbeddingConfig,
17
+ getEnhancerConfig,
18
+ getExcludePatterns,
19
+ getRerankerConfig,
20
+ isDev,
21
+ isMcpMode
22
+ };
@@ -0,0 +1,8 @@
1
+ import {
2
+ enhancePrompt
3
+ } from "./chunk-6HF343R7.js";
4
+ import "./chunk-YVLGQTLG.js";
5
+ import "./chunk-V2USKRIC.js";
6
+ export {
7
+ enhancePrompt
8
+ };
@@ -0,0 +1,44 @@
1
+ // src/enhancer/adapters/gemini.ts
2
+ var GeminiAdapter = class {
3
+ config;
4
+ constructor(config) {
5
+ this.config = config;
6
+ }
7
+ async chat(messages) {
8
+ const systemInstruction = messages.filter((m) => m.role === "system").map((m) => m.content).join("\n");
9
+ const contents = messages.filter((m) => m.role !== "system").map((m) => ({
10
+ role: m.role === "assistant" ? "model" : "user",
11
+ parts: [{ text: m.content }]
12
+ }));
13
+ const baseUrl = this.config.baseUrl.replace(/\/+$/, "");
14
+ const withoutVersion = baseUrl.replace(/\/v1beta$/, "");
15
+ const url = `${withoutVersion}/v1beta/models/${encodeURIComponent(this.config.model)}:generateContent?key=${encodeURIComponent(this.config.apiKey)}`;
16
+ const response = await fetch(url, {
17
+ method: "POST",
18
+ headers: {
19
+ "Content-Type": "application/json"
20
+ },
21
+ body: JSON.stringify({
22
+ // Gemini accepts systemInstruction at top-level.
23
+ systemInstruction: systemInstruction ? { parts: [{ text: systemInstruction }] } : void 0,
24
+ contents,
25
+ generationConfig: {
26
+ temperature: 0.7
27
+ }
28
+ })
29
+ });
30
+ const data = await response.json();
31
+ if (!response.ok || data.error) {
32
+ const errorMsg = data.error?.message || `HTTP ${response.status}`;
33
+ throw new Error(`Gemini API \u9519\u8BEF: ${errorMsg}`);
34
+ }
35
+ const content = data.candidates?.[0]?.content?.parts?.[0]?.text;
36
+ if (!content) {
37
+ throw new Error("Gemini API \u8FD4\u56DE\u4E3A\u7A7A");
38
+ }
39
+ return content;
40
+ }
41
+ };
42
+ export {
43
+ GeminiAdapter
44
+ };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
package/dist/index.js ADDED
@@ -0,0 +1,202 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ scan
4
+ } from "./chunk-CKN7LWEA.js";
5
+ import "./chunk-ECEVTSSZ.js";
6
+ import {
7
+ generateProjectId
8
+ } from "./chunk-LPFRFKFW.js";
9
+ import {
10
+ logger
11
+ } from "./chunk-YVLGQTLG.js";
12
+ import "./chunk-V2USKRIC.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
+ # Prompt Enhancer \u914D\u7F6E\uFF08\u53EF\u9009\uFF0C\u4F7F\u7528 enhance \u547D\u4EE4\u65F6\u9700\u8981\uFF09
69
+ # PROMPT_ENHANCER_ENDPOINT=openai
70
+ # PROMPT_ENHANCER_BASE_URL=
71
+ # PROMPT_ENHANCER_TOKEN=your-api-key-here
72
+ # PROMPT_ENHANCER_MODEL=
73
+ # PROMPT_ENHANCER_TEMPLATE=
74
+ `;
75
+ try {
76
+ await fs.writeFile(envFile, defaultEnvContent);
77
+ logger.info(`\u521B\u5EFA .env \u6587\u4EF6: ${envFile}`);
78
+ } catch (err) {
79
+ const error = err;
80
+ logger.error({ err, stack: error.stack }, `\u521B\u5EFA .env \u6587\u4EF6\u5931\u8D25: ${error.message}`);
81
+ process.exit(1);
82
+ }
83
+ logger.info("\u4E0B\u4E00\u6B65\u64CD\u4F5C:");
84
+ logger.info(` 1. \u7F16\u8F91\u914D\u7F6E\u6587\u4EF6: ${envFile}`);
85
+ logger.info(" 2. \u586B\u5199\u4F60\u7684 API Key \u548C\u5176\u4ED6\u914D\u7F6E");
86
+ logger.info("\u521D\u59CB\u5316\u5B8C\u6210\uFF01");
87
+ });
88
+ 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) => {
89
+ const rootPath = targetPath ? path.resolve(targetPath) : process.cwd();
90
+ const projectId = generateProjectId(rootPath);
91
+ logger.info(`\u5F00\u59CB\u626B\u63CF: ${rootPath}`);
92
+ logger.info(`\u9879\u76EE ID: ${projectId}`);
93
+ if (options.force) {
94
+ logger.info("\u5F3A\u5236\u91CD\u65B0\u7D22\u5F15: \u662F");
95
+ }
96
+ const startTime = Date.now();
97
+ try {
98
+ let lastLoggedPercent = 0;
99
+ const stats = await scan(rootPath, {
100
+ force: options.force,
101
+ onProgress: (current, total, message) => {
102
+ if (total !== void 0) {
103
+ const percent = Math.floor(current / total * 100);
104
+ if (percent >= lastLoggedPercent + 30 && percent < 100) {
105
+ logger.info(`\u7D22\u5F15\u8FDB\u5EA6: ${percent}% - ${message || ""}`);
106
+ lastLoggedPercent = Math.floor(percent / 30) * 30;
107
+ }
108
+ }
109
+ }
110
+ });
111
+ process.stdout.write("\n");
112
+ const duration = ((Date.now() - startTime) / 1e3).toFixed(2);
113
+ logger.info(`\u7D22\u5F15\u5B8C\u6210 (${duration}s)`);
114
+ logger.info(
115
+ `\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}`
116
+ );
117
+ } catch (err) {
118
+ const error = err;
119
+ logger.error({ err, stack: error.stack }, `\u7D22\u5F15\u5931\u8D25: ${error.message}`);
120
+ process.exit(1);
121
+ }
122
+ });
123
+ cli.command("mcp", "\u542F\u52A8 MCP \u670D\u52A1\u5668").action(async () => {
124
+ const { startMcpServer } = await import("./server-DENFYPME.js");
125
+ try {
126
+ await startMcpServer();
127
+ } catch (err) {
128
+ const error = err;
129
+ logger.error(
130
+ { error: error.message, stack: error.stack },
131
+ `MCP \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: ${error.message}`
132
+ );
133
+ process.exit(1);
134
+ }
135
+ });
136
+ cli.command("enhance <prompt>", "\u589E\u5F3A\u63D0\u793A\u8BCD").option("--no-browser", "\u76F4\u63A5\u8F93\u51FA\u5230\u7EC8\u7AEF\uFF0C\u4E0D\u542F\u52A8\u6D4F\u89C8\u5668").option("--endpoint <type>", "\u6307\u5B9A API \u7AEF\u70B9 (openai/claude/gemini)").action(
137
+ async (prompt, options) => {
138
+ const endpointRaw = options.endpoint?.toLowerCase();
139
+ const endpointOverride = endpointRaw === "openai" || endpointRaw === "claude" || endpointRaw === "gemini" ? endpointRaw : void 0;
140
+ if (options.browser === false) {
141
+ const { enhancePrompt } = await import("./enhancer-QHNMR35J.js");
142
+ try {
143
+ const result = await enhancePrompt({ prompt, endpointOverride });
144
+ process.stdout.write(`${result.enhanced}
145
+ `);
146
+ } catch (err) {
147
+ const error = err;
148
+ logger.error(
149
+ { error: error.message, stack: error.stack },
150
+ `enhance \u5931\u8D25: ${error.message}`
151
+ );
152
+ process.exit(1);
153
+ }
154
+ return;
155
+ }
156
+ const { startEnhanceServer } = await import("./server-276GGS5G.js");
157
+ const { openBrowser } = await import("./browser-VC5772XM.js");
158
+ try {
159
+ const result = await startEnhanceServer(prompt, {
160
+ endpointOverride,
161
+ onStarted: (url) => {
162
+ openBrowser(url);
163
+ }
164
+ });
165
+ process.stdout.write(`${result.enhanced}
166
+ `);
167
+ } catch (err) {
168
+ const error = err;
169
+ logger.error(
170
+ { error: error.message, stack: error.stack },
171
+ `enhance \u5931\u8D25: ${error.message}`
172
+ );
173
+ process.exit(1);
174
+ }
175
+ }
176
+ );
177
+ 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").option("--zen", "\u4F7F\u7528 MCP Zen \u914D\u7F6E\uFF08\u9ED8\u8BA4\u5F00\u542F\uFF09").action(
178
+ async (options) => {
179
+ const repoPath = options.repoPath ? path.resolve(options.repoPath) : process.cwd();
180
+ const informationRequest = options.informationRequest;
181
+ if (!informationRequest) {
182
+ logger.error("\u7F3A\u5C11 --information-request");
183
+ process.exit(1);
184
+ }
185
+ const technicalTerms = (options.technicalTerms || "").split(",").map((t) => t.trim()).filter(Boolean);
186
+ const useZen = options.zen !== false;
187
+ const { handleCodebaseRetrieval } = await import("./codebaseRetrieval-SCN3YIPM.js");
188
+ const response = await handleCodebaseRetrieval(
189
+ {
190
+ repo_path: repoPath,
191
+ information_request: informationRequest,
192
+ technical_terms: technicalTerms.length > 0 ? technicalTerms : void 0
193
+ },
194
+ useZen ? void 0 : {}
195
+ );
196
+ const text = response.content.map((item) => item.text).join("\n");
197
+ process.stdout.write(`${text}
198
+ `);
199
+ }
200
+ );
201
+ cli.help();
202
+ cli.parse();
@@ -0,0 +1,106 @@
1
+ import {
2
+ logger
3
+ } from "./chunk-YVLGQTLG.js";
4
+ import "./chunk-V2USKRIC.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_TIMEOUT_MS = 5 * 60 * 1e3;
12
+ var LOCK_CHECK_INTERVAL_MS = 100;
13
+ function getLockFilePath(projectId) {
14
+ return path.join(BASE_DIR, projectId, "index.lock");
15
+ }
16
+ function isLockValid(lockPath) {
17
+ try {
18
+ if (!fs.existsSync(lockPath)) {
19
+ return false;
20
+ }
21
+ const content = fs.readFileSync(lockPath, "utf-8");
22
+ const lockInfo = JSON.parse(content);
23
+ if (Date.now() - lockInfo.timestamp > LOCK_TIMEOUT_MS) {
24
+ logger.warn({ lockInfo, lockPath }, "\u9501\u5DF2\u8D85\u65F6");
25
+ return false;
26
+ }
27
+ try {
28
+ process.kill(lockInfo.pid, 0);
29
+ return true;
30
+ } catch {
31
+ logger.warn({ pid: lockInfo.pid }, "\u6301\u6709\u9501\u7684\u8FDB\u7A0B\u5DF2\u6B7B\u4EA1");
32
+ return false;
33
+ }
34
+ } catch (err) {
35
+ const error = err;
36
+ logger.debug({ error: error.message }, "\u8BFB\u53D6\u9501\u6587\u4EF6\u5931\u8D25");
37
+ return false;
38
+ }
39
+ }
40
+ async function acquireLock(projectId, operation, timeoutMs = 3e4) {
41
+ const lockPath = getLockFilePath(projectId);
42
+ const dir = path.dirname(lockPath);
43
+ if (!fs.existsSync(dir)) {
44
+ fs.mkdirSync(dir, { recursive: true });
45
+ }
46
+ const startTime = Date.now();
47
+ while (Date.now() - startTime < timeoutMs) {
48
+ if (!isLockValid(lockPath)) {
49
+ try {
50
+ const lockInfo = {
51
+ pid: process.pid,
52
+ timestamp: Date.now(),
53
+ operation
54
+ };
55
+ fs.writeFileSync(lockPath, JSON.stringify(lockInfo), { flag: "w" });
56
+ const verifyContent = fs.readFileSync(lockPath, "utf-8");
57
+ const verifyInfo = JSON.parse(verifyContent);
58
+ if (verifyInfo.pid === process.pid) {
59
+ logger.debug({ projectId: projectId.slice(0, 10), operation }, "\u83B7\u53D6\u9501\u6210\u529F");
60
+ return true;
61
+ }
62
+ } catch (err) {
63
+ const error = err;
64
+ logger.debug({ error: error.message }, "\u83B7\u53D6\u9501\u5931\u8D25\uFF0C\u91CD\u8BD5\u4E2D...");
65
+ }
66
+ } else {
67
+ logger.debug({ projectId: projectId.slice(0, 10) }, "\u7B49\u5F85\u9501\u91CA\u653E...");
68
+ }
69
+ await new Promise((resolve) => setTimeout(resolve, LOCK_CHECK_INTERVAL_MS));
70
+ }
71
+ logger.warn({ projectId: projectId.slice(0, 10), timeoutMs }, "\u83B7\u53D6\u9501\u8D85\u65F6");
72
+ return false;
73
+ }
74
+ function releaseLock(projectId) {
75
+ const lockPath = getLockFilePath(projectId);
76
+ try {
77
+ if (!fs.existsSync(lockPath)) {
78
+ return;
79
+ }
80
+ const content = fs.readFileSync(lockPath, "utf-8");
81
+ const lockInfo = JSON.parse(content);
82
+ if (lockInfo.pid === process.pid) {
83
+ fs.unlinkSync(lockPath);
84
+ logger.debug({ projectId: projectId.slice(0, 10) }, "\u91CA\u653E\u9501\u6210\u529F");
85
+ } else {
86
+ logger.warn({ ownPid: process.pid, lockPid: lockInfo.pid }, "\u5C1D\u8BD5\u91CA\u653E\u975E\u81EA\u5DF1\u6301\u6709\u7684\u9501");
87
+ }
88
+ } catch (err) {
89
+ const error = err;
90
+ logger.debug({ error: error.message }, "\u91CA\u653E\u9501\u65F6\u51FA\u9519");
91
+ }
92
+ }
93
+ async function withLock(projectId, operation, fn, timeoutMs = 3e4) {
94
+ const acquired = await acquireLock(projectId, operation, timeoutMs);
95
+ if (!acquired) {
96
+ 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`);
97
+ }
98
+ try {
99
+ return await fn();
100
+ } finally {
101
+ releaseLock(projectId);
102
+ }
103
+ }
104
+ export {
105
+ withLock
106
+ };
@@ -0,0 +1,9 @@
1
+ import {
2
+ isDebugEnabled,
3
+ logger
4
+ } from "./chunk-YVLGQTLG.js";
5
+ import "./chunk-V2USKRIC.js";
6
+ export {
7
+ isDebugEnabled,
8
+ logger
9
+ };
@@ -0,0 +1 @@
1
+ #!/usr/bin/env node
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/mcp/main.ts
4
+ if (!process.argv.includes("mcp")) {
5
+ process.argv.push("mcp");
6
+ }
7
+ var { logger } = await import("../logger-SF6S6GVR.js");
8
+ var { startMcpServer } = await import("../server-DENFYPME.js");
9
+ try {
10
+ await startMcpServer();
11
+ } catch (err) {
12
+ const error = err;
13
+ logger.error(
14
+ { error: error.message, stack: error.stack },
15
+ `MCP \u670D\u52A1\u5668\u542F\u52A8\u5931\u8D25: ${error.message}`
16
+ );
17
+ process.exit(1);
18
+ }
@@ -0,0 +1,34 @@
1
+ // src/enhancer/adapters/openai.ts
2
+ var OpenAiAdapter = class {
3
+ config;
4
+ constructor(config) {
5
+ this.config = config;
6
+ }
7
+ async chat(messages) {
8
+ const response = await fetch(this.config.baseUrl, {
9
+ method: "POST",
10
+ headers: {
11
+ Authorization: `Bearer ${this.config.apiKey}`,
12
+ "Content-Type": "application/json"
13
+ },
14
+ body: JSON.stringify({
15
+ model: this.config.model,
16
+ messages,
17
+ temperature: 0.7
18
+ })
19
+ });
20
+ const data = await response.json();
21
+ if (!response.ok || data.error) {
22
+ const errorMsg = data.error?.message || `HTTP ${response.status}`;
23
+ throw new Error(`OpenAI API \u9519\u8BEF: ${errorMsg}`);
24
+ }
25
+ const content = data.choices?.[0]?.message?.content;
26
+ if (!content) {
27
+ throw new Error("OpenAI API \u8FD4\u56DE\u4E3A\u7A7A");
28
+ }
29
+ return content;
30
+ }
31
+ };
32
+ export {
33
+ OpenAiAdapter
34
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ scan
3
+ } from "./chunk-CKN7LWEA.js";
4
+ import "./chunk-ECEVTSSZ.js";
5
+ import "./chunk-LPFRFKFW.js";
6
+ import "./chunk-YVLGQTLG.js";
7
+ import "./chunk-V2USKRIC.js";
8
+ export {
9
+ scan
10
+ };