@reconcrap/boss-recommend-mcp 1.3.24 → 1.3.25
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
CHANGED
package/src/test-boss-chat.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
import { __testables as cliTestables } from "./cli.js";
|
|
17
17
|
import { __testables as indexTestables } from "./index.js";
|
|
18
18
|
import { BossChatApp } from "../vendor/boss-chat-cli/src/app.js";
|
|
19
|
+
import { BossChatPage } from "../vendor/boss-chat-cli/src/browser/chat-page.js";
|
|
19
20
|
import { LlmClient, parseLlmJson } from "../vendor/boss-chat-cli/src/services/llm.js";
|
|
20
21
|
|
|
21
22
|
const { handleRequest } = indexTestables;
|
|
@@ -398,6 +399,90 @@ async function testBossChatPrepareShouldRetryWhenChatPageIsNotReady() {
|
|
|
398
399
|
});
|
|
399
400
|
}
|
|
400
401
|
|
|
402
|
+
async function testBossChatPageShouldTreatBlankChatShellAsOnChatPage() {
|
|
403
|
+
const fakeChromeClient = {
|
|
404
|
+
async callFunction() {
|
|
405
|
+
return {
|
|
406
|
+
href: "https://www.zhipin.com/web/chat/index",
|
|
407
|
+
readyState: "complete",
|
|
408
|
+
hasListContainer: false,
|
|
409
|
+
listItemCount: 0
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
const page = new BossChatPage(fakeChromeClient);
|
|
415
|
+
const pageState = await page.ensureOnChatPage();
|
|
416
|
+
assert.equal(pageState.href, "https://www.zhipin.com/web/chat/index");
|
|
417
|
+
|
|
418
|
+
await assert.rejects(
|
|
419
|
+
() => page.ensureReady(),
|
|
420
|
+
/CHAT_LIST_CONTAINER_NOT_FOUND/
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function testBossChatRecoverToChatIndexShouldForceNavigateAndWaitForCompleteLoad() {
|
|
425
|
+
const calls = [];
|
|
426
|
+
let stateIndex = 0;
|
|
427
|
+
const states = [
|
|
428
|
+
{
|
|
429
|
+
href: "https://www.zhipin.com/web/chat/index",
|
|
430
|
+
readyState: "loading",
|
|
431
|
+
hasListContainer: false,
|
|
432
|
+
listItemCount: 0
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
href: "https://www.zhipin.com/web/chat/index",
|
|
436
|
+
readyState: "interactive",
|
|
437
|
+
hasListContainer: false,
|
|
438
|
+
listItemCount: 0
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
href: "https://www.zhipin.com/web/chat/index",
|
|
442
|
+
readyState: "complete",
|
|
443
|
+
hasListContainer: false,
|
|
444
|
+
listItemCount: 0
|
|
445
|
+
}
|
|
446
|
+
];
|
|
447
|
+
|
|
448
|
+
const fakeChromeClient = {
|
|
449
|
+
async callFunction(fn, arg) {
|
|
450
|
+
calls.push({ name: fn.name, arg });
|
|
451
|
+
if (fn.name === "browserGetCurrentHref") {
|
|
452
|
+
return { href: "https://www.zhipin.com/web/chat/index" };
|
|
453
|
+
}
|
|
454
|
+
if (fn.name === "browserNavigateToChatIndex") {
|
|
455
|
+
return { ok: true, changed: true, href: "https://www.zhipin.com/web/chat/index" };
|
|
456
|
+
}
|
|
457
|
+
if (fn.name === "browserGetPageState") {
|
|
458
|
+
const value = states[Math.min(stateIndex, states.length - 1)];
|
|
459
|
+
stateIndex += 1;
|
|
460
|
+
return value;
|
|
461
|
+
}
|
|
462
|
+
throw new Error(`unexpected function: ${fn.name}`);
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
const page = new BossChatPage(fakeChromeClient);
|
|
467
|
+
const result = await page.recoverToChatIndex({
|
|
468
|
+
forceNavigate: true,
|
|
469
|
+
waitForReadyState: "complete",
|
|
470
|
+
maxAttempts: 5,
|
|
471
|
+
delayMs: 0
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
assert.equal(result.changed, true);
|
|
475
|
+
assert.equal(result.href, "https://www.zhipin.com/web/chat/index");
|
|
476
|
+
assert.equal(
|
|
477
|
+
calls.some((entry) => entry.name === "browserNavigateToChatIndex" && entry.arg?.force === true),
|
|
478
|
+
true
|
|
479
|
+
);
|
|
480
|
+
assert.equal(
|
|
481
|
+
calls.filter((entry) => entry.name === "browserGetPageState").length >= 3,
|
|
482
|
+
true
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
|
|
401
486
|
async function testBossChatMcpToolsShouldValidateAndRoute() {
|
|
402
487
|
await withBossChatWorkspace(async (workspaceRoot) => {
|
|
403
488
|
const toolsResponse = await handleRequest({
|
|
@@ -941,6 +1026,8 @@ async function testBossChatAppShouldPersistEvidenceArtifacts() {
|
|
|
941
1026
|
async function main() {
|
|
942
1027
|
await testBossChatAdapterShouldResolveSharedConfigAndInvokeLocalCli();
|
|
943
1028
|
await testBossChatPrepareShouldRetryWhenChatPageIsNotReady();
|
|
1029
|
+
await testBossChatPageShouldTreatBlankChatShellAsOnChatPage();
|
|
1030
|
+
await testBossChatRecoverToChatIndexShouldForceNavigateAndWaitForCompleteLoad();
|
|
944
1031
|
await testBossChatMcpToolsShouldValidateAndRoute();
|
|
945
1032
|
await testBossChatCliShouldSupportRunAndFollowUpParsing();
|
|
946
1033
|
testBossChatLlmEvidenceGateShouldDemoteMissingEvidence();
|
|
@@ -24,6 +24,7 @@ function browserGetPageState() {
|
|
|
24
24
|
|
|
25
25
|
return {
|
|
26
26
|
href: window.location.href,
|
|
27
|
+
readyState: document.readyState,
|
|
27
28
|
hasListContainer: Boolean(listContainer),
|
|
28
29
|
listItemCount: listItems.length,
|
|
29
30
|
};
|
|
@@ -33,9 +34,10 @@ function browserGetCurrentHref() {
|
|
|
33
34
|
return { href: window.location.href };
|
|
34
35
|
}
|
|
35
36
|
|
|
36
|
-
function browserNavigateToChatIndex() {
|
|
37
|
+
function browserNavigateToChatIndex(options = {}) {
|
|
37
38
|
const chatUrl = 'https://www.zhipin.com/web/chat/index';
|
|
38
|
-
|
|
39
|
+
const force = options?.force === true;
|
|
40
|
+
if (force || !String(window.location.href || '').includes('/web/chat/index')) {
|
|
39
41
|
window.location.assign(chatUrl);
|
|
40
42
|
return { ok: true, changed: true, href: chatUrl };
|
|
41
43
|
}
|
|
@@ -2236,11 +2238,20 @@ export class BossChatPage {
|
|
|
2236
2238
|
return target?.type === 'page' && String(target.url || '').includes(CHAT_URL_TOKEN);
|
|
2237
2239
|
}
|
|
2238
2240
|
|
|
2239
|
-
async
|
|
2240
|
-
|
|
2241
|
+
async getPageState() {
|
|
2242
|
+
return this.chromeClient.callFunction(browserGetPageState);
|
|
2243
|
+
}
|
|
2244
|
+
|
|
2245
|
+
async ensureOnChatPage() {
|
|
2246
|
+
const pageState = await this.getPageState();
|
|
2241
2247
|
if (!pageState?.href?.includes(CHAT_URL_TOKEN)) {
|
|
2242
2248
|
throw new Error('ACTIVE_TAB_IS_NOT_BOSS_CHAT_PAGE');
|
|
2243
2249
|
}
|
|
2250
|
+
return pageState;
|
|
2251
|
+
}
|
|
2252
|
+
|
|
2253
|
+
async ensureReady() {
|
|
2254
|
+
const pageState = await this.ensureOnChatPage();
|
|
2244
2255
|
if (!pageState.hasListContainer && Number(pageState.listItemCount || 0) <= 0) {
|
|
2245
2256
|
throw new Error('CHAT_LIST_CONTAINER_NOT_FOUND');
|
|
2246
2257
|
}
|
|
@@ -2250,16 +2261,20 @@ export class BossChatPage {
|
|
|
2250
2261
|
async recoverToChatIndex(options = {}) {
|
|
2251
2262
|
const maxAttempts = options.maxAttempts || 20;
|
|
2252
2263
|
const delayMs = options.delayMs || 500;
|
|
2264
|
+
const forceNavigate = options.forceNavigate === true;
|
|
2265
|
+
const waitForReadyState = options.waitForReadyState || 'complete';
|
|
2253
2266
|
const hrefResult = await this.chromeClient.callFunction(browserGetCurrentHref);
|
|
2254
|
-
if (String(hrefResult?.href || '').includes(CHAT_URL_TOKEN)) {
|
|
2267
|
+
if (!forceNavigate && String(hrefResult?.href || '').includes(CHAT_URL_TOKEN)) {
|
|
2255
2268
|
return { changed: false, href: hrefResult?.href || '' };
|
|
2256
2269
|
}
|
|
2257
2270
|
|
|
2258
|
-
await this.chromeClient.callFunction(browserNavigateToChatIndex);
|
|
2271
|
+
await this.chromeClient.callFunction(browserNavigateToChatIndex, { force: forceNavigate });
|
|
2259
2272
|
for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
|
|
2260
2273
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
2261
|
-
const state = await this.
|
|
2262
|
-
|
|
2274
|
+
const state = await this.getPageState();
|
|
2275
|
+
const onChatPage = String(state?.href || '').includes(CHAT_URL_TOKEN);
|
|
2276
|
+
const ready = !waitForReadyState || String(state?.readyState || '').toLowerCase() === String(waitForReadyState).toLowerCase();
|
|
2277
|
+
if (onChatPage && ready) {
|
|
2263
2278
|
return { changed: true, href: state.href };
|
|
2264
2279
|
}
|
|
2265
2280
|
}
|
|
@@ -44,6 +44,7 @@ const CLI_FILE_PATH = fileURLToPath(import.meta.url);
|
|
|
44
44
|
const MINIMAL_TERMINAL_PATTERNS = [/^进度: /, /^候选人结果: /];
|
|
45
45
|
const CHAT_INDEX_URL = 'https://www.zhipin.com/web/chat/index';
|
|
46
46
|
const CHAT_START_REQUIRED_FIELDS = ['job', 'start_from', 'target_count', 'criteria'];
|
|
47
|
+
const CHAT_PAGE_RENAVIGATE_MAX_ATTEMPTS = 3;
|
|
47
48
|
|
|
48
49
|
function sanitizePathToken(value, fallback = 'run') {
|
|
49
50
|
const token = String(value || '')
|
|
@@ -651,6 +652,8 @@ async function connectBossChatPage(chromeClient) {
|
|
|
651
652
|
target?.type === 'page' && /zhipin\.com/i.test(String(target?.url || ''));
|
|
652
653
|
let target = null;
|
|
653
654
|
let recoveredToChatIndex = false;
|
|
655
|
+
let blankChatPage = false;
|
|
656
|
+
let renavigateAttempts = 0;
|
|
654
657
|
|
|
655
658
|
try {
|
|
656
659
|
target = await chromeClient.connect(BossChatPage.targetMatcher);
|
|
@@ -659,15 +662,41 @@ async function connectBossChatPage(chromeClient) {
|
|
|
659
662
|
}
|
|
660
663
|
|
|
661
664
|
const page = new BossChatPage(chromeClient);
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
665
|
+
for (let attempt = 1; attempt <= CHAT_PAGE_RENAVIGATE_MAX_ATTEMPTS + 1; attempt += 1) {
|
|
666
|
+
try {
|
|
667
|
+
await page.ensureReady();
|
|
668
|
+
return {
|
|
669
|
+
target,
|
|
670
|
+
page,
|
|
671
|
+
recoveredToChatIndex,
|
|
672
|
+
blankChatPage,
|
|
673
|
+
renavigateAttempts,
|
|
674
|
+
};
|
|
675
|
+
} catch (error) {
|
|
676
|
+
const message = String(error?.message || error || '');
|
|
677
|
+
const canRetry =
|
|
678
|
+
/ACTIVE_TAB_IS_NOT_BOSS_CHAT_PAGE|CHAT_LIST_CONTAINER_NOT_FOUND/.test(message)
|
|
679
|
+
&& attempt <= CHAT_PAGE_RENAVIGATE_MAX_ATTEMPTS;
|
|
680
|
+
|
|
681
|
+
if (!canRetry) {
|
|
682
|
+
if (/CHAT_LIST_CONTAINER_NOT_FOUND/.test(message)) {
|
|
683
|
+
blankChatPage = true;
|
|
684
|
+
await page.ensureOnChatPage();
|
|
685
|
+
break;
|
|
686
|
+
}
|
|
687
|
+
throw error;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
await page.recoverToChatIndex({
|
|
691
|
+
forceNavigate: true,
|
|
692
|
+
waitForReadyState: 'complete',
|
|
693
|
+
});
|
|
694
|
+
recoveredToChatIndex = true;
|
|
695
|
+
renavigateAttempts += 1;
|
|
696
|
+
}
|
|
668
697
|
}
|
|
669
698
|
|
|
670
|
-
return { target, page, recoveredToChatIndex };
|
|
699
|
+
return { target, page, recoveredToChatIndex, blankChatPage, renavigateAttempts };
|
|
671
700
|
}
|
|
672
701
|
|
|
673
702
|
async function handlePrepareRunCommand(args, dataDir) {
|
|
@@ -704,7 +733,7 @@ async function handlePrepareRunCommand(args, dataDir) {
|
|
|
704
733
|
let chromeClient = null;
|
|
705
734
|
try {
|
|
706
735
|
chromeClient = new ChromeClient(mergedProfile.chrome.port);
|
|
707
|
-
const { target, page, recoveredToChatIndex } = await connectBossChatPage(chromeClient);
|
|
736
|
+
const { target, page, recoveredToChatIndex, blankChatPage, renavigateAttempts } = await connectBossChatPage(chromeClient);
|
|
708
737
|
const jobs = await page.listJobs();
|
|
709
738
|
if (!Array.isArray(jobs) || jobs.length === 0) {
|
|
710
739
|
return {
|
|
@@ -723,6 +752,8 @@ async function handlePrepareRunCommand(args, dataDir) {
|
|
|
723
752
|
page_url: CHAT_INDEX_URL,
|
|
724
753
|
connected_target: target?.url || '',
|
|
725
754
|
recovered_to_chat_index: recoveredToChatIndex,
|
|
755
|
+
blank_chat_page: blankChatPage,
|
|
756
|
+
renavigate_attempts: renavigateAttempts,
|
|
726
757
|
required_fields: CHAT_START_REQUIRED_FIELDS.slice(),
|
|
727
758
|
defaults: {
|
|
728
759
|
profile: String(args.profile || 'default').trim() || 'default',
|
|
@@ -1159,10 +1190,13 @@ async function executeRunCommand(args, dataDir) {
|
|
|
1159
1190
|
|
|
1160
1191
|
chromeClient = new ChromeClient(persistentProfile.chrome.port);
|
|
1161
1192
|
|
|
1162
|
-
const { target, page, recoveredToChatIndex } = await connectBossChatPage(chromeClient);
|
|
1193
|
+
const { target, page, recoveredToChatIndex, blankChatPage, renavigateAttempts } = await connectBossChatPage(chromeClient);
|
|
1163
1194
|
logger.log(`已连接 Chrome tab: ${target.title || target.url}`);
|
|
1164
1195
|
if (recoveredToChatIndex) {
|
|
1165
|
-
logger.log(
|
|
1196
|
+
logger.log(`检测到页面不符合预期,已重新跳转到 ${CHAT_INDEX_URL} 并等待加载完成。attempts=${renavigateAttempts}`);
|
|
1197
|
+
}
|
|
1198
|
+
if (blankChatPage) {
|
|
1199
|
+
logger.log('检测到聊天页处于空白未初始化状态,将继续通过岗位选择和首位候选人预热来恢复列表。');
|
|
1166
1200
|
}
|
|
1167
1201
|
|
|
1168
1202
|
const runProfile = await promptRunProfile({
|