@lightcone-ai/daemon 0.15.21 → 0.15.23

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.
@@ -36,6 +36,20 @@ const cache = new Map();
36
36
  let lastRequestAt = 0;
37
37
  let session = null;
38
38
 
39
+ function readSavedSession(profileDir) {
40
+ const sessionFile = path.join(profileDir, 'session.json');
41
+ if (!existsSync(sessionFile)) {
42
+ throw new Error('LOGIN_REQUIRED:WeChat MP backend session token is missing; please scan login again');
43
+ }
44
+ const saved = parseJsonMaybe(readFileSync(sessionFile, 'utf8'));
45
+ const token = String(saved?.token ?? '').trim();
46
+ const homeUrl = String(saved?.homeUrl ?? '').trim();
47
+ if (!token || !homeUrl.startsWith(`${WECHAT_MP_ORIGIN}/cgi-bin/`)) {
48
+ throw new Error('LOGIN_REQUIRED:WeChat MP backend session token is invalid; please scan login again');
49
+ }
50
+ return { token, homeUrl, savedAt: saved?.savedAt ?? null };
51
+ }
52
+
39
53
  function detectChrome() {
40
54
  if (process.env.CHROME_BIN) return process.env.CHROME_BIN;
41
55
  const candidates = [
@@ -85,6 +99,7 @@ class CdpSession {
85
99
 
86
100
  async launch(profileDir) {
87
101
  if (!profileDir) throw new Error('LOGIN_REQUIRED:WECHAT_MP_PROFILE_DIR is not configured');
102
+ const savedSession = readSavedSession(profileDir);
88
103
  const chrome = detectChrome();
89
104
  this._port = await getFreePort();
90
105
 
@@ -120,7 +135,7 @@ class CdpSession {
120
135
  await this.cdp('Network.enable');
121
136
  await this.cdp('Runtime.enable');
122
137
  await this.injectCookies(profileDir);
123
- await this.navigate(`${WECHAT_MP_ORIGIN}/cgi-bin/home?t=home/index&lang=zh_CN`);
138
+ await this.navigate(savedSession.homeUrl);
124
139
  }
125
140
 
126
141
  _connectWs(wsUrl) {
@@ -220,6 +235,19 @@ class CdpSession {
220
235
  async currentUrl() {
221
236
  return this.evalValue('location.href');
222
237
  }
238
+
239
+ async close() {
240
+ try { await this.cdp('Browser.close', {}, 3000); } catch {}
241
+ await sleep(300);
242
+ if (this._ws) {
243
+ try { this._ws.close(); } catch {}
244
+ this._ws = null;
245
+ }
246
+ if (this._proc) {
247
+ try { this._proc.kill('SIGKILL'); } catch {}
248
+ this._proc = null;
249
+ }
250
+ }
223
251
  }
224
252
 
225
253
  async function getSession() {
@@ -229,23 +257,40 @@ async function getSession() {
229
257
  return session;
230
258
  }
231
259
 
260
+ async function closeSession() {
261
+ const active = session;
262
+ session = null;
263
+ if (active) await active.close();
264
+ }
265
+
232
266
  async function ensureLoggedIn() {
233
267
  const cdp = await getSession();
268
+ const savedSession = readSavedSession(WECHAT_MP_PROFILE_DIR);
234
269
  let href = await cdp.currentUrl().catch(() => '');
235
270
  if (!String(href).startsWith(WECHAT_MP_ORIGIN)) {
236
- await cdp.navigate(`${WECHAT_MP_ORIGIN}/cgi-bin/home?t=home/index&lang=zh_CN`);
271
+ await cdp.navigate(savedSession.homeUrl);
237
272
  href = await cdp.currentUrl();
238
273
  }
239
- let token = '';
274
+ let token = savedSession.token;
240
275
  for (let i = 0; i < 12; i += 1) {
241
276
  href = await cdp.currentUrl().catch(() => href);
242
- token = new URL(String(href)).searchParams.get('token') ?? '';
277
+ token = new URL(String(href)).searchParams.get('token') ?? token;
243
278
  if (token) break;
244
279
  await sleep(500);
245
280
  }
281
+ const pageState = await cdp.evalValue(`(() => {
282
+ const text = document.body?.innerText || '';
283
+ return {
284
+ href: location.href,
285
+ loginRequired: text.includes('登录超时') || text.includes('请重新登录') || text.includes('重新登录')
286
+ };
287
+ })()`).catch(() => null);
246
288
  if (!token) {
247
289
  throw new Error('LOGIN_REQUIRED:WeChat MP backend login is required');
248
290
  }
291
+ if (pageState?.loginRequired) {
292
+ throw new Error('LOGIN_REQUIRED:WeChat MP backend session expired; please scan login again');
293
+ }
249
294
  return { cdp, token };
250
295
  }
251
296
 
@@ -461,6 +506,8 @@ server.tool(
461
506
  return toolResult({ ok: true, logged_in: true, token_present: Boolean(token), source_type: 'wechat_mp' });
462
507
  } catch (error) {
463
508
  return toolError(error);
509
+ } finally {
510
+ await closeSession();
464
511
  }
465
512
  }
466
513
  );
@@ -478,6 +525,8 @@ server.tool(
478
525
  return toolResult(await searchAccounts(checked));
479
526
  } catch (error) {
480
527
  return toolError(error);
528
+ } finally {
529
+ await closeSession();
481
530
  }
482
531
  }
483
532
  );
@@ -496,6 +545,8 @@ server.tool(
496
545
  return toolResult(await listRecentArticles(checked));
497
546
  } catch (error) {
498
547
  return toolError(error);
548
+ } finally {
549
+ await closeSession();
499
550
  }
500
551
  }
501
552
  );
@@ -519,3 +570,7 @@ server.tool(
519
570
  const transport = new StdioServerTransport();
520
571
  await server.connect(transport);
521
572
  console.error('[wechat-mp-fetch] MCP Server started');
573
+
574
+ process.once('SIGINT', () => { closeSession().finally(() => process.exit(130)); });
575
+ process.once('SIGTERM', () => { closeSession().finally(() => process.exit(143)); });
576
+ process.once('beforeExit', () => { if (session) void closeSession(); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.15.21",
3
+ "version": "0.15.23",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -106,19 +106,56 @@ export const PLATFORM_CONFIGS = {
106
106
  },
107
107
  async isPageLoggedIn(session) {
108
108
  const result = await session.send('Runtime.evaluate', {
109
- expression: `(() => {
109
+ expression: `(async () => {
110
110
  const href = location.href;
111
111
  const text = document.body?.innerText || '';
112
+ const token = new URL(href).searchParams.get('token') || '';
113
+ if (!/[?&]token=\\d+/.test(href) || text.includes('登录超时') || text.includes('重新登录')) {
114
+ return {
115
+ href,
116
+ hasToken: Boolean(token),
117
+ loginTimedOut: text.includes('登录超时') || text.includes('重新登录'),
118
+ apiOk: false
119
+ };
120
+ }
121
+
122
+ const url = new URL('/cgi-bin/searchbiz', location.origin);
123
+ url.searchParams.set('action', 'search_biz');
124
+ url.searchParams.set('begin', '0');
125
+ url.searchParams.set('count', '1');
126
+ url.searchParams.set('query', '腾讯');
127
+ url.searchParams.set('token', token);
128
+ url.searchParams.set('lang', 'zh_CN');
129
+ url.searchParams.set('f', 'json');
130
+ url.searchParams.set('ajax', '1');
131
+
132
+ let apiOk = false;
133
+ try {
134
+ const response = await fetch(url.toString(), {
135
+ credentials: 'include',
136
+ headers: {
137
+ 'Accept': 'application/json, text/javascript, */*; q=0.01',
138
+ 'X-Requested-With': 'XMLHttpRequest'
139
+ }
140
+ });
141
+ const payload = await response.json();
142
+ apiOk = response.ok && Number(payload?.base_resp?.ret ?? 0) === 0 && Array.isArray(payload?.list);
143
+ } catch {
144
+ apiOk = false;
145
+ }
146
+
112
147
  return {
113
148
  href,
114
- hasToken: /[?&]token=\\d+/.test(href),
115
- loginTimedOut: text.includes('登录超时') || text.includes('重新登录')
149
+ hasToken: Boolean(token),
150
+ loginTimedOut: false,
151
+ apiOk
116
152
  };
117
153
  })()`,
118
154
  returnByValue: true,
155
+ awaitPromise: true,
119
156
  }, 5000);
120
157
  const value = result?.result?.value ?? {};
121
- return Boolean(value.hasToken) && !value.loginTimedOut;
158
+ return Boolean(value.hasToken) && !value.loginTimedOut && Boolean(value.apiOk);
122
159
  },
123
160
  },
124
161
  };
@@ -453,8 +490,25 @@ export class BrowserLoginSession {
453
490
  );
454
491
  writeFileSync(path.join(this._profileDir, 'cookies.json'), JSON.stringify(cookies));
455
492
  console.log(`[BrowserLogin][${this._platform}] Saved ${cookies.length} cookies to cookies.json`);
493
+ if (this._platform === 'wechat_mp') {
494
+ const hrefResult = await this.send('Runtime.evaluate', {
495
+ expression: 'location.href',
496
+ returnByValue: true,
497
+ }, 5000);
498
+ const homeUrl = String(hrefResult?.result?.value ?? '');
499
+ const token = new URL(homeUrl).searchParams.get('token') ?? '';
500
+ if (!token) throw new Error('WeChat MP token missing from logged-in URL');
501
+ writeFileSync(path.join(this._profileDir, 'session.json'), JSON.stringify({
502
+ platform: this._platform,
503
+ token,
504
+ homeUrl,
505
+ savedAt: new Date().toISOString(),
506
+ }));
507
+ console.log(`[BrowserLogin][${this._platform}] Saved backend session token to session.json`);
508
+ }
456
509
  } catch (err) {
457
- console.error(`[BrowserLogin][${this._platform}] Failed to save cookies: ${err.message}`);
510
+ console.error(`[BrowserLogin][${this._platform}] Failed to save browser session: ${err.message}`);
511
+ if (this._platform === 'wechat_mp') throw err;
458
512
  }
459
513
  // Kill any stale chrome-pool session so the next publish job starts fresh with new cookies
460
514
  try {