@reconcrap/boss-recommend-mcp 1.3.2 → 1.3.4
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/boss-chat.js
CHANGED
|
@@ -30,6 +30,69 @@ function parsePositiveInteger(value, fallback = null) {
|
|
|
30
30
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
function isUnlimitedTargetCountToken(value) {
|
|
34
|
+
const token = normalizeText(value).toLowerCase();
|
|
35
|
+
if (!token) return false;
|
|
36
|
+
return [
|
|
37
|
+
"all",
|
|
38
|
+
"unlimited",
|
|
39
|
+
"infinity",
|
|
40
|
+
"inf",
|
|
41
|
+
"max",
|
|
42
|
+
"full",
|
|
43
|
+
"全部",
|
|
44
|
+
"全量",
|
|
45
|
+
"不限",
|
|
46
|
+
"扫到底",
|
|
47
|
+
"直到完成所有人选"
|
|
48
|
+
].includes(token);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function parseBossChatTargetCount(value) {
|
|
52
|
+
if (value === undefined || value === null) {
|
|
53
|
+
return {
|
|
54
|
+
provided: false,
|
|
55
|
+
targetCount: null,
|
|
56
|
+
cliArg: null
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const raw = normalizeText(value);
|
|
60
|
+
if (!raw) {
|
|
61
|
+
return {
|
|
62
|
+
provided: false,
|
|
63
|
+
targetCount: null,
|
|
64
|
+
cliArg: null
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
if (isUnlimitedTargetCountToken(raw)) {
|
|
68
|
+
return {
|
|
69
|
+
provided: true,
|
|
70
|
+
targetCount: null,
|
|
71
|
+
cliArg: "-1"
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
const parsed = Number.parseInt(String(raw), 10);
|
|
75
|
+
if (Number.isFinite(parsed) && parsed === -1) {
|
|
76
|
+
return {
|
|
77
|
+
provided: true,
|
|
78
|
+
targetCount: null,
|
|
79
|
+
cliArg: "-1"
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
83
|
+
return {
|
|
84
|
+
provided: true,
|
|
85
|
+
targetCount: parsed,
|
|
86
|
+
cliArg: String(parsed)
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
provided: false,
|
|
91
|
+
targetCount: null,
|
|
92
|
+
cliArg: null
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
33
96
|
function parseJsonOutput(text) {
|
|
34
97
|
const trimmed = String(text || "").trim();
|
|
35
98
|
if (!trimmed) return null;
|
|
@@ -152,14 +215,16 @@ function normalizeBossChatStartInput(input = {}) {
|
|
|
152
215
|
const startFromRaw = normalizeText(input.startFrom || input.start_from).toLowerCase();
|
|
153
216
|
const startFrom = startFromRaw === "all" ? "all" : startFromRaw === "unread" ? "unread" : "";
|
|
154
217
|
const criteria = normalizeText(input.criteria);
|
|
155
|
-
const
|
|
218
|
+
const parsedTarget = parseBossChatTargetCount(input.targetCount ?? input.target_count);
|
|
156
219
|
const port = parsePositiveInteger(input.port);
|
|
157
220
|
return {
|
|
158
221
|
profile,
|
|
159
222
|
job,
|
|
160
223
|
startFrom,
|
|
161
224
|
criteria,
|
|
162
|
-
targetCount,
|
|
225
|
+
targetCount: parsedTarget.targetCount,
|
|
226
|
+
targetCountArg: parsedTarget.cliArg,
|
|
227
|
+
targetCountProvided: parsedTarget.provided,
|
|
163
228
|
port,
|
|
164
229
|
dryRun: input.dryRun === true || input.dry_run === true,
|
|
165
230
|
noState: input.noState === true || input.no_state === true,
|
|
@@ -181,7 +246,7 @@ function getMissingBossChatStartFields(input = {}) {
|
|
|
181
246
|
const missing = [];
|
|
182
247
|
if (!normalized.job) missing.push("job");
|
|
183
248
|
if (!normalized.startFrom) missing.push("start_from");
|
|
184
|
-
if (!normalized.
|
|
249
|
+
if (!normalized.targetCountProvided) missing.push("target_count");
|
|
185
250
|
if (!normalized.criteria) missing.push("criteria");
|
|
186
251
|
return missing;
|
|
187
252
|
}
|
|
@@ -194,7 +259,7 @@ function buildBossChatCliArgs(command, input, resolvedConfig) {
|
|
|
194
259
|
if (normalized.job) args.push("--job", normalized.job);
|
|
195
260
|
if (normalized.startFrom) args.push("--start-from", normalized.startFrom);
|
|
196
261
|
if (normalized.criteria) args.push("--criteria", normalized.criteria);
|
|
197
|
-
if (normalized.
|
|
262
|
+
if (normalized.targetCountArg) args.push("--targetCount", normalized.targetCountArg);
|
|
198
263
|
args.push("--port", String(normalized.port || resolvedConfig.debugPort || 9222));
|
|
199
264
|
args.push("--baseurl", resolvedConfig.baseUrl);
|
|
200
265
|
args.push("--apikey", resolvedConfig.apiKey);
|
|
@@ -210,8 +275,8 @@ function buildBossChatCliArgs(command, input, resolvedConfig) {
|
|
|
210
275
|
args.push("--job", normalized.job);
|
|
211
276
|
args.push("--start-from", normalized.startFrom);
|
|
212
277
|
args.push("--criteria", normalized.criteria);
|
|
213
|
-
if (normalized.
|
|
214
|
-
args.push("--targetCount",
|
|
278
|
+
if (normalized.targetCountArg) {
|
|
279
|
+
args.push("--targetCount", normalized.targetCountArg);
|
|
215
280
|
}
|
|
216
281
|
args.push("--baseurl", resolvedConfig.baseUrl);
|
|
217
282
|
args.push("--apikey", resolvedConfig.apiKey);
|
package/src/index.js
CHANGED
|
@@ -70,9 +70,27 @@ let runSelfHealImpl = runRecommendSelfHeal;
|
|
|
70
70
|
let spawnProcessImpl = spawn;
|
|
71
71
|
const TERMINAL_RUN_STATES = new Set([RUN_STATE_COMPLETED, RUN_STATE_FAILED, RUN_STATE_CANCELED]);
|
|
72
72
|
|
|
73
|
-
function normalizeText(value) {
|
|
74
|
-
return String(value || "").replace(/\s+/g, " ").trim();
|
|
75
|
-
}
|
|
73
|
+
function normalizeText(value) {
|
|
74
|
+
return String(value || "").replace(/\s+/g, " ").trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isUnlimitedTargetCountToken(value) {
|
|
78
|
+
const token = normalizeText(value).toLowerCase();
|
|
79
|
+
if (!token) return false;
|
|
80
|
+
return [
|
|
81
|
+
"all",
|
|
82
|
+
"unlimited",
|
|
83
|
+
"infinity",
|
|
84
|
+
"inf",
|
|
85
|
+
"max",
|
|
86
|
+
"full",
|
|
87
|
+
"全部",
|
|
88
|
+
"全量",
|
|
89
|
+
"不限",
|
|
90
|
+
"扫到底",
|
|
91
|
+
"直到完成所有人选"
|
|
92
|
+
].includes(token);
|
|
93
|
+
}
|
|
76
94
|
|
|
77
95
|
function parsePositiveInteger(raw, fallback) {
|
|
78
96
|
const value = Number.parseInt(String(raw || ""), 10);
|
|
@@ -358,8 +376,16 @@ function createRunInputSchema() {
|
|
|
358
376
|
enum: ["unread", "all"]
|
|
359
377
|
},
|
|
360
378
|
target_count: {
|
|
361
|
-
|
|
362
|
-
|
|
379
|
+
oneOf: [
|
|
380
|
+
{
|
|
381
|
+
type: "integer",
|
|
382
|
+
minimum: 1
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
type: "string",
|
|
386
|
+
enum: ["all", "unlimited", "全部", "不限", "扫到底", "全量"]
|
|
387
|
+
}
|
|
388
|
+
]
|
|
363
389
|
},
|
|
364
390
|
dry_run: { type: "boolean" },
|
|
365
391
|
no_state: { type: "boolean" },
|
|
@@ -399,9 +425,17 @@ function createBossChatStartInputSchema() {
|
|
|
399
425
|
description: "boss-chat 的筛选 criteria"
|
|
400
426
|
},
|
|
401
427
|
target_count: {
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
428
|
+
oneOf: [
|
|
429
|
+
{
|
|
430
|
+
type: "integer",
|
|
431
|
+
minimum: 1
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
type: "string",
|
|
435
|
+
enum: ["all", "unlimited", "全部", "不限", "扫到底", "全量"]
|
|
436
|
+
}
|
|
437
|
+
],
|
|
438
|
+
description: "本次处理人数上限;支持正整数或 all/不限(扫到底)"
|
|
405
439
|
},
|
|
406
440
|
port: {
|
|
407
441
|
type: "integer",
|
|
@@ -673,9 +707,13 @@ function validateBossChatStartArgs(args) {
|
|
|
673
707
|
}
|
|
674
708
|
}
|
|
675
709
|
if (Object.prototype.hasOwnProperty.call(args, "target_count")) {
|
|
676
|
-
const
|
|
677
|
-
|
|
678
|
-
|
|
710
|
+
const rawTargetCount = args.target_count;
|
|
711
|
+
const targetCount = Number.parseInt(String(rawTargetCount), 10);
|
|
712
|
+
const tokenAllowed =
|
|
713
|
+
typeof rawTargetCount === "string" && isUnlimitedTargetCountToken(rawTargetCount);
|
|
714
|
+
const numericUnlimited = Number.isFinite(targetCount) && targetCount === -1;
|
|
715
|
+
if ((!Number.isFinite(targetCount) || targetCount <= 0) && !tokenAllowed && !numericUnlimited) {
|
|
716
|
+
return "target_count must be a positive integer or one of: all, unlimited, 全部, 不限, 扫到底, 全量";
|
|
679
717
|
}
|
|
680
718
|
}
|
|
681
719
|
if (Object.prototype.hasOwnProperty.call(args, "port")) {
|
|
@@ -226,6 +226,7 @@ export class BossChatApp {
|
|
|
226
226
|
|
|
227
227
|
let consecutiveErrors = 0;
|
|
228
228
|
let exhaustedScrolls = 0;
|
|
229
|
+
const exhaustedScrollLimit = targetCount ? 3 : 8;
|
|
229
230
|
|
|
230
231
|
try {
|
|
231
232
|
while (shouldContinue(summary, targetCount)) {
|
|
@@ -336,7 +337,7 @@ export class BossChatApp {
|
|
|
336
337
|
`列表滚动:ratio=${ratio.toFixed(2)} | didScroll=${Boolean(scrollResult.didScroll)} | top=${scrollResult.after?.top ?? 'n/a'} | scrollRetry=${exhaustedScrolls + 1}`,
|
|
337
338
|
);
|
|
338
339
|
exhaustedScrolls = scrollResult.didScroll ? exhaustedScrolls + 1 : exhaustedScrolls + 2;
|
|
339
|
-
if (exhaustedScrolls >=
|
|
340
|
+
if (exhaustedScrolls >= exhaustedScrollLimit) {
|
|
340
341
|
summary.exhausted = true;
|
|
341
342
|
this.logger.log('列表滚动终止:连续无可处理候选人,判定为 exhausted。');
|
|
342
343
|
break;
|
|
@@ -629,6 +629,41 @@ function browserActivateCandidate(options = {}) {
|
|
|
629
629
|
|
|
630
630
|
function browserScrollCustomerList(options = {}) {
|
|
631
631
|
const ratio = Number(options.ratio || 0.72);
|
|
632
|
+
const clamp = (value, low, high) => Math.max(low, Math.min(high, value));
|
|
633
|
+
const isOverflowScrollable = (el) => {
|
|
634
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
635
|
+
const style = getComputedStyle(el);
|
|
636
|
+
return /(auto|scroll|overlay)/i.test(String(style.overflowY || ''));
|
|
637
|
+
};
|
|
638
|
+
const findBestScrollableContainer = (seedCard) => {
|
|
639
|
+
const candidates = [];
|
|
640
|
+
const pushCandidate = (node) => {
|
|
641
|
+
if (node instanceof HTMLElement) candidates.push(node);
|
|
642
|
+
};
|
|
643
|
+
if (seedCard instanceof HTMLElement) {
|
|
644
|
+
let current = seedCard.parentElement;
|
|
645
|
+
let depth = 0;
|
|
646
|
+
while (current && depth < 30) {
|
|
647
|
+
pushCandidate(current);
|
|
648
|
+
current = current.parentElement;
|
|
649
|
+
depth += 1;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const unique = Array.from(new Set(candidates));
|
|
653
|
+
let best = null;
|
|
654
|
+
let bestScore = -Infinity;
|
|
655
|
+
for (const node of unique) {
|
|
656
|
+
const scrollRange = Number(node.scrollHeight || 0) - Number(node.clientHeight || 0);
|
|
657
|
+
const styleBonus = isOverflowScrollable(node) ? 80 : 0;
|
|
658
|
+
const classBonus = /user|list|chat/i.test(String(node.className || '')) ? 24 : 0;
|
|
659
|
+
const score = scrollRange + styleBonus + classBonus;
|
|
660
|
+
if (score > bestScore) {
|
|
661
|
+
best = node;
|
|
662
|
+
bestScore = score;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
return best;
|
|
666
|
+
};
|
|
632
667
|
const isScrollable = (el) =>
|
|
633
668
|
el instanceof HTMLElement &&
|
|
634
669
|
Number(el.scrollHeight || 0) > Number(el.clientHeight || 0) + 16 &&
|
|
@@ -671,27 +706,59 @@ function browserScrollCustomerList(options = {}) {
|
|
|
671
706
|
return { ok: false, error: 'CHAT_LIST_CONTAINER_NOT_FOUND' };
|
|
672
707
|
}
|
|
673
708
|
|
|
709
|
+
const firstCard = listContainer.querySelector('div[role="listitem"]') || document.querySelector('div[role="listitem"]');
|
|
710
|
+
if (firstCard instanceof HTMLElement) {
|
|
711
|
+
const best = findBestScrollableContainer(firstCard);
|
|
712
|
+
if (best instanceof HTMLElement) {
|
|
713
|
+
listContainer = best;
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
674
717
|
const before = {
|
|
675
718
|
top: Number(listContainer.scrollTop || 0),
|
|
676
719
|
height: Number(listContainer.scrollHeight || 0),
|
|
677
720
|
clientHeight: Number(listContainer.clientHeight || 0),
|
|
721
|
+
cardCount: Number(listContainer.querySelectorAll('div[role="listitem"]').length || 0),
|
|
678
722
|
};
|
|
679
723
|
const amount = Math.max(120, Math.round(before.clientHeight * Math.max(0.35, Math.min(0.95, ratio))));
|
|
680
724
|
const maxScroll = Math.max(0, before.height - before.clientHeight);
|
|
681
|
-
|
|
725
|
+
if (maxScroll > 0) {
|
|
726
|
+
listContainer.scrollTop = clamp(before.top + amount, 0, maxScroll);
|
|
727
|
+
} else {
|
|
728
|
+
const cards = Array.from(listContainer.querySelectorAll('div[role="listitem"]')).filter(
|
|
729
|
+
(node) => node instanceof HTMLElement,
|
|
730
|
+
);
|
|
731
|
+
const tail = cards[cards.length - 1];
|
|
732
|
+
if (tail instanceof HTMLElement) {
|
|
733
|
+
tail.scrollIntoView({ block: 'end', inline: 'nearest' });
|
|
734
|
+
try {
|
|
735
|
+
listContainer.dispatchEvent(
|
|
736
|
+
new WheelEvent('wheel', {
|
|
737
|
+
deltaY: amount,
|
|
738
|
+
bubbles: true,
|
|
739
|
+
cancelable: true,
|
|
740
|
+
}),
|
|
741
|
+
);
|
|
742
|
+
} catch {}
|
|
743
|
+
}
|
|
744
|
+
}
|
|
682
745
|
listContainer.dispatchEvent(new Event('scroll', { bubbles: true }));
|
|
683
746
|
|
|
684
747
|
const after = {
|
|
685
748
|
top: Number(listContainer.scrollTop || 0),
|
|
686
749
|
height: Number(listContainer.scrollHeight || 0),
|
|
687
750
|
clientHeight: Number(listContainer.clientHeight || 0),
|
|
751
|
+
cardCount: Number(listContainer.querySelectorAll('div[role="listitem"]').length || 0),
|
|
688
752
|
};
|
|
689
753
|
|
|
690
754
|
return {
|
|
691
755
|
ok: true,
|
|
692
756
|
before,
|
|
693
757
|
after,
|
|
694
|
-
didScroll:
|
|
758
|
+
didScroll:
|
|
759
|
+
before.top !== after.top ||
|
|
760
|
+
before.height !== after.height ||
|
|
761
|
+
before.cardCount !== after.cardCount,
|
|
695
762
|
};
|
|
696
763
|
}
|
|
697
764
|
|
|
@@ -724,29 +791,46 @@ function browserConversationReadyState() {
|
|
|
724
791
|
const disabledAttr = Boolean(el?.hasAttribute?.('disabled'));
|
|
725
792
|
return classText.includes('disabled') || ariaDisabled || disabledAttr;
|
|
726
793
|
};
|
|
794
|
+
const isDisabledDeep = (el) => {
|
|
795
|
+
if (!(el instanceof HTMLElement)) return true;
|
|
796
|
+
let current = el;
|
|
797
|
+
let depth = 0;
|
|
798
|
+
while (current && depth < 5) {
|
|
799
|
+
if (isDisabled(current)) return true;
|
|
800
|
+
current = current.parentElement;
|
|
801
|
+
depth += 1;
|
|
802
|
+
}
|
|
803
|
+
return false;
|
|
804
|
+
};
|
|
805
|
+
const resolveAttachmentButton = () => {
|
|
806
|
+
const candidates = Array.from(
|
|
807
|
+
document.querySelectorAll(
|
|
808
|
+
'.resume-btn-file, .btn.resume-btn-file, [class*="resume-btn-file"]',
|
|
809
|
+
),
|
|
810
|
+
).filter((el) => isVisible(el));
|
|
811
|
+
const match = candidates.find((el) => {
|
|
812
|
+
const text = normalize(el.textContent || '');
|
|
813
|
+
if (!text) return false;
|
|
814
|
+
if (!text.includes('附件简历')) return false;
|
|
815
|
+
if (text.includes('求附件简历')) return false;
|
|
816
|
+
return true;
|
|
817
|
+
});
|
|
818
|
+
return match || null;
|
|
819
|
+
};
|
|
727
820
|
const onlineResume = Array.from(
|
|
728
821
|
document.querySelectorAll(
|
|
729
822
|
'a.btn.resume-btn-online, a.resume-btn-online, .resume-btn-online, .btn.resume-btn-online',
|
|
730
823
|
),
|
|
731
824
|
).find((el) => {
|
|
732
825
|
if (!isVisible(el)) return false;
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
});
|
|
736
|
-
const attachmentResume = Array.from(
|
|
737
|
-
document.querySelectorAll(
|
|
738
|
-
'.btn.resume-btn-file, .resume-btn-file, [class*="resume-btn-file"], button, a, div, span',
|
|
739
|
-
),
|
|
740
|
-
).find((el) => {
|
|
741
|
-
if (!isVisible(el)) return false;
|
|
742
|
-
if (!normalize(el.textContent || '').includes('附件简历')) return false;
|
|
743
|
-
const classText = String(el.className || '').toLowerCase();
|
|
744
|
-
return classText.includes('resume-btn-file') || classText.includes('resume') || classText.includes('btn');
|
|
826
|
+
if (!normalize(el.textContent || '').includes('在线简历')) return false;
|
|
827
|
+
return !isDisabled(el);
|
|
745
828
|
});
|
|
829
|
+
const attachmentResume = resolveAttachmentButton();
|
|
746
830
|
const askResume = Array.from(document.querySelectorAll('span.operate-btn, button, a, span')).find(
|
|
747
831
|
(el) => isVisible(el) && isAskResumeText(el.textContent || ''),
|
|
748
832
|
);
|
|
749
|
-
const attachmentResumeEnabled = Boolean(attachmentResume) && !
|
|
833
|
+
const attachmentResumeEnabled = Boolean(attachmentResume) && !isDisabledDeep(attachmentResume);
|
|
750
834
|
return {
|
|
751
835
|
hasOnlineResume: Boolean(onlineResume),
|
|
752
836
|
hasAskResume: Boolean(askResume),
|
|
@@ -131,11 +131,35 @@ function parseStartFrom(value, fallback = 'unread') {
|
|
|
131
131
|
return fallback;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
function isUnlimitedTargetCountToken(value) {
|
|
135
|
+
const token = String(value || '').trim().toLowerCase();
|
|
136
|
+
if (!token) return false;
|
|
137
|
+
return [
|
|
138
|
+
'all',
|
|
139
|
+
'unlimited',
|
|
140
|
+
'infinity',
|
|
141
|
+
'inf',
|
|
142
|
+
'max',
|
|
143
|
+
'full',
|
|
144
|
+
'全部',
|
|
145
|
+
'全量',
|
|
146
|
+
'不限',
|
|
147
|
+
'扫到底',
|
|
148
|
+
'直到完成所有人选',
|
|
149
|
+
].includes(token);
|
|
150
|
+
}
|
|
151
|
+
|
|
134
152
|
function parseTargetCount(value) {
|
|
135
153
|
if (value === undefined || value === null || String(value).trim() === '') {
|
|
136
154
|
return null;
|
|
137
155
|
}
|
|
156
|
+
if (isUnlimitedTargetCountToken(value)) {
|
|
157
|
+
return -1;
|
|
158
|
+
}
|
|
138
159
|
const parsed = Number.parseInt(String(value), 10);
|
|
160
|
+
if (Number.isFinite(parsed) && parsed === -1) {
|
|
161
|
+
return -1;
|
|
162
|
+
}
|
|
139
163
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
140
164
|
}
|
|
141
165
|
|
|
@@ -266,7 +290,7 @@ function printUsage() {
|
|
|
266
290
|
console.log(' --job <text|value|index> Select job by label/value/index');
|
|
267
291
|
console.log(' --criteria <text> Screening criteria for resume evaluation');
|
|
268
292
|
console.log(' --start-from <unread|all> Start from unread or all list');
|
|
269
|
-
console.log(' --targetCount <n>
|
|
293
|
+
console.log(' --targetCount <n|all> Maximum candidates to process; all means unlimited');
|
|
270
294
|
console.log(' --baseurl <url> Override LLM base URL');
|
|
271
295
|
console.log(' --apikey <key> Override LLM API key');
|
|
272
296
|
console.log(' --model <name> Override LLM model');
|
|
@@ -540,7 +564,9 @@ function validateStartRunArgs(args) {
|
|
|
540
564
|
const missing = [];
|
|
541
565
|
if (!args?.overrides?.jobSelection) missing.push('--job');
|
|
542
566
|
if (!args?.overrides?.startFrom) missing.push('--start-from');
|
|
543
|
-
if (
|
|
567
|
+
if (args?.overrides?.targetCount === undefined || args?.overrides?.targetCount === null) {
|
|
568
|
+
missing.push('--targetCount');
|
|
569
|
+
}
|
|
544
570
|
if (!args?.overrides?.screeningCriteria) missing.push('--criteria');
|
|
545
571
|
|
|
546
572
|
if (missing.length === 0) return null;
|
|
@@ -557,7 +583,12 @@ function validateStartRunArgs(args) {
|
|
|
557
583
|
function buildPreparePendingQuestions(args, jobs = []) {
|
|
558
584
|
const pendingQuestions = [];
|
|
559
585
|
const startFromValue = String(args?.overrides?.startFrom || '').trim().toLowerCase();
|
|
560
|
-
const targetCountValue = Number.parseInt(String(args?.overrides?.targetCount
|
|
586
|
+
const targetCountValue = Number.parseInt(String(args?.overrides?.targetCount ?? ''), 10);
|
|
587
|
+
const hasTargetCount =
|
|
588
|
+
args?.overrides?.targetCount !== undefined &&
|
|
589
|
+
args?.overrides?.targetCount !== null &&
|
|
590
|
+
Number.isFinite(targetCountValue) &&
|
|
591
|
+
(targetCountValue > 0 || targetCountValue === -1);
|
|
561
592
|
const criteriaValue = String(args?.overrides?.screeningCriteria || '').trim();
|
|
562
593
|
const jobValue = String(args?.overrides?.jobSelection || '').trim();
|
|
563
594
|
const jobOptions = jobs.map((job, index) => ({
|
|
@@ -586,10 +617,10 @@ function buildPreparePendingQuestions(args, jobs = []) {
|
|
|
586
617
|
],
|
|
587
618
|
});
|
|
588
619
|
}
|
|
589
|
-
if (!
|
|
620
|
+
if (!hasTargetCount) {
|
|
590
621
|
pendingQuestions.push({
|
|
591
622
|
field: 'target_count',
|
|
592
|
-
question: '
|
|
623
|
+
question: '请输入目标数量(正整数)或 all(扫到底)',
|
|
593
624
|
required: true,
|
|
594
625
|
});
|
|
595
626
|
}
|
|
@@ -1198,8 +1229,12 @@ async function executeRunCommand(args, dataDir) {
|
|
|
1198
1229
|
cleanupRuntimeControls = setupRuntimeControls(runControl);
|
|
1199
1230
|
|
|
1200
1231
|
logger.log('开始处理 Boss 聊天候选人列表...');
|
|
1232
|
+
const targetCountLabel =
|
|
1233
|
+
Number.isFinite(Number(runProfile.targetCount)) && Number(runProfile.targetCount) > 0
|
|
1234
|
+
? String(runProfile.targetCount)
|
|
1235
|
+
: '扫到底';
|
|
1201
1236
|
logger.log(
|
|
1202
|
-
`本次设置: 岗位=${runProfile.jobSelection.label}, 范围=${runProfile.startFrom === 'all' ? '全部' : '未读'}, 上限=${
|
|
1237
|
+
`本次设置: 岗位=${runProfile.jobSelection.label}, 范围=${runProfile.startFrom === 'all' ? '全部' : '未读'}, 上限=${targetCountLabel}`,
|
|
1203
1238
|
);
|
|
1204
1239
|
logger.log('运行中快捷键: p=暂停/继续, r=继续, q=停止, Ctrl+C=停止');
|
|
1205
1240
|
|