@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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +33 -8
  3. package/src/index.js +77 -21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reconcrap/boss-recruit-mcp",
3
- "version": "1.0.17",
3
+ "version": "1.0.19",
4
4
  "description": "Unified MCP pipeline for boss-search-cli and boss-screen-cli",
5
5
  "keywords": [
6
6
  "boss",
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 || path.join(getCodexHome(), "boss-recruit-mcp", `chrome-profile-${port}`);
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
- const header = `Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n`;
15
- process.stdout.write(header + body);
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 headerEnd = buffer.indexOf("\r\n\r\n");
187
- if (headerEnd === -1) break;
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
- const headerText = buffer.slice(0, headerEnd).toString("utf8");
190
- const contentLengthLine = headerText
191
- .split("\r\n")
192
- .find((line) => line.toLowerCase().startsWith("content-length:"));
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
- if (!contentLengthLine) {
195
- buffer = buffer.slice(headerEnd + 4);
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 contentLength = Number.parseInt(contentLengthLine.split(":")[1].trim(), 10);
200
- const bodyStart = headerEnd + 4;
201
- const bodyEnd = bodyStart + contentLength;
202
- if (buffer.length < bodyEnd) break;
203
-
204
- const body = buffer.slice(bodyStart, bodyEnd).toString("utf8");
205
- buffer = buffer.slice(bodyEnd);
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(body);
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
  }