@reconcrap/boss-recommend-mcp 1.3.36 → 1.3.38
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/README.md +6 -2
- package/package.json +1 -1
- package/skills/boss-chat/SKILL.md +8 -0
- package/skills/boss-recommend-pipeline/SKILL.md +2 -0
- package/src/boss-chat.js +32 -27
- package/src/cli.js +8 -1
- package/src/index.js +28 -0
- package/src/pipeline.js +139 -14
- package/src/test-boss-chat.js +191 -75
- package/src/test-pipeline.js +123 -4
- package/vendor/boss-chat-cli/src/app.js +21 -16
- package/vendor/boss-chat-cli/src/cli.js +68 -50
- package/vendor/boss-chat-cli/src/services/profile-store.js +26 -21
|
@@ -189,14 +189,15 @@ function parseArgs(argv) {
|
|
|
189
189
|
json: false,
|
|
190
190
|
runId: '',
|
|
191
191
|
detachedWorker: false,
|
|
192
|
-
overrides: {
|
|
193
|
-
startFrom: undefined,
|
|
194
|
-
targetCount: undefined,
|
|
195
|
-
screeningCriteria: undefined,
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
192
|
+
overrides: {
|
|
193
|
+
startFrom: undefined,
|
|
194
|
+
targetCount: undefined,
|
|
195
|
+
screeningCriteria: undefined,
|
|
196
|
+
greetingText: undefined,
|
|
197
|
+
jobSelection: undefined,
|
|
198
|
+
llm: {},
|
|
199
|
+
chrome: {},
|
|
200
|
+
runtime: {},
|
|
200
201
|
},
|
|
201
202
|
};
|
|
202
203
|
|
|
@@ -246,14 +247,19 @@ function parseArgs(argv) {
|
|
|
246
247
|
case 'startFrom':
|
|
247
248
|
args.overrides.startFrom = parseStartFrom(value, 'unread');
|
|
248
249
|
break;
|
|
249
|
-
case 'criteria':
|
|
250
|
-
case 'screeningCriteria':
|
|
251
|
-
args.overrides.screeningCriteria = String(value || '').trim();
|
|
252
|
-
break;
|
|
253
|
-
case '
|
|
254
|
-
case '
|
|
255
|
-
|
|
256
|
-
|
|
250
|
+
case 'criteria':
|
|
251
|
+
case 'screeningCriteria':
|
|
252
|
+
args.overrides.screeningCriteria = String(value || '').trim();
|
|
253
|
+
break;
|
|
254
|
+
case 'greeting':
|
|
255
|
+
case 'greeting-text':
|
|
256
|
+
case 'greetingText':
|
|
257
|
+
args.overrides.greetingText = String(value || '').trim();
|
|
258
|
+
break;
|
|
259
|
+
case 'job':
|
|
260
|
+
case 'jobSelection':
|
|
261
|
+
args.overrides.jobSelection = String(value || '').trim();
|
|
262
|
+
break;
|
|
257
263
|
case 'baseurl':
|
|
258
264
|
case 'baseUrl':
|
|
259
265
|
args.overrides.llm.baseUrl = value || '';
|
|
@@ -401,10 +407,11 @@ function printUsage() {
|
|
|
401
407
|
console.log('Run options:');
|
|
402
408
|
console.log(' --dry-run Evaluate and click, but do not request resume');
|
|
403
409
|
console.log(' --no-state Disable in-run candidate deduplication');
|
|
404
|
-
console.log(' --job <text|value|index> Select job by label/value/index');
|
|
405
|
-
console.log(' --criteria <text> Screening criteria for resume evaluation');
|
|
406
|
-
console.log(' --
|
|
407
|
-
console.log(' --
|
|
410
|
+
console.log(' --job <text|value|index> Select job by label/value/index');
|
|
411
|
+
console.log(' --criteria <text> Screening criteria for resume evaluation');
|
|
412
|
+
console.log(' --greeting <text> Optional greeting message sent before asking for resume');
|
|
413
|
+
console.log(' --start-from <unread|all> Start from unread or all list');
|
|
414
|
+
console.log(' --targetCount <n|all> Maximum candidates to process; all means unlimited');
|
|
408
415
|
console.log(' --baseurl <url> Override LLM base URL');
|
|
409
416
|
console.log(' --apikey <key> Override LLM API key');
|
|
410
417
|
console.log(' --model <name> Override LLM model');
|
|
@@ -615,9 +622,10 @@ async function promptRunProfile({ page, persistentProfile, overrides }) {
|
|
|
615
622
|
}
|
|
616
623
|
}
|
|
617
624
|
|
|
618
|
-
let startFrom = overrides.startFrom;
|
|
619
|
-
let screeningCriteria = overrides.screeningCriteria;
|
|
620
|
-
let targetCount = overrides.targetCount;
|
|
625
|
+
let startFrom = overrides.startFrom;
|
|
626
|
+
let screeningCriteria = overrides.screeningCriteria;
|
|
627
|
+
let targetCount = overrides.targetCount;
|
|
628
|
+
let greetingText = String(overrides.greetingText || '').trim() || String(persistentProfile?.greetingText || '').trim();
|
|
621
629
|
|
|
622
630
|
if (process.stdin.isTTY) {
|
|
623
631
|
const rl = readline.createInterface({
|
|
@@ -672,10 +680,11 @@ async function promptRunProfile({ page, persistentProfile, overrides }) {
|
|
|
672
680
|
label: selectedJob.label,
|
|
673
681
|
},
|
|
674
682
|
startFrom,
|
|
675
|
-
screeningCriteria,
|
|
676
|
-
targetCount: targetCount ?? null,
|
|
677
|
-
|
|
678
|
-
}
|
|
683
|
+
screeningCriteria,
|
|
684
|
+
targetCount: targetCount ?? null,
|
|
685
|
+
greetingText,
|
|
686
|
+
});
|
|
687
|
+
}
|
|
679
688
|
|
|
680
689
|
function validateStartRunArgs(args) {
|
|
681
690
|
const missing = [];
|
|
@@ -967,10 +976,11 @@ async function handlePrepareRunCommand(args, dataDir) {
|
|
|
967
976
|
blank_chat_page: blankChatPage,
|
|
968
977
|
renavigate_attempts: renavigateAttempts,
|
|
969
978
|
required_fields: CHAT_START_REQUIRED_FIELDS.slice(),
|
|
970
|
-
defaults: {
|
|
971
|
-
profile: String(args.profile || 'default').trim() || 'default',
|
|
972
|
-
start_from: 'unread',
|
|
973
|
-
|
|
979
|
+
defaults: {
|
|
980
|
+
profile: String(args.profile || 'default').trim() || 'default',
|
|
981
|
+
start_from: 'unread',
|
|
982
|
+
greeting_text: String(args.overrides.greetingText || '').trim() || String(mergedProfile.greetingText || '').trim(),
|
|
983
|
+
},
|
|
974
984
|
job_options: jobs.map((job, index) => ({
|
|
975
985
|
index: index + 1,
|
|
976
986
|
label: String(job.label || ''),
|
|
@@ -1000,9 +1010,12 @@ async function handlePrepareRunCommand(args, dataDir) {
|
|
|
1000
1010
|
function buildDetachedRunArgs(args, runId) {
|
|
1001
1011
|
const workerArgs = [CLI_FILE_PATH, 'run', '--detached-worker', '--run-id', runId];
|
|
1002
1012
|
workerArgs.push('--profile', args.profile);
|
|
1003
|
-
workerArgs.push('--job', String(args.overrides.jobSelection));
|
|
1004
|
-
workerArgs.push('--start-from', String(args.overrides.startFrom));
|
|
1005
|
-
workerArgs.push('--criteria', String(args.overrides.screeningCriteria));
|
|
1013
|
+
workerArgs.push('--job', String(args.overrides.jobSelection));
|
|
1014
|
+
workerArgs.push('--start-from', String(args.overrides.startFrom));
|
|
1015
|
+
workerArgs.push('--criteria', String(args.overrides.screeningCriteria));
|
|
1016
|
+
if (String(args.overrides.greetingText || '').trim()) {
|
|
1017
|
+
workerArgs.push('--greeting', String(args.overrides.greetingText));
|
|
1018
|
+
}
|
|
1006
1019
|
|
|
1007
1020
|
if (args.dryRun) workerArgs.push('--dry-run');
|
|
1008
1021
|
if (args.noState) workerArgs.push('--no-state');
|
|
@@ -1084,13 +1097,17 @@ async function handleStartRunCommand(args, dataDir) {
|
|
|
1084
1097
|
dryRun: Boolean(args.dryRun),
|
|
1085
1098
|
noState: Boolean(args.noState),
|
|
1086
1099
|
input: {
|
|
1087
|
-
job: String(args.overrides.jobSelection || ''),
|
|
1088
|
-
startFrom: String(args.overrides.startFrom || ''),
|
|
1089
|
-
criteria: String(args.overrides.screeningCriteria || ''),
|
|
1090
|
-
targetCount: args.overrides.targetCount ?? null,
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1100
|
+
job: String(args.overrides.jobSelection || ''),
|
|
1101
|
+
startFrom: String(args.overrides.startFrom || ''),
|
|
1102
|
+
criteria: String(args.overrides.screeningCriteria || ''),
|
|
1103
|
+
targetCount: args.overrides.targetCount ?? null,
|
|
1104
|
+
greetingText:
|
|
1105
|
+
String(args.overrides.greetingText || '').trim()
|
|
1106
|
+
|| String(mergedProfile.greetingText || '').trim()
|
|
1107
|
+
|| null,
|
|
1108
|
+
},
|
|
1109
|
+
},
|
|
1110
|
+
});
|
|
1094
1111
|
writeRunState(dataDir, snapshot);
|
|
1095
1112
|
appendRunEvent(dataDir, runId, {
|
|
1096
1113
|
type: 'lifecycle',
|
|
@@ -1417,16 +1434,17 @@ async function executeRunCommand(args, dataDir) {
|
|
|
1417
1434
|
logger.log('检测到聊天页处于空白未初始化状态,将继续通过岗位选择和首位候选人预热来恢复列表。');
|
|
1418
1435
|
}
|
|
1419
1436
|
|
|
1420
|
-
const runProfile = await promptRunProfile({
|
|
1421
|
-
page,
|
|
1422
|
-
persistentProfile,
|
|
1423
|
-
overrides: args.overrides,
|
|
1424
|
-
});
|
|
1437
|
+
const runProfile = await promptRunProfile({
|
|
1438
|
+
page,
|
|
1439
|
+
persistentProfile,
|
|
1440
|
+
overrides: args.overrides,
|
|
1441
|
+
});
|
|
1425
1442
|
const appliedJob = await page.selectJob(runProfile.jobSelection);
|
|
1426
|
-
runProfile.jobSelection = {
|
|
1427
|
-
value: appliedJob.value || runProfile.jobSelection.value,
|
|
1428
|
-
label: appliedJob.label || runProfile.jobSelection.label,
|
|
1429
|
-
};
|
|
1443
|
+
runProfile.jobSelection = {
|
|
1444
|
+
value: appliedJob.value || runProfile.jobSelection.value,
|
|
1445
|
+
label: appliedJob.label || runProfile.jobSelection.label,
|
|
1446
|
+
};
|
|
1447
|
+
await profileStore.save(args.profile, toPersistentProfile(runProfile));
|
|
1430
1448
|
|
|
1431
1449
|
if (asyncMode) {
|
|
1432
1450
|
updateRunState(dataDir, runId, {
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const DEFAULT_GREETING_TEXT = 'Hi同学,能麻烦发下简历吗?';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_PROFILE = {
|
|
7
|
+
screeningCriteria: '',
|
|
8
|
+
targetCount: null,
|
|
9
|
+
startFrom: 'unread',
|
|
10
|
+
greetingText: DEFAULT_GREETING_TEXT,
|
|
11
|
+
jobSelection: null,
|
|
12
|
+
llm: {
|
|
10
13
|
baseUrl: '',
|
|
11
14
|
apiKey: '',
|
|
12
15
|
model: '',
|
|
@@ -74,12 +77,13 @@ function normalizeJobSelection(jobSelection) {
|
|
|
74
77
|
};
|
|
75
78
|
}
|
|
76
79
|
|
|
77
|
-
export function toPersistentProfile(profile = {}) {
|
|
78
|
-
const normalized = normalizeProfile(profile);
|
|
79
|
-
return {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
export function toPersistentProfile(profile = {}) {
|
|
81
|
+
const normalized = normalizeProfile(profile);
|
|
82
|
+
return {
|
|
83
|
+
greetingText: normalized.greetingText,
|
|
84
|
+
llm: {
|
|
85
|
+
baseUrl: normalized.llm.baseUrl,
|
|
86
|
+
apiKey: normalized.llm.apiKey,
|
|
83
87
|
model: normalized.llm.model,
|
|
84
88
|
thinkingLevel: normalized.llm.thinkingLevel,
|
|
85
89
|
timeoutMs: normalized.llm.timeoutMs,
|
|
@@ -95,12 +99,13 @@ export function toPersistentProfile(profile = {}) {
|
|
|
95
99
|
};
|
|
96
100
|
}
|
|
97
101
|
|
|
98
|
-
export function normalizeProfile(profile = {}) {
|
|
99
|
-
const merged = mergeProfile(cloneDefaultProfile(), profile);
|
|
100
|
-
merged.screeningCriteria = String(merged.screeningCriteria || '').trim();
|
|
101
|
-
merged.startFrom = String(merged.startFrom || '').trim().toLowerCase() === 'all' ? 'all' : 'unread';
|
|
102
|
-
merged.targetCount = normalizeOptionalPositiveNumber(merged.targetCount, null);
|
|
103
|
-
merged.
|
|
102
|
+
export function normalizeProfile(profile = {}) {
|
|
103
|
+
const merged = mergeProfile(cloneDefaultProfile(), profile);
|
|
104
|
+
merged.screeningCriteria = String(merged.screeningCriteria || '').trim();
|
|
105
|
+
merged.startFrom = String(merged.startFrom || '').trim().toLowerCase() === 'all' ? 'all' : 'unread';
|
|
106
|
+
merged.targetCount = normalizeOptionalPositiveNumber(merged.targetCount, null);
|
|
107
|
+
merged.greetingText = String(merged.greetingText || '').trim() || DEFAULT_GREETING_TEXT;
|
|
108
|
+
merged.jobSelection = normalizeJobSelection(merged.jobSelection);
|
|
104
109
|
merged.chrome.port = normalizeNumber(merged.chrome.port, DEFAULT_PROFILE.chrome.port);
|
|
105
110
|
merged.llm.baseUrl = String(merged.llm.baseUrl || '').trim().replace(/\/+$/, '');
|
|
106
111
|
merged.llm.apiKey = String(merged.llm.apiKey || '').trim();
|