@imenam/simple-scraper 1.0.9 → 1.0.12

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/gui.js CHANGED
@@ -20,6 +20,47 @@ if (!process.env.PROXY_URL) {
20
20
  process.stderr.write('[GUI] PROXY_URL non défini — arrêt.\n');
21
21
  process.exit(0);
22
22
  }
23
+ const APP_PATH = process.env.PROXY_APP_PATH ?? '/simple-scraper-mcp';
24
+ const APP_NAME = process.env.PROXY_APP_NAME ?? 'Simple Scraper MCP';
25
+ // ─── Cycle de vie du process ─────────────────────────────────────────────────
26
+ let proxyClient = null;
27
+ /**
28
+ * Libère la route avant de mourir. Sans ça le proxy continue de router vers un
29
+ * port mort (502 opaques) jusqu'à ce que son health check finisse par nettoyer.
30
+ */
31
+ async function cleanupProxy() {
32
+ if (proxyClient)
33
+ await proxyClient.unregister();
34
+ process.exit(0);
35
+ }
36
+ process.on('SIGTERM', () => void cleanupProxy());
37
+ process.on('SIGINT', () => void cleanupProxy());
38
+ // Le master peut disparaître sans nous tuer (crash, kill -9) : sans ce watchdog
39
+ // la GUI reste orpheline et garde le port et la route du proxy.
40
+ const parentPid = process.ppid;
41
+ const parentCheckInterval = setInterval(() => {
42
+ try {
43
+ process.kill(parentPid, 0);
44
+ }
45
+ catch {
46
+ console.error(`[GUI] Master (PID ${parentPid}) disparu — arrêt.`);
47
+ clearInterval(parentCheckInterval);
48
+ void cleanupProxy();
49
+ }
50
+ }, 2000);
51
+ process.on('disconnect', () => {
52
+ console.error('[GUI] Canal IPC fermé par le master — arrêt.');
53
+ void cleanupProxy();
54
+ });
55
+ process.on('uncaughtException', (err) => {
56
+ console.error(`[GUI] Uncaught Exception: ${err.message}`);
57
+ console.error(err.stack ?? 'No stack trace');
58
+ process.exit(1);
59
+ });
60
+ process.on('unhandledRejection', (reason) => {
61
+ console.error(`[GUI] Unhandled Rejection: ${reason}`);
62
+ process.exit(1);
63
+ });
23
64
  const getCookiesDir = () => process.env.COOKIES_DIR ?? null;
24
65
  const app = express();
25
66
  app.use(bodyParser.json());
@@ -130,20 +171,33 @@ app.get('/api/sessions/:id/screenshot', async (req, res) => {
130
171
  }
131
172
  });
132
173
  async function startServer() {
133
- const proxyClient = new ProxyClient(process.env.PROXY_URL);
134
- const result = await proxyClient.register({
135
- path: process.env.PROXY_APP_PATH ?? '/simple-scraper-mcp',
136
- name: process.env.PROXY_APP_NAME ?? 'Simple Scraper MCP',
137
- });
174
+ const client = new ProxyClient(process.env.PROXY_URL);
175
+ const result = await client.register({ path: APP_PATH, name: APP_NAME });
176
+ // Une autre instance du serveur MCP sert déjà cette GUI. C'est le cas nominal
177
+ // quand le client redémarre le serveur : il lance le nouveau master avant de
178
+ // couper l'ancien, dont la GUI tient encore la route. Ce n'est pas un crash —
179
+ // on sort en 0 (sinon le launcher log "Giving up") sans toucher au proxy, dont
180
+ // la route appartient à l'autre instance.
181
+ if (result.error === 'HTTP 409') {
182
+ console.error('[GUI] Une autre instance sert déjà cette GUI — arrêt.');
183
+ ipc.send({
184
+ type: 'ALREADY_RUNNING',
185
+ data: { url: `${process.env.PROXY_URL}${APP_PATH}` },
186
+ timestamp: new Date().toISOString(),
187
+ });
188
+ process.exit(0);
189
+ }
138
190
  if (!result.success) {
139
- process.stderr.write('[GUI] Enregistrement proxy échoué — arrêt.\n');
191
+ process.stderr.write(`[GUI] Enregistrement proxy échoué (${result.error}) — arrêt.\n`);
140
192
  process.exit(1);
141
193
  }
194
+ // À partir d'ici la route nous appartient : cleanupProxy peut la libérer.
195
+ proxyClient = client;
142
196
  const port = result.port;
143
197
  app.post('/api/stop', async (_req, res) => {
144
198
  console.error('POST /api/stop called');
145
199
  res.json({ success: true, message: 'Stopping...' });
146
- await proxyClient.unregister();
200
+ await client.unregister();
147
201
  setTimeout(() => {
148
202
  if (process.send && process.connected) {
149
203
  process.send({ type: 'STOP' });
package/dist/index.js CHANGED
@@ -20,8 +20,12 @@ setupLogging('MASTER');
20
20
  const launcher = new GuiLauncher({
21
21
  guiPath: path.join(__dirname, 'gui.js'),
22
22
  onMessage: (msg) => {
23
- if (msg.type === 'STOP')
23
+ if (msg.type === 'STOP') {
24
24
  process.exit(0);
25
+ }
26
+ else if (msg.type === 'ALREADY_RUNNING') {
27
+ console.error(`[MASTER] GUI déjà servie par une autre instance : ${msg.data?.url ?? 'URL inconnue'}`);
28
+ }
25
29
  },
26
30
  });
27
31
  const server = new McpServer({
@@ -609,17 +613,21 @@ server.tool('screenshot', 'Take a screenshot of a page. Provide either session_i
609
613
  }
610
614
  }
611
615
  });
612
- async function main() {
616
+ function launchGUI() {
613
617
  if (!process.env.PROXY_URL) {
614
618
  console.error('[MASTER] PROXY_URL non défini — GUI désactivée.');
619
+ return;
615
620
  }
616
- else {
617
- launcher.start();
618
- registerSessionRpc(launcher);
619
- }
621
+ launcher.start();
622
+ registerSessionRpc(launcher);
623
+ }
624
+ async function main() {
620
625
  const transport = new StdioServerTransport();
621
626
  await server.connect(transport);
622
627
  console.error('[MCP] Simple Scraper MCP server started');
628
+ // Après le connect, comme mcp-documentor : au redémarrage du serveur par le
629
+ // client, le master sortant a alors eu le temps de libérer la route.
630
+ launchGUI();
623
631
  }
624
632
  main().catch(async (err) => {
625
633
  console.error('[MCP] Fatal error:', err);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imenam/simple-scraper",
3
- "version": "1.0.9",
3
+ "version": "1.0.12",
4
4
  "description": "MCP server for web scraping and JavaScript execution using Puppeteer",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",