@reconcrap/boss-recommend-mcp 1.3.3 → 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;
|
|
@@ -630,6 +630,40 @@ function browserActivateCandidate(options = {}) {
|
|
|
630
630
|
function browserScrollCustomerList(options = {}) {
|
|
631
631
|
const ratio = Number(options.ratio || 0.72);
|
|
632
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
|
+
};
|
|
633
667
|
const isScrollable = (el) =>
|
|
634
668
|
el instanceof HTMLElement &&
|
|
635
669
|
Number(el.scrollHeight || 0) > Number(el.clientHeight || 0) + 16 &&
|
|
@@ -672,27 +706,59 @@ function browserScrollCustomerList(options = {}) {
|
|
|
672
706
|
return { ok: false, error: 'CHAT_LIST_CONTAINER_NOT_FOUND' };
|
|
673
707
|
}
|
|
674
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
|
+
|
|
675
717
|
const before = {
|
|
676
718
|
top: Number(listContainer.scrollTop || 0),
|
|
677
719
|
height: Number(listContainer.scrollHeight || 0),
|
|
678
720
|
clientHeight: Number(listContainer.clientHeight || 0),
|
|
721
|
+
cardCount: Number(listContainer.querySelectorAll('div[role="listitem"]').length || 0),
|
|
679
722
|
};
|
|
680
723
|
const amount = Math.max(120, Math.round(before.clientHeight * Math.max(0.35, Math.min(0.95, ratio))));
|
|
681
724
|
const maxScroll = Math.max(0, before.height - before.clientHeight);
|
|
682
|
-
|
|
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
|
+
}
|
|
683
745
|
listContainer.dispatchEvent(new Event('scroll', { bubbles: true }));
|
|
684
746
|
|
|
685
747
|
const after = {
|
|
686
748
|
top: Number(listContainer.scrollTop || 0),
|
|
687
749
|
height: Number(listContainer.scrollHeight || 0),
|
|
688
750
|
clientHeight: Number(listContainer.clientHeight || 0),
|
|
751
|
+
cardCount: Number(listContainer.querySelectorAll('div[role="listitem"]').length || 0),
|
|
689
752
|
};
|
|
690
753
|
|
|
691
754
|
return {
|
|
692
755
|
ok: true,
|
|
693
756
|
before,
|
|
694
757
|
after,
|
|
695
|
-
didScroll:
|
|
758
|
+
didScroll:
|
|
759
|
+
before.top !== after.top ||
|
|
760
|
+
before.height !== after.height ||
|
|
761
|
+
before.cardCount !== after.cardCount,
|
|
696
762
|
};
|
|
697
763
|
}
|
|
698
764
|
|
|
@@ -725,29 +791,46 @@ function browserConversationReadyState() {
|
|
|
725
791
|
const disabledAttr = Boolean(el?.hasAttribute?.('disabled'));
|
|
726
792
|
return classText.includes('disabled') || ariaDisabled || disabledAttr;
|
|
727
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
|
+
};
|
|
728
820
|
const onlineResume = Array.from(
|
|
729
821
|
document.querySelectorAll(
|
|
730
822
|
'a.btn.resume-btn-online, a.resume-btn-online, .resume-btn-online, .btn.resume-btn-online',
|
|
731
823
|
),
|
|
732
824
|
).find((el) => {
|
|
733
825
|
if (!isVisible(el)) return false;
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
});
|
|
737
|
-
const attachmentResume = Array.from(
|
|
738
|
-
document.querySelectorAll(
|
|
739
|
-
'.btn.resume-btn-file, .resume-btn-file, [class*="resume-btn-file"], button, a, div, span',
|
|
740
|
-
),
|
|
741
|
-
).find((el) => {
|
|
742
|
-
if (!isVisible(el)) return false;
|
|
743
|
-
if (!normalize(el.textContent || '').includes('附件简历')) return false;
|
|
744
|
-
const classText = String(el.className || '').toLowerCase();
|
|
745
|
-
return classText.includes('resume-btn-file') || classText.includes('resume') || classText.includes('btn');
|
|
826
|
+
if (!normalize(el.textContent || '').includes('在线简历')) return false;
|
|
827
|
+
return !isDisabled(el);
|
|
746
828
|
});
|
|
829
|
+
const attachmentResume = resolveAttachmentButton();
|
|
747
830
|
const askResume = Array.from(document.querySelectorAll('span.operate-btn, button, a, span')).find(
|
|
748
831
|
(el) => isVisible(el) && isAskResumeText(el.textContent || ''),
|
|
749
832
|
);
|
|
750
|
-
const attachmentResumeEnabled = Boolean(attachmentResume) && !
|
|
833
|
+
const attachmentResumeEnabled = Boolean(attachmentResume) && !isDisabledDeep(attachmentResume);
|
|
751
834
|
return {
|
|
752
835
|
hasOnlineResume: Boolean(onlineResume),
|
|
753
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
|
|