@linkegringo/mcp 1.0.3 → 1.0.5
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/dist/index.d.ts +313 -7
- package/dist/index.js +385 -104
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { Experience } from '@linkegringo/core';
|
|
2
4
|
|
|
3
5
|
declare function createLinkeGringoMcpServer(): McpServer;
|
|
4
6
|
|
|
@@ -19,23 +21,50 @@ interface ChromeTabInfo {
|
|
|
19
21
|
webSocketDebuggerUrl?: string;
|
|
20
22
|
devtoolsFrontendUrl?: string;
|
|
21
23
|
}
|
|
24
|
+
interface LinkeGringoTabInfo {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
url: string;
|
|
28
|
+
webSocketDebuggerUrl?: string;
|
|
29
|
+
}
|
|
30
|
+
interface LinkeGringoSessionState {
|
|
31
|
+
hasUploadedProfile: boolean;
|
|
32
|
+
candidateName?: string;
|
|
33
|
+
targetRole?: string;
|
|
34
|
+
step?: string;
|
|
35
|
+
inboundScore?: number;
|
|
36
|
+
profile?: any;
|
|
37
|
+
review?: any;
|
|
38
|
+
}
|
|
22
39
|
interface CdpStatus {
|
|
23
40
|
isRunning: boolean;
|
|
24
41
|
port: number;
|
|
25
42
|
host: string;
|
|
26
43
|
browser?: string;
|
|
27
44
|
protocolVersion?: string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
/**
|
|
46
|
+
* Abas ativas restritas estritamente ao escopo do LinkeGringo (Privacy Shield).
|
|
47
|
+
* Abas pessoais (e-mails, mensageiros, bancos) são completamente omitidas.
|
|
48
|
+
*/
|
|
49
|
+
activeTabs: LinkeGringoTabInfo[];
|
|
50
|
+
linkeGringoTabs: LinkeGringoTabInfo[];
|
|
51
|
+
otherTabsCount: number;
|
|
34
52
|
linkeGringoTabFound: boolean;
|
|
35
53
|
linkeGringoTabUrl?: string;
|
|
54
|
+
sessionState?: LinkeGringoSessionState | null;
|
|
36
55
|
error?: string;
|
|
37
56
|
}
|
|
38
57
|
|
|
58
|
+
declare function isLinkeGringoUrl(url?: string): boolean;
|
|
59
|
+
declare function findDevToolsActivePort(): {
|
|
60
|
+
port: number;
|
|
61
|
+
wsPath: string;
|
|
62
|
+
} | null;
|
|
63
|
+
declare function probeViaWebSocket(port: number, wsPath: string, timeoutMs?: number): Promise<{
|
|
64
|
+
linkeGringoTabs: LinkeGringoTabInfo[];
|
|
65
|
+
otherTabsCount: number;
|
|
66
|
+
sessionState: LinkeGringoSessionState | null;
|
|
67
|
+
} | null>;
|
|
39
68
|
declare function checkChromeCdp(port?: number, host?: string, timeoutMs?: number): Promise<CdpStatus>;
|
|
40
69
|
|
|
41
70
|
interface McpTarget {
|
|
@@ -63,4 +92,281 @@ declare function installMcpServerConfig(configPath: string): {
|
|
|
63
92
|
declare function parseArgs(args?: string[]): InstallerOptions;
|
|
64
93
|
declare function runInstaller(args?: string[]): InstallResult[];
|
|
65
94
|
|
|
66
|
-
|
|
95
|
+
interface SparseExperience {
|
|
96
|
+
company: string;
|
|
97
|
+
title: string;
|
|
98
|
+
estimatedBullets: number;
|
|
99
|
+
}
|
|
100
|
+
declare function getExperienceBulletCount(exp: {
|
|
101
|
+
description?: string;
|
|
102
|
+
bullets?: string[];
|
|
103
|
+
}): number;
|
|
104
|
+
declare function detectSparseExperiences(experiences?: Experience[]): SparseExperience[];
|
|
105
|
+
declare const auditProfileInputSchema: z.ZodObject<{
|
|
106
|
+
profileText: z.ZodOptional<z.ZodString>;
|
|
107
|
+
headline: z.ZodOptional<z.ZodString>;
|
|
108
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
109
|
+
experiences: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
110
|
+
company: z.ZodString;
|
|
111
|
+
title: z.ZodString;
|
|
112
|
+
bullets: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
113
|
+
description: z.ZodOptional<z.ZodString>;
|
|
114
|
+
}, "strip", z.ZodTypeAny, {
|
|
115
|
+
title: string;
|
|
116
|
+
company: string;
|
|
117
|
+
description?: string | undefined;
|
|
118
|
+
bullets?: string[] | undefined;
|
|
119
|
+
}, {
|
|
120
|
+
title: string;
|
|
121
|
+
company: string;
|
|
122
|
+
description?: string | undefined;
|
|
123
|
+
bullets?: string[] | undefined;
|
|
124
|
+
}>, "many">>;
|
|
125
|
+
skills: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
126
|
+
targetRole: z.ZodDefault<z.ZodString>;
|
|
127
|
+
targetMarket: z.ZodDefault<z.ZodString>;
|
|
128
|
+
}, "strip", z.ZodTypeAny, {
|
|
129
|
+
targetMarket: string;
|
|
130
|
+
targetRole: string;
|
|
131
|
+
headline?: string | undefined;
|
|
132
|
+
summary?: string | undefined;
|
|
133
|
+
experiences?: {
|
|
134
|
+
title: string;
|
|
135
|
+
company: string;
|
|
136
|
+
description?: string | undefined;
|
|
137
|
+
bullets?: string[] | undefined;
|
|
138
|
+
}[] | undefined;
|
|
139
|
+
skills?: string[] | undefined;
|
|
140
|
+
profileText?: string | undefined;
|
|
141
|
+
}, {
|
|
142
|
+
headline?: string | undefined;
|
|
143
|
+
summary?: string | undefined;
|
|
144
|
+
experiences?: {
|
|
145
|
+
title: string;
|
|
146
|
+
company: string;
|
|
147
|
+
description?: string | undefined;
|
|
148
|
+
bullets?: string[] | undefined;
|
|
149
|
+
}[] | undefined;
|
|
150
|
+
skills?: string[] | undefined;
|
|
151
|
+
targetMarket?: string | undefined;
|
|
152
|
+
targetRole?: string | undefined;
|
|
153
|
+
profileText?: string | undefined;
|
|
154
|
+
}>;
|
|
155
|
+
type AuditProfileInput = z.infer<typeof auditProfileInputSchema>;
|
|
156
|
+
interface AuditIssue {
|
|
157
|
+
severity: 'critical' | 'warning' | 'info';
|
|
158
|
+
category: 'headline' | 'experience' | 'about' | 'skills';
|
|
159
|
+
message: string;
|
|
160
|
+
recommendation: string;
|
|
161
|
+
}
|
|
162
|
+
declare function handleAuditProfile(input: AuditProfileInput): Promise<{
|
|
163
|
+
content: {
|
|
164
|
+
type: "text";
|
|
165
|
+
text: string;
|
|
166
|
+
}[];
|
|
167
|
+
structuredData: {
|
|
168
|
+
status: string;
|
|
169
|
+
linkeGringoTabUrl: string | undefined;
|
|
170
|
+
message: string;
|
|
171
|
+
actionRequired: string;
|
|
172
|
+
score?: undefined;
|
|
173
|
+
targetRole?: undefined;
|
|
174
|
+
headline?: undefined;
|
|
175
|
+
totalBullets?: undefined;
|
|
176
|
+
bulletsWithMetrics?: undefined;
|
|
177
|
+
sparseExperiences?: undefined;
|
|
178
|
+
triageBottlenecks?: undefined;
|
|
179
|
+
issues?: undefined;
|
|
180
|
+
};
|
|
181
|
+
} | {
|
|
182
|
+
content: {
|
|
183
|
+
type: "text";
|
|
184
|
+
text: string;
|
|
185
|
+
}[];
|
|
186
|
+
structuredData: {
|
|
187
|
+
status: string;
|
|
188
|
+
message: string;
|
|
189
|
+
actionRequired: string;
|
|
190
|
+
linkeGringoTabUrl?: undefined;
|
|
191
|
+
score?: undefined;
|
|
192
|
+
targetRole?: undefined;
|
|
193
|
+
headline?: undefined;
|
|
194
|
+
totalBullets?: undefined;
|
|
195
|
+
bulletsWithMetrics?: undefined;
|
|
196
|
+
sparseExperiences?: undefined;
|
|
197
|
+
triageBottlenecks?: undefined;
|
|
198
|
+
issues?: undefined;
|
|
199
|
+
};
|
|
200
|
+
} | {
|
|
201
|
+
content: {
|
|
202
|
+
type: "text";
|
|
203
|
+
text: string;
|
|
204
|
+
}[];
|
|
205
|
+
structuredData: {
|
|
206
|
+
score: number;
|
|
207
|
+
targetRole: string;
|
|
208
|
+
headline: string;
|
|
209
|
+
totalBullets: number;
|
|
210
|
+
bulletsWithMetrics: number;
|
|
211
|
+
sparseExperiences: SparseExperience[];
|
|
212
|
+
triageBottlenecks: string[];
|
|
213
|
+
issues: AuditIssue[];
|
|
214
|
+
status?: undefined;
|
|
215
|
+
linkeGringoTabUrl?: undefined;
|
|
216
|
+
message?: undefined;
|
|
217
|
+
actionRequired?: undefined;
|
|
218
|
+
};
|
|
219
|
+
}>;
|
|
220
|
+
|
|
221
|
+
declare const simulateRecruiterSearchInputSchema: z.ZodObject<{
|
|
222
|
+
headline: z.ZodString;
|
|
223
|
+
summary: z.ZodDefault<z.ZodString>;
|
|
224
|
+
skills: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
225
|
+
experienceBullets: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
226
|
+
targetRole: z.ZodDefault<z.ZodString>;
|
|
227
|
+
requiredKeywords: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
|
|
228
|
+
}, "strip", z.ZodTypeAny, {
|
|
229
|
+
headline: string;
|
|
230
|
+
summary: string;
|
|
231
|
+
skills: string[];
|
|
232
|
+
targetRole: string;
|
|
233
|
+
experienceBullets: string[];
|
|
234
|
+
requiredKeywords?: string[] | undefined;
|
|
235
|
+
}, {
|
|
236
|
+
headline: string;
|
|
237
|
+
summary?: string | undefined;
|
|
238
|
+
skills?: string[] | undefined;
|
|
239
|
+
targetRole?: string | undefined;
|
|
240
|
+
experienceBullets?: string[] | undefined;
|
|
241
|
+
requiredKeywords?: string[] | undefined;
|
|
242
|
+
}>;
|
|
243
|
+
type SimulateRecruiterSearchInput = z.infer<typeof simulateRecruiterSearchInputSchema>;
|
|
244
|
+
declare function handleSimulateRecruiterSearch(input: SimulateRecruiterSearchInput): Promise<{
|
|
245
|
+
content: {
|
|
246
|
+
type: "text";
|
|
247
|
+
text: string;
|
|
248
|
+
}[];
|
|
249
|
+
structuredData: {
|
|
250
|
+
overallStatus: "weak" | "missing" | "match";
|
|
251
|
+
matchPercentage: number;
|
|
252
|
+
evaluations: {
|
|
253
|
+
term: string;
|
|
254
|
+
status: "weak" | "missing" | "match";
|
|
255
|
+
detail: string;
|
|
256
|
+
}[];
|
|
257
|
+
matchCount: number;
|
|
258
|
+
weakCount: number;
|
|
259
|
+
missingCount: number;
|
|
260
|
+
};
|
|
261
|
+
}>;
|
|
262
|
+
|
|
263
|
+
declare const convertToXyzBulletInputSchema: z.ZodObject<{
|
|
264
|
+
rawBullet: z.ZodString;
|
|
265
|
+
roleContext: z.ZodDefault<z.ZodString>;
|
|
266
|
+
action: z.ZodOptional<z.ZodString>;
|
|
267
|
+
metric: z.ZodOptional<z.ZodString>;
|
|
268
|
+
method: z.ZodOptional<z.ZodString>;
|
|
269
|
+
}, "strip", z.ZodTypeAny, {
|
|
270
|
+
rawBullet: string;
|
|
271
|
+
roleContext: string;
|
|
272
|
+
method?: string | undefined;
|
|
273
|
+
metric?: string | undefined;
|
|
274
|
+
action?: string | undefined;
|
|
275
|
+
}, {
|
|
276
|
+
rawBullet: string;
|
|
277
|
+
method?: string | undefined;
|
|
278
|
+
metric?: string | undefined;
|
|
279
|
+
roleContext?: string | undefined;
|
|
280
|
+
action?: string | undefined;
|
|
281
|
+
}>;
|
|
282
|
+
type ConvertToXyzBulletInput = z.infer<typeof convertToXyzBulletInputSchema>;
|
|
283
|
+
declare function formatGoogleXyzBullet(parts: {
|
|
284
|
+
action: string;
|
|
285
|
+
metric: string;
|
|
286
|
+
method: string;
|
|
287
|
+
}): string;
|
|
288
|
+
declare function handleConvertToXyzBullet(input: ConvertToXyzBulletInput): Promise<{
|
|
289
|
+
content: {
|
|
290
|
+
type: "text";
|
|
291
|
+
text: string;
|
|
292
|
+
}[];
|
|
293
|
+
structuredData: {
|
|
294
|
+
formattedBullet: string;
|
|
295
|
+
parts: {
|
|
296
|
+
action: string;
|
|
297
|
+
metric: string;
|
|
298
|
+
method: string;
|
|
299
|
+
};
|
|
300
|
+
hasMetrics?: undefined;
|
|
301
|
+
rawBullet?: undefined;
|
|
302
|
+
proposals?: undefined;
|
|
303
|
+
};
|
|
304
|
+
} | {
|
|
305
|
+
content: {
|
|
306
|
+
type: "text";
|
|
307
|
+
text: string;
|
|
308
|
+
}[];
|
|
309
|
+
structuredData: {
|
|
310
|
+
hasMetrics: boolean;
|
|
311
|
+
rawBullet: string;
|
|
312
|
+
proposals: string[];
|
|
313
|
+
formattedBullet?: undefined;
|
|
314
|
+
parts?: undefined;
|
|
315
|
+
};
|
|
316
|
+
}>;
|
|
317
|
+
|
|
318
|
+
declare const generateHeadlineInputSchema: z.ZodObject<{
|
|
319
|
+
targetRole: z.ZodDefault<z.ZodString>;
|
|
320
|
+
coreTechnologies: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
321
|
+
keyDifferentiator: z.ZodOptional<z.ZodString>;
|
|
322
|
+
seniorityOrScope: z.ZodDefault<z.ZodString>;
|
|
323
|
+
}, "strip", z.ZodTypeAny, {
|
|
324
|
+
targetRole: string;
|
|
325
|
+
coreTechnologies: string[];
|
|
326
|
+
seniorityOrScope: string;
|
|
327
|
+
keyDifferentiator?: string | undefined;
|
|
328
|
+
}, {
|
|
329
|
+
targetRole?: string | undefined;
|
|
330
|
+
coreTechnologies?: string[] | undefined;
|
|
331
|
+
keyDifferentiator?: string | undefined;
|
|
332
|
+
seniorityOrScope?: string | undefined;
|
|
333
|
+
}>;
|
|
334
|
+
type GenerateHeadlineInput = z.infer<typeof generateHeadlineInputSchema>;
|
|
335
|
+
declare function handleGenerateHeadline(input: GenerateHeadlineInput): Promise<{
|
|
336
|
+
content: {
|
|
337
|
+
type: "text";
|
|
338
|
+
text: string;
|
|
339
|
+
}[];
|
|
340
|
+
structuredData: {
|
|
341
|
+
proposals: {
|
|
342
|
+
type: string;
|
|
343
|
+
headline: string;
|
|
344
|
+
charCount: number;
|
|
345
|
+
focus: string;
|
|
346
|
+
}[];
|
|
347
|
+
};
|
|
348
|
+
}>;
|
|
349
|
+
|
|
350
|
+
declare const checkChromeCdpInputSchema: z.ZodObject<{
|
|
351
|
+
port: z.ZodDefault<z.ZodNumber>;
|
|
352
|
+
host: z.ZodDefault<z.ZodString>;
|
|
353
|
+
timeoutMs: z.ZodDefault<z.ZodNumber>;
|
|
354
|
+
}, "strip", z.ZodTypeAny, {
|
|
355
|
+
port: number;
|
|
356
|
+
host: string;
|
|
357
|
+
timeoutMs: number;
|
|
358
|
+
}, {
|
|
359
|
+
port?: number | undefined;
|
|
360
|
+
host?: string | undefined;
|
|
361
|
+
timeoutMs?: number | undefined;
|
|
362
|
+
}>;
|
|
363
|
+
type CheckChromeCdpInput = z.infer<typeof checkChromeCdpInputSchema>;
|
|
364
|
+
declare function handleCheckChromeCdp(input: CheckChromeCdpInput): Promise<{
|
|
365
|
+
content: {
|
|
366
|
+
type: "text";
|
|
367
|
+
text: string;
|
|
368
|
+
}[];
|
|
369
|
+
structuredData: CdpStatus;
|
|
370
|
+
}>;
|
|
371
|
+
|
|
372
|
+
export { type AuditIssue, type AuditProfileInput, type CdpStatus, type CheckChromeCdpInput, type ChromeTabInfo, type ChromeVersionResponse, type ConvertToXyzBulletInput, type GenerateHeadlineInput, type InstallResult, type InstallerOptions, type LinkeGringoSessionState, type LinkeGringoTabInfo, type McpTarget, type SimulateRecruiterSearchInput, type SparseExperience, auditProfileInputSchema, checkChromeCdp, checkChromeCdpInputSchema, convertToXyzBulletInputSchema, createLinkeGringoMcpServer, detectSparseExperiences, findDevToolsActivePort, formatGoogleXyzBullet, generateHeadlineInputSchema, getExperienceBulletCount, getMcpConfigsForSystem, handleAuditProfile, handleCheckChromeCdp, handleConvertToXyzBullet, handleGenerateHeadline, handleSimulateRecruiterSearch, installMcpServerConfig, isLinkeGringoUrl, parseArgs, probeViaWebSocket, runInstaller, simulateRecruiterSearchInputSchema };
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,233 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
8
8
|
|
|
9
9
|
// src/tools/audit-profile.ts
|
|
10
10
|
import { z } from "zod";
|
|
11
|
+
|
|
12
|
+
// src/cdp/probe.ts
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
import os from "os";
|
|
15
|
+
import path from "path";
|
|
16
|
+
function isLinkeGringoUrl(url = "") {
|
|
17
|
+
const lower = url.toLowerCase();
|
|
18
|
+
return lower.includes("localhost:5173") || lower.includes("127.0.0.1:5173") || lower.includes("localhost:4173") || lower.includes("127.0.0.1:4173") || lower.includes("linkegringo") || lower.includes("muriel-gasparini.github.io");
|
|
19
|
+
}
|
|
20
|
+
function findDevToolsActivePort() {
|
|
21
|
+
const possiblePaths = [
|
|
22
|
+
// Linux
|
|
23
|
+
path.join(os.homedir(), ".config", "google-chrome", "DevToolsActivePort"),
|
|
24
|
+
path.join(os.homedir(), ".config", "chromium", "DevToolsActivePort"),
|
|
25
|
+
path.join(os.homedir(), ".config", "google-chrome-beta", "DevToolsActivePort"),
|
|
26
|
+
// macOS
|
|
27
|
+
path.join(os.homedir(), "Library", "Application Support", "Google", "Chrome", "DevToolsActivePort"),
|
|
28
|
+
// Windows
|
|
29
|
+
path.join(process.env.LOCALAPPDATA || "", "Google", "Chrome", "User Data", "DevToolsActivePort")
|
|
30
|
+
];
|
|
31
|
+
for (const p of possiblePaths) {
|
|
32
|
+
if (p && fs.existsSync(p)) {
|
|
33
|
+
try {
|
|
34
|
+
const lines = fs.readFileSync(p, "utf8").trim().split("\n");
|
|
35
|
+
if (lines.length >= 2) {
|
|
36
|
+
const port = parseInt(lines[0].trim(), 10);
|
|
37
|
+
const wsPath = lines[1].trim();
|
|
38
|
+
if (port > 0 && wsPath) {
|
|
39
|
+
return { port, wsPath };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
async function probeViaWebSocket(port, wsPath, timeoutMs = 2e3) {
|
|
49
|
+
const wsUrl = `ws://127.0.0.1:${port}${wsPath}`;
|
|
50
|
+
if (typeof globalThis.WebSocket !== "function") return null;
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
let ws;
|
|
53
|
+
const timer = setTimeout(() => {
|
|
54
|
+
try {
|
|
55
|
+
ws?.close();
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
resolve(null);
|
|
59
|
+
}, timeoutMs);
|
|
60
|
+
try {
|
|
61
|
+
ws = new globalThis.WebSocket(wsUrl);
|
|
62
|
+
} catch {
|
|
63
|
+
clearTimeout(timer);
|
|
64
|
+
resolve(null);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
let linkeTarget = null;
|
|
68
|
+
let linkeGringoTabs = [];
|
|
69
|
+
let otherTabsCount = 0;
|
|
70
|
+
ws.onopen = () => {
|
|
71
|
+
ws.send(JSON.stringify({ id: 1, method: "Target.getTargets" }));
|
|
72
|
+
};
|
|
73
|
+
ws.onmessage = (event) => {
|
|
74
|
+
try {
|
|
75
|
+
const msg = JSON.parse(String(event.data));
|
|
76
|
+
if (msg.id === 1) {
|
|
77
|
+
const targets = msg.result?.targetInfos || [];
|
|
78
|
+
const pageTargets = targets.filter((t) => t.type === "page");
|
|
79
|
+
const matched = pageTargets.filter((t) => isLinkeGringoUrl(t.url));
|
|
80
|
+
linkeGringoTabs = matched.map((t) => ({
|
|
81
|
+
id: t.targetId,
|
|
82
|
+
title: t.title,
|
|
83
|
+
url: t.url
|
|
84
|
+
}));
|
|
85
|
+
otherTabsCount = pageTargets.length - matched.length;
|
|
86
|
+
if (linkeGringoTabs.length === 0) {
|
|
87
|
+
clearTimeout(timer);
|
|
88
|
+
ws.close();
|
|
89
|
+
resolve({ linkeGringoTabs: [], otherTabsCount, sessionState: null });
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
linkeTarget = matched[0];
|
|
93
|
+
ws.send(
|
|
94
|
+
JSON.stringify({
|
|
95
|
+
id: 2,
|
|
96
|
+
method: "Target.attachToTarget",
|
|
97
|
+
params: { targetId: linkeTarget.targetId, flatten: true }
|
|
98
|
+
})
|
|
99
|
+
);
|
|
100
|
+
} else if (msg.id === 2) {
|
|
101
|
+
const sessionId = msg.result?.sessionId;
|
|
102
|
+
ws.send(
|
|
103
|
+
JSON.stringify({
|
|
104
|
+
id: 3,
|
|
105
|
+
sessionId,
|
|
106
|
+
method: "Runtime.evaluate",
|
|
107
|
+
params: {
|
|
108
|
+
expression: 'window.localStorage.getItem("linkegringo_active_session")',
|
|
109
|
+
returnByValue: true
|
|
110
|
+
}
|
|
111
|
+
})
|
|
112
|
+
);
|
|
113
|
+
} else if (msg.id === 3) {
|
|
114
|
+
clearTimeout(timer);
|
|
115
|
+
ws.close();
|
|
116
|
+
const raw = msg.result?.result?.value;
|
|
117
|
+
let session = null;
|
|
118
|
+
try {
|
|
119
|
+
if (raw) session = JSON.parse(raw);
|
|
120
|
+
} catch {
|
|
121
|
+
}
|
|
122
|
+
const hasUploadedProfile = Boolean(session && session.profile);
|
|
123
|
+
const sessionState = {
|
|
124
|
+
hasUploadedProfile,
|
|
125
|
+
candidateName: session?.profile?.name,
|
|
126
|
+
targetRole: session?.profile?.targetRole || session?.objective,
|
|
127
|
+
step: session?.step || (hasUploadedProfile ? "diagnostic" : "upload"),
|
|
128
|
+
inboundScore: session?.review?.inboundReadinessScore,
|
|
129
|
+
profile: session?.profile || null,
|
|
130
|
+
review: session?.review || null
|
|
131
|
+
};
|
|
132
|
+
resolve({
|
|
133
|
+
linkeGringoTabs,
|
|
134
|
+
otherTabsCount,
|
|
135
|
+
sessionState
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
try {
|
|
141
|
+
ws?.close();
|
|
142
|
+
} catch {
|
|
143
|
+
}
|
|
144
|
+
resolve(null);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
ws.onerror = () => {
|
|
148
|
+
clearTimeout(timer);
|
|
149
|
+
resolve(null);
|
|
150
|
+
};
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
async function checkChromeCdp(port = 9222, host = "127.0.0.1", timeoutMs = 5e3) {
|
|
154
|
+
const activePortData = findDevToolsActivePort();
|
|
155
|
+
if (activePortData) {
|
|
156
|
+
const wsResult = await probeViaWebSocket(activePortData.port, activePortData.wsPath, timeoutMs);
|
|
157
|
+
if (wsResult) {
|
|
158
|
+
const found = wsResult.linkeGringoTabs.length > 0;
|
|
159
|
+
return {
|
|
160
|
+
isRunning: true,
|
|
161
|
+
port: activePortData.port,
|
|
162
|
+
host: "127.0.0.1",
|
|
163
|
+
browser: "Google Chrome (DevTools Protocol)",
|
|
164
|
+
protocolVersion: "1.3",
|
|
165
|
+
activeTabs: wsResult.linkeGringoTabs,
|
|
166
|
+
linkeGringoTabs: wsResult.linkeGringoTabs,
|
|
167
|
+
otherTabsCount: wsResult.otherTabsCount,
|
|
168
|
+
linkeGringoTabFound: found,
|
|
169
|
+
linkeGringoTabUrl: wsResult.linkeGringoTabs[0]?.url,
|
|
170
|
+
sessionState: wsResult.sessionState
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
const controller = new AbortController();
|
|
175
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
176
|
+
try {
|
|
177
|
+
const versionRes = await fetch(`http://${host}:${port}/json/version`, {
|
|
178
|
+
signal: controller.signal
|
|
179
|
+
});
|
|
180
|
+
if (!versionRes.ok) {
|
|
181
|
+
throw new Error(`HTTP ${versionRes.status}: ${versionRes.statusText}`);
|
|
182
|
+
}
|
|
183
|
+
const versionData = await versionRes.json();
|
|
184
|
+
let pageTabs = [];
|
|
185
|
+
try {
|
|
186
|
+
const listRes = await fetch(`http://${host}:${port}/json/list`, {
|
|
187
|
+
signal: controller.signal
|
|
188
|
+
});
|
|
189
|
+
if (listRes.ok) {
|
|
190
|
+
const rawTabs = await listRes.json();
|
|
191
|
+
if (Array.isArray(rawTabs)) {
|
|
192
|
+
pageTabs = rawTabs.filter((t) => t.type === "page");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
const linkeGringoTabs = pageTabs.filter((t) => isLinkeGringoUrl(t.url)).map((t) => ({
|
|
198
|
+
id: t.id,
|
|
199
|
+
title: t.title,
|
|
200
|
+
url: t.url,
|
|
201
|
+
webSocketDebuggerUrl: t.webSocketDebuggerUrl
|
|
202
|
+
}));
|
|
203
|
+
const otherTabsCount = pageTabs.length - linkeGringoTabs.length;
|
|
204
|
+
const found = linkeGringoTabs.length > 0;
|
|
205
|
+
return {
|
|
206
|
+
isRunning: true,
|
|
207
|
+
port,
|
|
208
|
+
host,
|
|
209
|
+
browser: versionData.Browser,
|
|
210
|
+
protocolVersion: versionData["Protocol-Version"],
|
|
211
|
+
activeTabs: linkeGringoTabs,
|
|
212
|
+
linkeGringoTabs,
|
|
213
|
+
otherTabsCount,
|
|
214
|
+
linkeGringoTabFound: found,
|
|
215
|
+
linkeGringoTabUrl: linkeGringoTabs[0]?.url,
|
|
216
|
+
sessionState: null
|
|
217
|
+
};
|
|
218
|
+
} catch (err) {
|
|
219
|
+
const error = err;
|
|
220
|
+
const isTimeout = error.name === "AbortError" || error.name === "TimeoutError";
|
|
221
|
+
return {
|
|
222
|
+
isRunning: false,
|
|
223
|
+
port,
|
|
224
|
+
host,
|
|
225
|
+
activeTabs: [],
|
|
226
|
+
linkeGringoTabs: [],
|
|
227
|
+
otherTabsCount: 0,
|
|
228
|
+
linkeGringoTabFound: false,
|
|
229
|
+
sessionState: null,
|
|
230
|
+
error: isTimeout ? "Conex\xE3o expirou (Chrome n\xE3o respondeu em 2s na porta " + port + ")" : "Porta fechada ou depura\xE7\xE3o remota desativada. Acesse chrome://inspect/#remote-debugging para ativar."
|
|
231
|
+
};
|
|
232
|
+
} finally {
|
|
233
|
+
clearTimeout(timeoutId);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// src/tools/audit-profile.ts
|
|
11
238
|
function getExperienceBulletCount(exp) {
|
|
12
239
|
if (Array.isArray(exp.bullets) && exp.bullets.length > 0) {
|
|
13
240
|
return exp.bullets.length;
|
|
@@ -57,7 +284,77 @@ async function handleAuditProfile(input) {
|
|
|
57
284
|
let headline = input.headline || "";
|
|
58
285
|
let summary = input.summary || "";
|
|
59
286
|
let skills = input.skills || [];
|
|
60
|
-
const rawExperiences = input.experiences || [];
|
|
287
|
+
const rawExperiences = [...input.experiences || []];
|
|
288
|
+
if (!headline && !input.profileText && rawExperiences.length === 0) {
|
|
289
|
+
try {
|
|
290
|
+
const cdp = await checkChromeCdp();
|
|
291
|
+
if (cdp.sessionState?.hasUploadedProfile && cdp.sessionState.profile) {
|
|
292
|
+
const p = cdp.sessionState.profile;
|
|
293
|
+
headline = p.headline || "";
|
|
294
|
+
summary = p.summary || "";
|
|
295
|
+
skills = p.skills || [];
|
|
296
|
+
if (Array.isArray(p.experiences)) {
|
|
297
|
+
for (const exp of p.experiences) {
|
|
298
|
+
rawExperiences.push({
|
|
299
|
+
company: exp.companyName || exp.company || "Company",
|
|
300
|
+
title: exp.title || "Engineer",
|
|
301
|
+
bullets: exp.bullets,
|
|
302
|
+
description: exp.description
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} else if (cdp.linkeGringoTabFound) {
|
|
307
|
+
return {
|
|
308
|
+
content: [
|
|
309
|
+
{
|
|
310
|
+
type: "text",
|
|
311
|
+
text: `
|
|
312
|
+
# \u26A0\uFE0F Nenhum Perfil Carregado no LinkeGringo
|
|
313
|
+
|
|
314
|
+
A aplica\xE7\xE3o **LinkeGringo est\xE1 aberta no seu navegador** (\`${cdp.linkeGringoTabUrl || "http://localhost:5173"}\`), mas o **upload do PDF do LinkedIn ainda n\xE3o foi realizado**.
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
### \u{1F4A1} Pr\xF3ximo Passo
|
|
319
|
+
Por favor, acesse a aba do LinkeGringo no seu navegador e **arraste o PDF do seu perfil do LinkedIn para o dropzone** (ou clique para selecionar o arquivo).
|
|
320
|
+
|
|
321
|
+
Assim que o upload for processado pela aplica\xE7\xE3o web, chame o \`audit_profile\` novamente para auditar o perfil real automaticamente!
|
|
322
|
+
`.trim()
|
|
323
|
+
}
|
|
324
|
+
],
|
|
325
|
+
structuredData: {
|
|
326
|
+
status: "waiting_for_upload",
|
|
327
|
+
linkeGringoTabUrl: cdp.linkeGringoTabUrl,
|
|
328
|
+
message: "O usu\xE1rio ainda n\xE3o subiu o PDF do LinkedIn na aplica\xE7\xE3o web.",
|
|
329
|
+
actionRequired: "upload_pdf"
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
} else {
|
|
333
|
+
return {
|
|
334
|
+
content: [
|
|
335
|
+
{
|
|
336
|
+
type: "text",
|
|
337
|
+
text: `
|
|
338
|
+
# \u26A0\uFE0F Nenhum Perfil Fornecido para Auditoria
|
|
339
|
+
|
|
340
|
+
Nenhum dado de perfil foi informado e a aplica\xE7\xE3o LinkeGringo n\xE3o foi detectada no navegador.
|
|
341
|
+
|
|
342
|
+
### Como prosseguir:
|
|
343
|
+
1. **Pela Web**: Abra o LinkeGringo em \`http://localhost:5173\` no Google Chrome e fa\xE7a o upload do PDF do seu LinkedIn; OU
|
|
344
|
+
2. **Via Par\xE2metros**: Forne\xE7a o texto bruto do perfil no par\xE2metro \`profileText\` ou informe \`headline\`, \`experiences\` e \`skills\`.
|
|
345
|
+
`.trim()
|
|
346
|
+
}
|
|
347
|
+
],
|
|
348
|
+
structuredData: {
|
|
349
|
+
status: "missing_profile",
|
|
350
|
+
message: "Nenhum perfil fornecido e aplica\xE7\xE3o LinkeGringo n\xE3o detectada no navegador.",
|
|
351
|
+
actionRequired: "provide_input_or_open_web"
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
} catch {
|
|
356
|
+
}
|
|
357
|
+
}
|
|
61
358
|
if (input.profileText && !headline && rawExperiences.length === 0) {
|
|
62
359
|
const lines = input.profileText.split("\n").map((l) => l.trim()).filter(Boolean);
|
|
63
360
|
if (lines.length > 0) {
|
|
@@ -960,89 +1257,57 @@ ${proposals.map(
|
|
|
960
1257
|
|
|
961
1258
|
// src/tools/cdp-check.ts
|
|
962
1259
|
import { z as z11 } from "zod";
|
|
963
|
-
|
|
964
|
-
// src/cdp/probe.ts
|
|
965
|
-
async function checkChromeCdp(port = 9222, host = "127.0.0.1", timeoutMs = 2e3) {
|
|
966
|
-
const controller = new AbortController();
|
|
967
|
-
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
968
|
-
try {
|
|
969
|
-
const versionRes = await fetch(`http://${host}:${port}/json/version`, {
|
|
970
|
-
signal: controller.signal
|
|
971
|
-
});
|
|
972
|
-
if (!versionRes.ok) {
|
|
973
|
-
throw new Error(`HTTP ${versionRes.status}: ${versionRes.statusText}`);
|
|
974
|
-
}
|
|
975
|
-
const versionData = await versionRes.json();
|
|
976
|
-
let pageTabs = [];
|
|
977
|
-
try {
|
|
978
|
-
const listRes = await fetch(`http://${host}:${port}/json/list`, {
|
|
979
|
-
signal: controller.signal
|
|
980
|
-
});
|
|
981
|
-
if (listRes.ok) {
|
|
982
|
-
const rawTabs = await listRes.json();
|
|
983
|
-
if (Array.isArray(rawTabs)) {
|
|
984
|
-
pageTabs = rawTabs.filter((t) => t.type === "page");
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
} catch {
|
|
988
|
-
}
|
|
989
|
-
const linkeGringoTab = pageTabs.find((t) => {
|
|
990
|
-
const lowerUrl = (t.url || "").toLowerCase();
|
|
991
|
-
return lowerUrl.includes("linkegringo") || lowerUrl.includes("5173") || lowerUrl.includes("muriel-gasparini.github.io");
|
|
992
|
-
});
|
|
993
|
-
return {
|
|
994
|
-
isRunning: true,
|
|
995
|
-
port,
|
|
996
|
-
host,
|
|
997
|
-
browser: versionData.Browser,
|
|
998
|
-
protocolVersion: versionData["Protocol-Version"],
|
|
999
|
-
activeTabs: pageTabs.map((t) => ({
|
|
1000
|
-
id: t.id,
|
|
1001
|
-
title: t.title,
|
|
1002
|
-
url: t.url,
|
|
1003
|
-
webSocketDebuggerUrl: t.webSocketDebuggerUrl
|
|
1004
|
-
})),
|
|
1005
|
-
linkeGringoTabFound: Boolean(linkeGringoTab),
|
|
1006
|
-
linkeGringoTabUrl: linkeGringoTab?.url
|
|
1007
|
-
};
|
|
1008
|
-
} catch (err) {
|
|
1009
|
-
const error = err;
|
|
1010
|
-
const isTimeout = error.name === "AbortError" || error.name === "TimeoutError";
|
|
1011
|
-
return {
|
|
1012
|
-
isRunning: false,
|
|
1013
|
-
port,
|
|
1014
|
-
host,
|
|
1015
|
-
activeTabs: [],
|
|
1016
|
-
linkeGringoTabFound: false,
|
|
1017
|
-
error: isTimeout ? "Conex\xE3o expirou (Chrome n\xE3o respondeu em 2s na porta " + port + ")" : "Porta fechada ou depura\xE7\xE3o remota desativada. Acesse chrome://inspect/#remote-debugging para ativar."
|
|
1018
|
-
};
|
|
1019
|
-
} finally {
|
|
1020
|
-
clearTimeout(timeoutId);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
// src/tools/cdp-check.ts
|
|
1025
1260
|
var checkChromeCdpInputSchema = z11.object({
|
|
1026
1261
|
port: z11.number().default(9222).describe("Porta do Chrome DevTools Protocol a ser testada (padr\xE3o 9222)"),
|
|
1027
1262
|
host: z11.string().default("127.0.0.1").describe("Host do Chrome (padr\xE3o 127.0.0.1)"),
|
|
1028
|
-
timeoutMs: z11.number().default(
|
|
1263
|
+
timeoutMs: z11.number().default(5e3).describe("Tempo limite em milissegundos para a conex\xE3o")
|
|
1029
1264
|
});
|
|
1030
1265
|
async function handleCheckChromeCdp(input) {
|
|
1031
1266
|
const status = await checkChromeCdp(input.port, input.host, input.timeoutMs);
|
|
1267
|
+
const session = status.sessionState;
|
|
1268
|
+
const isProfileLoaded = Boolean(session?.hasUploadedProfile);
|
|
1269
|
+
let sessionSection = "";
|
|
1270
|
+
if (status.linkeGringoTabFound) {
|
|
1271
|
+
if (isProfileLoaded) {
|
|
1272
|
+
sessionSection = `
|
|
1273
|
+
### \u{1F464} Sess\xE3o do LinkeGringo: \u{1F7E2} Perfil Carregado
|
|
1274
|
+
- **Candidato**: ${session?.candidateName || "Identificado"}
|
|
1275
|
+
- **Cargo Alvo**: ${session?.targetRole || "N\xE3o especificado"}
|
|
1276
|
+
- **Passo Atual**: \`${session?.step || "diagnostic"}\`
|
|
1277
|
+
- **Inbound Score**: ${session?.inboundScore !== void 0 ? `${session.inboundScore}/100` : "Pronto para c\xE1lculo"}
|
|
1278
|
+
|
|
1279
|
+
> \u{1F4A1} **Instru\xE7\xE3o para a IA**: O perfil do usu\xE1rio j\xE1 est\xE1 carregado. Voc\xEA pode utilizar diretamente as ferramentas \`audit_profile\`, \`simulate_recruiter_search\`, \`generate_headline_proposals\` e \`convert_to_xyz_bullet\` com os dados do candidato.
|
|
1280
|
+
`;
|
|
1281
|
+
} else {
|
|
1282
|
+
sessionSection = `
|
|
1283
|
+
### \u{1F464} Sess\xE3o do LinkeGringo: \u{1F7E1} Aguardando Upload do PDF
|
|
1284
|
+
- **Passo Atual**: \`upload\` (Tela inicial)
|
|
1285
|
+
- **Status do Arquivo**: Nenhum PDF do LinkedIn foi carregado ainda pelo usu\xE1rio.
|
|
1286
|
+
|
|
1287
|
+
> \u{1F4A1} **Instru\xE7\xE3o para a IA**: O usu\xE1rio est\xE1 com o LinkeGringo aberto, mas ainda n\xE3o subiu o PDF. **Pe\xE7a educadamente para o usu\xE1rio arrastar ou selecionar o PDF do seu perfil do LinkedIn no dropzone da aplica\xE7\xE3o web** (${status.linkeGringoTabUrl || "http://localhost:5173"}). Assim que o usu\xE1rio subir o PDF, voc\xEA ter\xE1 acesso instant\xE2neo aos dados para auditar e otimizar!
|
|
1288
|
+
`;
|
|
1289
|
+
}
|
|
1290
|
+
} else {
|
|
1291
|
+
sessionSection = `
|
|
1292
|
+
### \u{1F310} Sess\xE3o do LinkeGringo: \u26A0\uFE0F N\xE3o Detectada
|
|
1293
|
+
> O LinkeGringo n\xE3o foi detectado em nenhuma aba aberta. Pe\xE7a ao usu\xE1rio para abrir \`http://localhost:5173\` no navegador ou fornecer o texto do perfil diretamente.
|
|
1294
|
+
`;
|
|
1295
|
+
}
|
|
1032
1296
|
const markdownSummary = `
|
|
1033
|
-
# Status do Chrome Remote Debugging (CDP)
|
|
1297
|
+
# Status do Chrome Remote Debugging (CDP) & LinkeGringo
|
|
1034
1298
|
|
|
1035
|
-
**Porta
|
|
1036
|
-
**Status
|
|
1299
|
+
**Porta CDP**: ${status.host}:${status.port}
|
|
1300
|
+
**Status do Chrome**: ${status.isRunning ? "\u{1F7E2} Conectado e Ativo" : "\u{1F534} Desconectado"}
|
|
1037
1301
|
|
|
1038
1302
|
${status.isRunning ? `
|
|
1039
|
-
- **Vers\xE3o do Navegador**: ${status.browser || "
|
|
1040
|
-
- **
|
|
1041
|
-
- **Total de Abas Abertas**: ${status.activeTabs.length}
|
|
1042
|
-
- **Aba do LinkeGringo**: ${status.linkeGringoTabFound ? `\u2713 Detectada (${status.linkeGringoTabUrl})` : "\u26A0\uFE0F Nenhuma aba do LinkeGringo aberta no momento"}
|
|
1303
|
+
- **Vers\xE3o do Navegador**: ${status.browser || "Google Chrome"}
|
|
1304
|
+
- **Aba do LinkeGringo**: ${status.linkeGringoTabFound ? `\u2713 Detectada (\`${status.linkeGringoTabUrl}\`)` : "\u26A0\uFE0F Nenhuma aba do LinkeGringo aberta"}
|
|
1043
1305
|
|
|
1044
|
-
|
|
1045
|
-
${status.
|
|
1306
|
+
### \u{1F6E1}\uFE0F Privacy Shield Ativo
|
|
1307
|
+
- **Abas Pessoais Protegidas**: ${status.otherTabsCount} aba(s) abertas no navegador foram preservadas sem inspe\xE7\xE3o (e-mails, mensageiros, documentos).
|
|
1308
|
+
- **Escopo Restrito**: O LinkeGringo acessa exclusivamente abas pertencentes \xE0 pr\xF3pria aplica\xE7\xE3o LinkeGringo.
|
|
1309
|
+
|
|
1310
|
+
${sessionSection}
|
|
1046
1311
|
` : `
|
|
1047
1312
|
> \u274C **Motivo**: ${status.error || "Porta fechada."}
|
|
1048
1313
|
>
|
|
@@ -1157,49 +1422,49 @@ function createLinkeGringoMcpServer() {
|
|
|
1157
1422
|
}
|
|
1158
1423
|
|
|
1159
1424
|
// src/cli/installer.ts
|
|
1160
|
-
import
|
|
1161
|
-
import
|
|
1162
|
-
import
|
|
1425
|
+
import fs2 from "fs";
|
|
1426
|
+
import path2 from "path";
|
|
1427
|
+
import os2 from "os";
|
|
1163
1428
|
function getMcpConfigsForSystem() {
|
|
1164
|
-
const home =
|
|
1165
|
-
const platform =
|
|
1429
|
+
const home = os2.homedir();
|
|
1430
|
+
const platform = os2.platform();
|
|
1166
1431
|
const configs = [];
|
|
1167
|
-
const antigravityPath =
|
|
1432
|
+
const antigravityPath = path2.join(home, ".gemini", "config", "mcp_config.json");
|
|
1168
1433
|
configs.push({
|
|
1169
1434
|
id: "antigravity",
|
|
1170
1435
|
client: "Google Antigravity",
|
|
1171
1436
|
configPath: antigravityPath,
|
|
1172
|
-
detected:
|
|
1437
|
+
detected: fs2.existsSync(path2.join(home, ".gemini")) || fs2.existsSync(antigravityPath)
|
|
1173
1438
|
});
|
|
1174
1439
|
let claudePath;
|
|
1175
1440
|
let claudeDir;
|
|
1176
1441
|
if (platform === "darwin") {
|
|
1177
|
-
claudeDir =
|
|
1178
|
-
claudePath =
|
|
1442
|
+
claudeDir = path2.join(home, "Library", "Application Support", "Claude");
|
|
1443
|
+
claudePath = path2.join(claudeDir, "claude_desktop_config.json");
|
|
1179
1444
|
} else if (platform === "win32") {
|
|
1180
|
-
claudeDir =
|
|
1181
|
-
claudePath =
|
|
1445
|
+
claudeDir = path2.join(process.env.APPDATA || path2.join(home, "AppData", "Roaming"), "Claude");
|
|
1446
|
+
claudePath = path2.join(claudeDir, "claude_desktop_config.json");
|
|
1182
1447
|
} else {
|
|
1183
|
-
claudeDir =
|
|
1184
|
-
claudePath =
|
|
1448
|
+
claudeDir = path2.join(home, ".config", "Claude");
|
|
1449
|
+
claudePath = path2.join(claudeDir, "claude_desktop_config.json");
|
|
1185
1450
|
}
|
|
1186
1451
|
configs.push({
|
|
1187
1452
|
id: "claude",
|
|
1188
1453
|
client: `Claude Desktop (${platform === "darwin" ? "macOS" : platform === "win32" ? "Windows" : "Linux"})`,
|
|
1189
1454
|
configPath: claudePath,
|
|
1190
|
-
detected:
|
|
1455
|
+
detected: fs2.existsSync(claudeDir) || fs2.existsSync(claudePath)
|
|
1191
1456
|
});
|
|
1192
|
-
const cursorDir =
|
|
1193
|
-
const cursorPath =
|
|
1457
|
+
const cursorDir = path2.join(home, ".cursor");
|
|
1458
|
+
const cursorPath = path2.join(cursorDir, "mcp.json");
|
|
1194
1459
|
configs.push({
|
|
1195
1460
|
id: "cursor",
|
|
1196
1461
|
client: "Cursor AI",
|
|
1197
1462
|
configPath: cursorPath,
|
|
1198
|
-
detected:
|
|
1463
|
+
detected: fs2.existsSync(cursorDir) || fs2.existsSync(cursorPath)
|
|
1199
1464
|
});
|
|
1200
|
-
const windsurfDir =
|
|
1201
|
-
const windsurfPath =
|
|
1202
|
-
if (
|
|
1465
|
+
const windsurfDir = path2.join(home, ".codeium", "windsurf");
|
|
1466
|
+
const windsurfPath = path2.join(windsurfDir, "mcp_config.json");
|
|
1467
|
+
if (fs2.existsSync(windsurfDir) || fs2.existsSync(windsurfPath)) {
|
|
1203
1468
|
configs.push({
|
|
1204
1469
|
id: "windsurf",
|
|
1205
1470
|
client: "Windsurf",
|
|
@@ -1210,15 +1475,15 @@ function getMcpConfigsForSystem() {
|
|
|
1210
1475
|
return configs;
|
|
1211
1476
|
}
|
|
1212
1477
|
function installMcpServerConfig(configPath) {
|
|
1213
|
-
const dir =
|
|
1214
|
-
if (!
|
|
1215
|
-
|
|
1478
|
+
const dir = path2.dirname(configPath);
|
|
1479
|
+
if (!fs2.existsSync(dir)) {
|
|
1480
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
1216
1481
|
}
|
|
1217
1482
|
let configData = { mcpServers: {} };
|
|
1218
1483
|
let isNew = true;
|
|
1219
|
-
if (
|
|
1484
|
+
if (fs2.existsSync(configPath)) {
|
|
1220
1485
|
try {
|
|
1221
|
-
const raw =
|
|
1486
|
+
const raw = fs2.readFileSync(configPath, "utf8");
|
|
1222
1487
|
if (raw.trim()) {
|
|
1223
1488
|
configData = JSON.parse(raw);
|
|
1224
1489
|
isNew = false;
|
|
@@ -1238,7 +1503,7 @@ function installMcpServerConfig(configPath) {
|
|
|
1238
1503
|
command: "npx",
|
|
1239
1504
|
args: ["-y", "chrome-devtools-mcp@latest", "--autoConnect"]
|
|
1240
1505
|
};
|
|
1241
|
-
|
|
1506
|
+
fs2.writeFileSync(configPath, JSON.stringify(configData, null, 2) + "\n", "utf8");
|
|
1242
1507
|
return {
|
|
1243
1508
|
status: isNew ? "created" : "updated",
|
|
1244
1509
|
path: configPath
|
|
@@ -1266,8 +1531,8 @@ function runInstaller(args = process.argv) {
|
|
|
1266
1531
|
const results = [];
|
|
1267
1532
|
if (options.local) {
|
|
1268
1533
|
const cwd = process.cwd();
|
|
1269
|
-
const localCursorDir =
|
|
1270
|
-
const localPath =
|
|
1534
|
+
const localCursorDir = path2.join(cwd, ".cursor");
|
|
1535
|
+
const localPath = path2.join(localCursorDir, "mcp.json");
|
|
1271
1536
|
try {
|
|
1272
1537
|
const res = installMcpServerConfig(localPath);
|
|
1273
1538
|
results.push({
|
|
@@ -1340,7 +1605,7 @@ function runInstaller(args = process.argv) {
|
|
|
1340
1605
|
}
|
|
1341
1606
|
|
|
1342
1607
|
// src/index.ts
|
|
1343
|
-
import
|
|
1608
|
+
import fs3 from "fs";
|
|
1344
1609
|
import { fileURLToPath } from "url";
|
|
1345
1610
|
async function main() {
|
|
1346
1611
|
if (process.argv.includes("install") || process.argv.includes("setup") || process.argv.includes("--install")) {
|
|
@@ -1356,7 +1621,7 @@ function isDirectExecution() {
|
|
|
1356
1621
|
if (!process.argv[1]) return false;
|
|
1357
1622
|
try {
|
|
1358
1623
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
1359
|
-
const scriptPath =
|
|
1624
|
+
const scriptPath = fs3.existsSync(process.argv[1]) ? fs3.realpathSync(process.argv[1]) : process.argv[1];
|
|
1360
1625
|
return currentFilePath === scriptPath || process.argv[1].endsWith("index.js") || process.argv[1].endsWith("linkegringo-mcp") || process.argv[1].endsWith("mcp") || process.argv[1].endsWith("linkegringo");
|
|
1361
1626
|
} catch {
|
|
1362
1627
|
return true;
|
|
@@ -1369,10 +1634,26 @@ if (isDirectExecution()) {
|
|
|
1369
1634
|
});
|
|
1370
1635
|
}
|
|
1371
1636
|
export {
|
|
1637
|
+
auditProfileInputSchema,
|
|
1372
1638
|
checkChromeCdp,
|
|
1639
|
+
checkChromeCdpInputSchema,
|
|
1640
|
+
convertToXyzBulletInputSchema,
|
|
1373
1641
|
createLinkeGringoMcpServer,
|
|
1642
|
+
detectSparseExperiences,
|
|
1643
|
+
findDevToolsActivePort,
|
|
1644
|
+
formatGoogleXyzBullet,
|
|
1645
|
+
generateHeadlineInputSchema,
|
|
1646
|
+
getExperienceBulletCount,
|
|
1374
1647
|
getMcpConfigsForSystem,
|
|
1648
|
+
handleAuditProfile,
|
|
1649
|
+
handleCheckChromeCdp,
|
|
1650
|
+
handleConvertToXyzBullet,
|
|
1651
|
+
handleGenerateHeadline,
|
|
1652
|
+
handleSimulateRecruiterSearch,
|
|
1375
1653
|
installMcpServerConfig,
|
|
1654
|
+
isLinkeGringoUrl,
|
|
1376
1655
|
parseArgs,
|
|
1377
|
-
|
|
1656
|
+
probeViaWebSocket,
|
|
1657
|
+
runInstaller,
|
|
1658
|
+
simulateRecruiterSearchInputSchema
|
|
1378
1659
|
};
|