@reconcrap/boss-recruit-mcp 1.0.17 → 1.0.19
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/package.json +1 -1
- package/src/cli.js +33 -8
- package/src/index.js +77 -21
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -133,14 +133,39 @@ function writeInstalledSkillVersion(version) {
|
|
|
133
133
|
fs.writeFileSync(markerPath, `${version}\n`, "utf8");
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
-
function getChromeUserDataDir(port, options = {}) {
|
|
137
|
-
const rawProvided = options.userDataDir ?? options["user-data-dir"];
|
|
138
|
-
const provided = typeof rawProvided === "string" ? rawProvided.trim() : "";
|
|
139
|
-
const basePath = provided ||
|
|
140
|
-
const targetPath = path.resolve(basePath);
|
|
141
|
-
ensureDir(targetPath);
|
|
142
|
-
return targetPath;
|
|
143
|
-
}
|
|
136
|
+
function getChromeUserDataDir(port, options = {}) {
|
|
137
|
+
const rawProvided = options.userDataDir ?? options["user-data-dir"];
|
|
138
|
+
const provided = typeof rawProvided === "string" ? rawProvided.trim() : "";
|
|
139
|
+
const basePath = provided || resolveDefaultChromeUserDataDir(port);
|
|
140
|
+
const targetPath = path.resolve(basePath);
|
|
141
|
+
ensureDir(targetPath);
|
|
142
|
+
return targetPath;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function getSharedChromeUserDataDir(port) {
|
|
146
|
+
return path.join(getCodexHome(), "boss-mcp", `chrome-profile-${port}`);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getLegacyRecruitChromeUserDataDir(port) {
|
|
150
|
+
return path.join(getCodexHome(), "boss-recruit-mcp", `chrome-profile-${port}`);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function getLegacyRecommendChromeUserDataDir(port) {
|
|
154
|
+
return path.join(os.homedir(), ".boss-recommend-mcp", `chrome-profile-${port}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function resolveDefaultChromeUserDataDir(port) {
|
|
158
|
+
const sharedPath = getSharedChromeUserDataDir(port);
|
|
159
|
+
if (pathExists(sharedPath)) {
|
|
160
|
+
return sharedPath;
|
|
161
|
+
}
|
|
162
|
+
const legacyPaths = [
|
|
163
|
+
getLegacyRecruitChromeUserDataDir(port),
|
|
164
|
+
getLegacyRecommendChromeUserDataDir(port)
|
|
165
|
+
];
|
|
166
|
+
const legacyExisting = legacyPaths.find((candidate) => pathExists(candidate));
|
|
167
|
+
return legacyExisting || sharedPath;
|
|
168
|
+
}
|
|
144
169
|
|
|
145
170
|
function parseOptions(args) {
|
|
146
171
|
const options = {};
|
package/src/index.js
CHANGED
|
@@ -8,11 +8,18 @@ const require = createRequire(import.meta.url);
|
|
|
8
8
|
const { version: SERVER_VERSION } = require("../package.json");
|
|
9
9
|
const TOOL_NAME = "run_recruit_pipeline";
|
|
10
10
|
const SERVER_NAME = "boss-recruit-mcp";
|
|
11
|
+
const FRAMING_UNKNOWN = "unknown";
|
|
12
|
+
const FRAMING_HEADER = "header";
|
|
13
|
+
const FRAMING_LINE = "line";
|
|
11
14
|
|
|
12
|
-
function writeMessage(message) {
|
|
15
|
+
function writeMessage(message, framing = FRAMING_LINE) {
|
|
13
16
|
const body = JSON.stringify(message);
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
if (framing === FRAMING_HEADER) {
|
|
18
|
+
const header = `Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n`;
|
|
19
|
+
process.stdout.write(header + body);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
process.stdout.write(`${body}\n`);
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
function createJsonRpcError(id, code, message) {
|
|
@@ -178,42 +185,91 @@ export function startServer() {
|
|
|
178
185
|
const mcpRoot = path.resolve(path.dirname(thisFile), "..");
|
|
179
186
|
const workspaceRoot = envRoot ? path.resolve(envRoot) : path.resolve(mcpRoot, "..");
|
|
180
187
|
let buffer = Buffer.alloc(0);
|
|
188
|
+
let framing = FRAMING_UNKNOWN;
|
|
181
189
|
|
|
182
190
|
process.stdin.on("data", async (chunk) => {
|
|
183
191
|
buffer = Buffer.concat([buffer, chunk]);
|
|
192
|
+
if (buffer.length >= 3 && buffer[0] === 0xef && buffer[1] === 0xbb && buffer[2] === 0xbf) {
|
|
193
|
+
buffer = buffer.slice(3);
|
|
194
|
+
}
|
|
184
195
|
|
|
185
196
|
while (true) {
|
|
186
|
-
const
|
|
187
|
-
|
|
197
|
+
const crlfHeaderEnd = buffer.indexOf("\r\n\r\n");
|
|
198
|
+
const lfHeaderEnd = buffer.indexOf("\n\n");
|
|
199
|
+
const crHeaderEnd = buffer.indexOf("\r\r");
|
|
200
|
+
let headerEnd = -1;
|
|
201
|
+
let headerSeparatorLength = 0;
|
|
202
|
+
if (
|
|
203
|
+
crlfHeaderEnd !== -1
|
|
204
|
+
&& (lfHeaderEnd === -1 || crlfHeaderEnd < lfHeaderEnd)
|
|
205
|
+
&& (crHeaderEnd === -1 || crlfHeaderEnd < crHeaderEnd)
|
|
206
|
+
) {
|
|
207
|
+
headerEnd = crlfHeaderEnd;
|
|
208
|
+
headerSeparatorLength = 4;
|
|
209
|
+
} else if (lfHeaderEnd !== -1 && (crHeaderEnd === -1 || lfHeaderEnd < crHeaderEnd)) {
|
|
210
|
+
headerEnd = lfHeaderEnd;
|
|
211
|
+
headerSeparatorLength = 2;
|
|
212
|
+
} else if (crHeaderEnd !== -1) {
|
|
213
|
+
headerEnd = crHeaderEnd;
|
|
214
|
+
headerSeparatorLength = 2;
|
|
215
|
+
}
|
|
216
|
+
if (headerEnd !== -1) {
|
|
217
|
+
const headerText = buffer.slice(0, headerEnd).toString("utf8");
|
|
218
|
+
const contentLengthLine = headerText
|
|
219
|
+
.split(/\r\n|\n|\r/)
|
|
220
|
+
.find((line) => line.toLowerCase().startsWith("content-length:"));
|
|
188
221
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
222
|
+
if (!contentLengthLine) {
|
|
223
|
+
buffer = buffer.slice(headerEnd + headerSeparatorLength);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const contentLength = Number.parseInt(contentLengthLine.split(":")[1].trim(), 10);
|
|
228
|
+
if (!Number.isFinite(contentLength) || contentLength < 0) {
|
|
229
|
+
buffer = buffer.slice(headerEnd + headerSeparatorLength);
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
193
232
|
|
|
194
|
-
|
|
195
|
-
|
|
233
|
+
const bodyStart = headerEnd + headerSeparatorLength;
|
|
234
|
+
const bodyEnd = bodyStart + contentLength;
|
|
235
|
+
if (buffer.length < bodyEnd) break;
|
|
236
|
+
|
|
237
|
+
const body = buffer.slice(bodyStart, bodyEnd).toString("utf8");
|
|
238
|
+
buffer = buffer.slice(bodyEnd);
|
|
239
|
+
framing = FRAMING_HEADER;
|
|
240
|
+
|
|
241
|
+
let message;
|
|
242
|
+
try {
|
|
243
|
+
message = JSON.parse(body);
|
|
244
|
+
} catch {
|
|
245
|
+
writeMessage(createJsonRpcError(null, -32700, "Parse error"), FRAMING_HEADER);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const response = await handleRequest(message, workspaceRoot);
|
|
250
|
+
if (response) writeMessage(response, framing);
|
|
196
251
|
continue;
|
|
197
252
|
}
|
|
198
253
|
|
|
199
|
-
const
|
|
200
|
-
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
203
|
-
|
|
204
|
-
const
|
|
205
|
-
|
|
254
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
255
|
+
if (newlineIndex === -1) break;
|
|
256
|
+
const rawLine = buffer.slice(0, newlineIndex).toString("utf8").replace(/\r$/, "");
|
|
257
|
+
if (/^\s*content-length:/i.test(rawLine)) break;
|
|
258
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
259
|
+
const line = rawLine.trim();
|
|
260
|
+
if (!line) continue;
|
|
261
|
+
framing = FRAMING_LINE;
|
|
206
262
|
|
|
207
263
|
let message;
|
|
208
264
|
try {
|
|
209
|
-
message = JSON.parse(
|
|
265
|
+
message = JSON.parse(line);
|
|
210
266
|
} catch {
|
|
211
|
-
writeMessage(createJsonRpcError(null, -32700, "Parse error"));
|
|
267
|
+
writeMessage(createJsonRpcError(null, -32700, "Parse error"), FRAMING_LINE);
|
|
212
268
|
continue;
|
|
213
269
|
}
|
|
214
270
|
|
|
215
271
|
const response = await handleRequest(message, workspaceRoot);
|
|
216
|
-
if (response) writeMessage(response);
|
|
272
|
+
if (response) writeMessage(response, framing);
|
|
217
273
|
}
|
|
218
274
|
});
|
|
219
275
|
}
|