@link-assistant/hive-mind 1.69.11 ā 1.69.13
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/CHANGELOG.md +12 -0
- package/package.json +1 -1
- package/src/limits-i18n.lib.mjs +145 -0
- package/src/limits.lib.mjs +46 -74
- package/src/locales/en.lino +52 -0
- package/src/locales/hi.lino +52 -0
- package/src/locales/ru.lino +53 -1
- package/src/locales/zh.lino +52 -0
- package/src/solve.results.lib.mjs +24 -0
- package/src/telegram-bot.mjs +9 -8
- package/src/telegram-safe-reply.lib.mjs +117 -14
- package/src/telegram-solve-queue-command.lib.mjs +18 -6
- package/src/telegram-solve-queue.helpers.lib.mjs +58 -6
- package/src/telegram-solve-queue.lib.mjs +65 -41
- package/src/version-info.lib.mjs +52 -15
|
@@ -22,6 +22,7 @@ export { QUEUE_CONFIG, THRESHOLD_STRATEGIES } from './queue-config.lib.mjs';
|
|
|
22
22
|
import { QUEUE_CONFIG } from './queue-config.lib.mjs';
|
|
23
23
|
import { formatExecutingWorkSessionMessage, formatStartingWorkSessionMessage } from './work-session-formatting.lib.mjs';
|
|
24
24
|
import { t } from './i18n.lib.mjs';
|
|
25
|
+
import { lt } from './limits-i18n.lib.mjs';
|
|
25
26
|
|
|
26
27
|
export const QueueItemStatus = {
|
|
27
28
|
QUEUED: 'queued',
|
|
@@ -32,6 +33,23 @@ export const QueueItemStatus = {
|
|
|
32
33
|
CANCELLED: 'cancelled',
|
|
33
34
|
};
|
|
34
35
|
|
|
36
|
+
function getLocale(options = {}) {
|
|
37
|
+
if (typeof options === 'string') return options;
|
|
38
|
+
return options?.locale || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function appendWaitingForCurrentCommand(reason, locale) {
|
|
42
|
+
return `${reason} (${lt('queue_waiting_current_command', {}, { locale })})`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function appendRemainingDuration(reason, ms, locale) {
|
|
46
|
+
return `${reason} (${lt('remaining', { duration: formatDuration(ms, { locale }) }, { locale })})`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function queueStatusLabel(status, locale) {
|
|
50
|
+
return lt(`queue_status_${status}`, {}, { locale });
|
|
51
|
+
}
|
|
52
|
+
|
|
35
53
|
/**
|
|
36
54
|
* Queue item representing a /solve command request
|
|
37
55
|
*/
|
|
@@ -373,7 +391,7 @@ export class SolveQueue {
|
|
|
373
391
|
if (toolQueue.length === 0) continue;
|
|
374
392
|
|
|
375
393
|
// Check if first item in this tool's queue can start
|
|
376
|
-
const check = await this.canStartCommand({ tool });
|
|
394
|
+
const check = await this.canStartCommand({ tool, locale: toolQueue[0]?.locale || null });
|
|
377
395
|
|
|
378
396
|
// When a 'reject' strategy threshold is exceeded, immediately reject
|
|
379
397
|
// all items in this tool's queue instead of leaving them waiting.
|
|
@@ -411,16 +429,16 @@ export class SolveQueue {
|
|
|
411
429
|
* @see https://github.com/link-assistant/hive-mind/issues/1555
|
|
412
430
|
*/
|
|
413
431
|
async rejectAllItemsInQueue(tool, toolQueue, rejectReason) {
|
|
414
|
-
const reason = rejectReason || 'Resource limit exceeded';
|
|
415
432
|
while (toolQueue.length > 0) {
|
|
416
433
|
const item = toolQueue.shift();
|
|
434
|
+
const reason = rejectReason || lt('resource_limit_exceeded', {}, { locale: item.locale });
|
|
417
435
|
item.setFailed(reason);
|
|
418
436
|
this.failed.push(item);
|
|
419
437
|
this.stats.totalFailed++;
|
|
420
438
|
|
|
421
439
|
this.log(`Rejected queued item: ${item.toString()} from ${tool} queue - ${reason}`);
|
|
422
440
|
|
|
423
|
-
await this.updateItemMessage(item,
|
|
441
|
+
await this.updateItemMessage(item, t('telegram.solve_rejected', { infoBlock: item.infoBlock, reason }, { locale: item.locale }));
|
|
424
442
|
}
|
|
425
443
|
while (this.failed.length > 100) this.failed.shift();
|
|
426
444
|
}
|
|
@@ -546,6 +564,7 @@ export class SolveQueue {
|
|
|
546
564
|
*/
|
|
547
565
|
async canStartCommand(options = {}) {
|
|
548
566
|
const tool = options.tool || 'claude';
|
|
567
|
+
const locale = getLocale(options);
|
|
549
568
|
const reasons = [];
|
|
550
569
|
let oneAtATime = false;
|
|
551
570
|
let rejected = false;
|
|
@@ -558,8 +577,8 @@ export class SolveQueue {
|
|
|
558
577
|
if (lastStartTime) {
|
|
559
578
|
const timeSinceLastStart = Date.now() - lastStartTime;
|
|
560
579
|
if (timeSinceLastStart < QUEUE_CONFIG.MIN_START_INTERVAL_MS) {
|
|
561
|
-
const
|
|
562
|
-
reasons.push(formatWaitingReason('min_interval', 0, 0
|
|
580
|
+
const waitMs = QUEUE_CONFIG.MIN_START_INTERVAL_MS - timeSinceLastStart;
|
|
581
|
+
reasons.push(appendRemainingDuration(formatWaitingReason('min_interval', 0, 0, { locale }), waitMs, locale));
|
|
563
582
|
this.recordThrottle('min_interval');
|
|
564
583
|
}
|
|
565
584
|
}
|
|
@@ -609,7 +628,7 @@ export class SolveQueue {
|
|
|
609
628
|
// System resources apply to ALL tools, not just Claude
|
|
610
629
|
// See: https://github.com/link-assistant/hive-mind/issues/1155
|
|
611
630
|
// See: https://github.com/link-assistant/hive-mind/issues/1253 (strategies)
|
|
612
|
-
const resourceCheck = await this.checkSystemResources(totalProcessing);
|
|
631
|
+
const resourceCheck = await this.checkSystemResources(totalProcessing, { locale });
|
|
613
632
|
if (resourceCheck.rejected) {
|
|
614
633
|
rejected = true;
|
|
615
634
|
rejectReason = resourceCheck.rejectReason;
|
|
@@ -628,7 +647,7 @@ export class SolveQueue {
|
|
|
628
647
|
// See: https://github.com/link-assistant/hive-mind/issues/1253 (strategies)
|
|
629
648
|
const hasRunningToolProcess = (externalProcessing.byTool[tool] || 0) > 0;
|
|
630
649
|
const toolProcessingCount = this.getProcessingCountByTool(tool);
|
|
631
|
-
const limitCheck = await this.checkApiLimits(hasRunningToolProcess, toolProcessingCount, tool);
|
|
650
|
+
const limitCheck = await this.checkApiLimits(hasRunningToolProcess, toolProcessingCount, tool, { locale });
|
|
632
651
|
if (limitCheck.rejected) {
|
|
633
652
|
rejected = true;
|
|
634
653
|
rejectReason = limitCheck.rejectReason;
|
|
@@ -646,16 +665,16 @@ export class SolveQueue {
|
|
|
646
665
|
// Add claude_running info at the END (not beginning) of reasons
|
|
647
666
|
// Since it's supplementary info, not the primary blocking reason
|
|
648
667
|
// See: https://github.com/link-assistant/hive-mind/issues/1078
|
|
649
|
-
reasons.push(formatWaitingReason('claude_running', claudeProcessCount, 0
|
|
668
|
+
reasons.push(`${formatWaitingReason('claude_running', claudeProcessCount, 0, { locale })} (${lt('queue_processes', { count: claudeProcessCount }, { locale })})`);
|
|
650
669
|
}
|
|
651
670
|
if (tool === 'codex' && hasRunningCodex && reasons.length > 0) {
|
|
652
|
-
reasons.push(formatWaitingReason('codex_running', codexProcessCount, 0
|
|
671
|
+
reasons.push(`${formatWaitingReason('codex_running', codexProcessCount, 0, { locale })} (${lt('queue_processes', { count: codexProcessCount }, { locale })})`);
|
|
653
672
|
}
|
|
654
673
|
if (tool === 'qwen' && hasRunningQwen && reasons.length > 0) {
|
|
655
|
-
reasons.push(formatWaitingReason('qwen_running', qwenProcessCount, 0
|
|
674
|
+
reasons.push(`${formatWaitingReason('qwen_running', qwenProcessCount, 0, { locale })} (${lt('queue_processes', { count: qwenProcessCount }, { locale })})`);
|
|
656
675
|
}
|
|
657
676
|
if (tool === 'gemini' && hasRunningGemini && reasons.length > 0) {
|
|
658
|
-
reasons.push(formatWaitingReason('gemini_running', geminiProcessCount, 0
|
|
677
|
+
reasons.push(`${formatWaitingReason('gemini_running', geminiProcessCount, 0, { locale })} (${lt('queue_processes', { count: geminiProcessCount }, { locale })})`);
|
|
659
678
|
}
|
|
660
679
|
|
|
661
680
|
const canStart = reasons.length === 0 && !rejected;
|
|
@@ -712,7 +731,8 @@ export class SolveQueue {
|
|
|
712
731
|
* @param {number} totalProcessing - Total processing count (queue + external claude processes)
|
|
713
732
|
* @returns {Promise<{ok: boolean, reasons: string[], oneAtATime: boolean, rejected: boolean, rejectReason: string|null}>}
|
|
714
733
|
*/
|
|
715
|
-
async checkSystemResources(totalProcessing = 0) {
|
|
734
|
+
async checkSystemResources(totalProcessing = 0, options = {}) {
|
|
735
|
+
const locale = getLocale(options);
|
|
716
736
|
const reasons = [];
|
|
717
737
|
let oneAtATime = false;
|
|
718
738
|
let rejected = false;
|
|
@@ -723,7 +743,7 @@ export class SolveQueue {
|
|
|
723
743
|
if (memResult.success) {
|
|
724
744
|
const usedRatio = memResult.memory.usedPercentage / 100;
|
|
725
745
|
if (usedRatio >= QUEUE_CONFIG.thresholds.ram.value) {
|
|
726
|
-
const reason = formatWaitingReason('ram', memResult.memory.usedPercentage, QUEUE_CONFIG.thresholds.ram.value);
|
|
746
|
+
const reason = formatWaitingReason('ram', memResult.memory.usedPercentage, QUEUE_CONFIG.thresholds.ram.value, { locale });
|
|
727
747
|
const strategy = QUEUE_CONFIG.thresholds.ram.strategy;
|
|
728
748
|
this.recordThrottle(`ram_${strategy}`);
|
|
729
749
|
|
|
@@ -733,7 +753,7 @@ export class SolveQueue {
|
|
|
733
753
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
734
754
|
oneAtATime = true;
|
|
735
755
|
if (totalProcessing > 0) {
|
|
736
|
-
reasons.push(reason
|
|
756
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
737
757
|
}
|
|
738
758
|
} else {
|
|
739
759
|
// 'enqueue' - block unconditionally
|
|
@@ -760,7 +780,7 @@ export class SolveQueue {
|
|
|
760
780
|
}
|
|
761
781
|
|
|
762
782
|
if (usageRatio >= QUEUE_CONFIG.thresholds.cpu.value) {
|
|
763
|
-
const reason = formatWaitingReason('cpu', usagePercent, QUEUE_CONFIG.thresholds.cpu.value);
|
|
783
|
+
const reason = formatWaitingReason('cpu', usagePercent, QUEUE_CONFIG.thresholds.cpu.value, { locale });
|
|
764
784
|
const strategy = QUEUE_CONFIG.thresholds.cpu.strategy;
|
|
765
785
|
this.recordThrottle(`cpu_${strategy}`);
|
|
766
786
|
|
|
@@ -770,7 +790,7 @@ export class SolveQueue {
|
|
|
770
790
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
771
791
|
oneAtATime = true;
|
|
772
792
|
if (totalProcessing > 0) {
|
|
773
|
-
reasons.push(reason
|
|
793
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
774
794
|
}
|
|
775
795
|
} else {
|
|
776
796
|
// 'enqueue' - block unconditionally
|
|
@@ -788,7 +808,7 @@ export class SolveQueue {
|
|
|
788
808
|
const usedPercent = 100 - diskResult.diskSpace.freePercentage;
|
|
789
809
|
const usedRatio = usedPercent / 100;
|
|
790
810
|
if (usedRatio >= QUEUE_CONFIG.thresholds.disk.value) {
|
|
791
|
-
const reason = formatWaitingReason('disk', usedPercent, QUEUE_CONFIG.thresholds.disk.value);
|
|
811
|
+
const reason = formatWaitingReason('disk', usedPercent, QUEUE_CONFIG.thresholds.disk.value, { locale });
|
|
792
812
|
const strategy = QUEUE_CONFIG.thresholds.disk.strategy;
|
|
793
813
|
this.recordThrottle(`disk_${strategy}`);
|
|
794
814
|
|
|
@@ -798,7 +818,7 @@ export class SolveQueue {
|
|
|
798
818
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
799
819
|
oneAtATime = true;
|
|
800
820
|
if (totalProcessing > 0) {
|
|
801
|
-
reasons.push(reason
|
|
821
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
802
822
|
}
|
|
803
823
|
} else {
|
|
804
824
|
// 'enqueue' - block unconditionally
|
|
@@ -833,7 +853,8 @@ export class SolveQueue {
|
|
|
833
853
|
* @param {string} tool - The tool being used ('claude', 'agent', 'codex', 'gemini', 'qwen', etc.)
|
|
834
854
|
* @returns {Promise<{ok: boolean, reasons: string[], oneAtATime: boolean, rejected: boolean, rejectReason: string|null}>}
|
|
835
855
|
*/
|
|
836
|
-
async checkApiLimits(hasRunningToolProcess = false, toolProcessingCount = 0, tool = 'claude') {
|
|
856
|
+
async checkApiLimits(hasRunningToolProcess = false, toolProcessingCount = 0, tool = 'claude', options = {}) {
|
|
857
|
+
const locale = getLocale(options);
|
|
837
858
|
const reasons = [];
|
|
838
859
|
let oneAtATime = false;
|
|
839
860
|
let rejected = false;
|
|
@@ -862,7 +883,7 @@ export class SolveQueue {
|
|
|
862
883
|
if (sessionPercent !== null) {
|
|
863
884
|
const sessionRatio = sessionPercent / 100;
|
|
864
885
|
if (sessionRatio >= QUEUE_CONFIG.thresholds.claude5Hour.value) {
|
|
865
|
-
const reason = formatWaitingReason('claude_5_hour_session', sessionPercent, QUEUE_CONFIG.thresholds.claude5Hour.value);
|
|
886
|
+
const reason = formatWaitingReason('claude_5_hour_session', sessionPercent, QUEUE_CONFIG.thresholds.claude5Hour.value, { locale });
|
|
866
887
|
const strategy = QUEUE_CONFIG.thresholds.claude5Hour.strategy;
|
|
867
888
|
this.recordThrottle(sessionRatio >= 1.0 ? 'claude_5_hour_session_100' : `claude_5_hour_session_${strategy}`);
|
|
868
889
|
|
|
@@ -872,7 +893,7 @@ export class SolveQueue {
|
|
|
872
893
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
873
894
|
oneAtATime = true;
|
|
874
895
|
if (totalToolProcessing > 0) {
|
|
875
|
-
reasons.push(reason
|
|
896
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
876
897
|
}
|
|
877
898
|
} else {
|
|
878
899
|
// 'enqueue' - block unconditionally
|
|
@@ -887,7 +908,7 @@ export class SolveQueue {
|
|
|
887
908
|
if (weeklyPercent !== null) {
|
|
888
909
|
const weeklyRatio = weeklyPercent / 100;
|
|
889
910
|
if (weeklyRatio >= QUEUE_CONFIG.thresholds.claudeWeekly.value) {
|
|
890
|
-
const reason = formatWaitingReason('claude_weekly', weeklyPercent, QUEUE_CONFIG.thresholds.claudeWeekly.value);
|
|
911
|
+
const reason = formatWaitingReason('claude_weekly', weeklyPercent, QUEUE_CONFIG.thresholds.claudeWeekly.value, { locale });
|
|
891
912
|
const strategy = QUEUE_CONFIG.thresholds.claudeWeekly.strategy;
|
|
892
913
|
this.recordThrottle(weeklyRatio >= 1.0 ? 'claude_weekly_100' : `claude_weekly_${strategy}`);
|
|
893
914
|
|
|
@@ -897,7 +918,7 @@ export class SolveQueue {
|
|
|
897
918
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
898
919
|
oneAtATime = true;
|
|
899
920
|
if (totalToolProcessing > 0) {
|
|
900
|
-
reasons.push(reason
|
|
921
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
901
922
|
}
|
|
902
923
|
} else {
|
|
903
924
|
// 'enqueue' - block unconditionally
|
|
@@ -915,7 +936,7 @@ export class SolveQueue {
|
|
|
915
936
|
if (sessionPercent !== null) {
|
|
916
937
|
const sessionRatio = sessionPercent / 100;
|
|
917
938
|
if (sessionRatio >= QUEUE_CONFIG.thresholds.codex5Hour.value) {
|
|
918
|
-
const reason = formatWaitingReason('codex_5_hour_session', sessionPercent, QUEUE_CONFIG.thresholds.codex5Hour.value);
|
|
939
|
+
const reason = formatWaitingReason('codex_5_hour_session', sessionPercent, QUEUE_CONFIG.thresholds.codex5Hour.value, { locale });
|
|
919
940
|
const strategy = QUEUE_CONFIG.thresholds.codex5Hour.strategy;
|
|
920
941
|
this.recordThrottle(sessionRatio >= 1.0 ? 'codex_5_hour_session_100' : `codex_5_hour_session_${strategy}`);
|
|
921
942
|
|
|
@@ -925,7 +946,7 @@ export class SolveQueue {
|
|
|
925
946
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
926
947
|
oneAtATime = true;
|
|
927
948
|
if (totalToolProcessing > 0) {
|
|
928
|
-
reasons.push(reason
|
|
949
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
929
950
|
}
|
|
930
951
|
} else {
|
|
931
952
|
reasons.push(reason);
|
|
@@ -936,7 +957,7 @@ export class SolveQueue {
|
|
|
936
957
|
if (weeklyPercent !== null) {
|
|
937
958
|
const weeklyRatio = weeklyPercent / 100;
|
|
938
959
|
if (weeklyRatio >= QUEUE_CONFIG.thresholds.codexWeekly.value) {
|
|
939
|
-
const reason = formatWaitingReason('codex_weekly', weeklyPercent, QUEUE_CONFIG.thresholds.codexWeekly.value);
|
|
960
|
+
const reason = formatWaitingReason('codex_weekly', weeklyPercent, QUEUE_CONFIG.thresholds.codexWeekly.value, { locale });
|
|
940
961
|
const strategy = QUEUE_CONFIG.thresholds.codexWeekly.strategy;
|
|
941
962
|
this.recordThrottle(weeklyRatio >= 1.0 ? 'codex_weekly_100' : `codex_weekly_${strategy}`);
|
|
942
963
|
|
|
@@ -946,7 +967,7 @@ export class SolveQueue {
|
|
|
946
967
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
947
968
|
oneAtATime = true;
|
|
948
969
|
if (totalToolProcessing > 0) {
|
|
949
|
-
reasons.push(reason
|
|
970
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
950
971
|
}
|
|
951
972
|
} else {
|
|
952
973
|
reasons.push(reason);
|
|
@@ -967,7 +988,7 @@ export class SolveQueue {
|
|
|
967
988
|
const usedPercent = githubResult.githubRateLimit.usedPercentage;
|
|
968
989
|
const usedRatio = usedPercent / 100;
|
|
969
990
|
if (usedRatio >= QUEUE_CONFIG.thresholds.githubApi.value) {
|
|
970
|
-
const reason = formatWaitingReason('github', usedPercent, QUEUE_CONFIG.thresholds.githubApi.value);
|
|
991
|
+
const reason = formatWaitingReason('github', usedPercent, QUEUE_CONFIG.thresholds.githubApi.value, { locale });
|
|
971
992
|
const strategy = QUEUE_CONFIG.thresholds.githubApi.strategy;
|
|
972
993
|
this.recordThrottle(usedRatio >= 1.0 ? 'github_100' : `github_${strategy}`);
|
|
973
994
|
|
|
@@ -977,7 +998,7 @@ export class SolveQueue {
|
|
|
977
998
|
} else if (strategy === 'dequeue-one-at-a-time') {
|
|
978
999
|
oneAtATime = true;
|
|
979
1000
|
if (totalToolProcessing > 0) {
|
|
980
|
-
reasons.push(reason
|
|
1001
|
+
reasons.push(appendWaitingForCurrentCommand(reason, locale));
|
|
981
1002
|
}
|
|
982
1003
|
} else {
|
|
983
1004
|
// 'enqueue' - block unconditionally
|
|
@@ -1125,7 +1146,7 @@ export class SolveQueue {
|
|
|
1125
1146
|
// First check if the tool's threshold triggers a 'reject' strategy.
|
|
1126
1147
|
// If so, reject all items at once rather than iterating one by one.
|
|
1127
1148
|
// See: https://github.com/link-assistant/hive-mind/issues/1555
|
|
1128
|
-
const toolCheck = await this.canStartCommand({ tool });
|
|
1149
|
+
const toolCheck = await this.canStartCommand({ tool, locale: toolQueue[0]?.locale || null });
|
|
1129
1150
|
if (toolCheck.rejected) {
|
|
1130
1151
|
await this.rejectAllItemsInQueue(tool, toolQueue, toolCheck.rejectReason);
|
|
1131
1152
|
continue;
|
|
@@ -1134,9 +1155,10 @@ export class SolveQueue {
|
|
|
1134
1155
|
for (let i = 0; i < toolQueue.length; i++) {
|
|
1135
1156
|
const item = toolQueue[i];
|
|
1136
1157
|
if (item.status === QueueItemStatus.QUEUED || item.status === QueueItemStatus.WAITING) {
|
|
1158
|
+
const itemCheck = item.locale === (toolQueue[0]?.locale || null) ? toolCheck : await this.canStartCommand({ tool, locale: item.locale });
|
|
1137
1159
|
const previousStatus = item.status;
|
|
1138
1160
|
const previousReason = item.waitingReason;
|
|
1139
|
-
const waitReason =
|
|
1161
|
+
const waitReason = itemCheck.reason || lt('queue_waiting_in_queue', {}, { locale: item.locale });
|
|
1140
1162
|
item.setWaiting(waitReason);
|
|
1141
1163
|
|
|
1142
1164
|
// Update message if status/reason changed or it's time for periodic update
|
|
@@ -1283,14 +1305,15 @@ export class SolveQueue {
|
|
|
1283
1305
|
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
1284
1306
|
* @see https://github.com/link-assistant/hive-mind/issues/1267
|
|
1285
1307
|
*/
|
|
1286
|
-
async formatStatus() {
|
|
1308
|
+
async formatStatus(options = {}) {
|
|
1309
|
+
const locale = getLocale(options);
|
|
1287
1310
|
// Always show per-tool breakdown for all known queues
|
|
1288
1311
|
const externalProcessing = await this.getExternalProcessingSnapshot(Object.keys(this.queues));
|
|
1289
|
-
let message = '
|
|
1312
|
+
let message = `${lt('queues', {}, { locale })}\n`;
|
|
1290
1313
|
for (const [tool, toolQueue] of Object.entries(this.queues)) {
|
|
1291
1314
|
const pending = toolQueue.length;
|
|
1292
1315
|
const processing = externalProcessing.byTool[tool] || 0;
|
|
1293
|
-
message += `${tool} (
|
|
1316
|
+
message += `${tool} (${lt('queue_pending', {}, { locale })}: ${pending}, ${lt('queue_processing', {}, { locale })}: ${processing})\n`;
|
|
1294
1317
|
}
|
|
1295
1318
|
|
|
1296
1319
|
return message;
|
|
@@ -1320,39 +1343,40 @@ export class SolveQueue {
|
|
|
1320
1343
|
* @see https://github.com/link-assistant/hive-mind/issues/1159
|
|
1321
1344
|
* @see https://github.com/link-assistant/hive-mind/issues/1267
|
|
1322
1345
|
*/
|
|
1323
|
-
async formatDetailedStatus() {
|
|
1346
|
+
async formatDetailedStatus(options = {}) {
|
|
1347
|
+
const locale = getLocale(options);
|
|
1324
1348
|
const stats = this.getStats();
|
|
1325
1349
|
const externalProcessing = await this.getExternalProcessingSnapshot(Object.keys(this.queues));
|
|
1326
1350
|
|
|
1327
1351
|
// Get actual processing counts for each tool queue.
|
|
1328
1352
|
// This combines pgrep with tracked isolation status so users see detached
|
|
1329
1353
|
// screen-isolated work even when the direct AI CLI process count is lower.
|
|
1330
|
-
let message = '
|
|
1354
|
+
let message = `š *${lt('solve_queue_status', {}, { locale })}*\n\n`;
|
|
1331
1355
|
|
|
1332
1356
|
// Show per-tool queue breakdown with items grouped by queue
|
|
1333
1357
|
for (const [tool, toolQueue] of Object.entries(this.queues)) {
|
|
1334
1358
|
const pending = toolQueue.length;
|
|
1335
1359
|
const processing = externalProcessing.byTool[tool] || 0;
|
|
1336
|
-
message += `*${tool}* (
|
|
1360
|
+
message += `*${tool}* (${lt('queue_pending', {}, { locale })}: ${pending}, ${lt('queue_processing', {}, { locale })}: ${processing})\n`;
|
|
1337
1361
|
|
|
1338
1362
|
// Show first 5 queued items for this tool
|
|
1339
1363
|
const displayItems = toolQueue.slice(0, 5);
|
|
1340
1364
|
for (const item of displayItems) {
|
|
1341
|
-
const waitTime = formatDuration(item.getWaitTime());
|
|
1342
|
-
message += ` ⢠${item.url} (${item.status}, ${waitTime})\n`;
|
|
1365
|
+
const waitTime = formatDuration(item.getWaitTime(), { locale });
|
|
1366
|
+
message += ` ⢠${item.url} (${queueStatusLabel(item.status, locale)}, ${waitTime})\n`;
|
|
1343
1367
|
if (item.waitingReason) {
|
|
1344
1368
|
message += ` ā ${item.waitingReason}\n`;
|
|
1345
1369
|
}
|
|
1346
1370
|
}
|
|
1347
1371
|
if (toolQueue.length > 5) {
|
|
1348
|
-
message += ` ...
|
|
1372
|
+
message += ` ... ${lt('queue_and_more', { count: toolQueue.length - 5 }, { locale })}\n`;
|
|
1349
1373
|
}
|
|
1350
1374
|
|
|
1351
1375
|
message += '\n';
|
|
1352
1376
|
}
|
|
1353
1377
|
|
|
1354
1378
|
// Summary stats
|
|
1355
|
-
message +=
|
|
1379
|
+
message += `${lt('queue_completed', {}, { locale })}: ${stats.completed}, ${lt('queue_failed', {}, { locale })}: ${stats.failed}\n`;
|
|
1356
1380
|
|
|
1357
1381
|
return message;
|
|
1358
1382
|
}
|
package/src/version-info.lib.mjs
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// This reduces version gathering time from ~30-150s to ~5s (limited by slowest command).
|
|
9
9
|
|
|
10
10
|
import { getVersion } from './version.lib.mjs';
|
|
11
|
+
import { t } from './i18n.lib.mjs';
|
|
11
12
|
import { exec } from 'child_process';
|
|
12
13
|
import { promisify } from 'util';
|
|
13
14
|
|
|
@@ -969,6 +970,40 @@ export async function getVersionInfo(verbose = false, processVersion = null) {
|
|
|
969
970
|
* @param {string|null} version - Version string or null
|
|
970
971
|
* @param {string} [key] - Tool key for version parser lookup
|
|
971
972
|
*/
|
|
973
|
+
const ENGLISH_VERSION_LABELS = {
|
|
974
|
+
ai_agents: 'AI Agents',
|
|
975
|
+
browsers: 'Browsers',
|
|
976
|
+
browser_automation: 'Browser Automation',
|
|
977
|
+
connected: 'connected',
|
|
978
|
+
development_tools: 'Development Tools',
|
|
979
|
+
environment: 'Environment',
|
|
980
|
+
architecture: 'Architecture',
|
|
981
|
+
kernel: 'Kernel',
|
|
982
|
+
not_connected: 'not connected',
|
|
983
|
+
os: 'OS',
|
|
984
|
+
platform: 'Platform',
|
|
985
|
+
process_running_restart_needed: 'Process running: `{{processVersion}}` (restart needed)',
|
|
986
|
+
system: 'System',
|
|
987
|
+
version: 'Version',
|
|
988
|
+
};
|
|
989
|
+
|
|
990
|
+
function resolveVersionLocale(options = {}) {
|
|
991
|
+
if (typeof options === 'string') return options;
|
|
992
|
+
return options?.locale || null;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
function vt(key, params = {}, options = {}) {
|
|
996
|
+
const fullKey = `version.${key}`;
|
|
997
|
+
const locale = resolveVersionLocale(options);
|
|
998
|
+
const value = t(fullKey, params, locale ? { locale } : {});
|
|
999
|
+
if (value !== fullKey) return value;
|
|
1000
|
+
let fallback = ENGLISH_VERSION_LABELS[key] || key;
|
|
1001
|
+
for (const [paramKey, paramValue] of Object.entries(params)) {
|
|
1002
|
+
fallback = fallback.replace(new RegExp(`{{${paramKey}}}`, 'g'), String(paramValue));
|
|
1003
|
+
}
|
|
1004
|
+
return fallback;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
972
1007
|
function addVersionLine(lines, label, version, key) {
|
|
973
1008
|
if (version) {
|
|
974
1009
|
const display = key ? parseVersion(key, version) : version;
|
|
@@ -982,15 +1017,17 @@ function addVersionLine(lines, label, version, key) {
|
|
|
982
1017
|
* @param {Object} versions - Version information object
|
|
983
1018
|
* @returns {string} Formatted message
|
|
984
1019
|
*/
|
|
985
|
-
export function formatVersionMessage(versions) {
|
|
1020
|
+
export function formatVersionMessage(versions, options = {}) {
|
|
1021
|
+
const locale = resolveVersionLocale(options);
|
|
1022
|
+
const vOptions = { locale };
|
|
986
1023
|
const lines = [];
|
|
987
1024
|
|
|
988
1025
|
// === Hive-Mind Package (single entry with restart warning) ===
|
|
989
1026
|
lines.push('*š¤ Hive-Mind*');
|
|
990
1027
|
if (versions.hiveMind) {
|
|
991
|
-
lines.push(`ā¢
|
|
1028
|
+
lines.push(`⢠${vt('version', {}, vOptions)}: \`${versions.hiveMind}\``);
|
|
992
1029
|
if (versions.needsRestart) {
|
|
993
|
-
lines.push(`ā ļø
|
|
1030
|
+
lines.push(`ā ļø _${vt('process_running_restart_needed', { processVersion: versions.processVersion }, vOptions)}_`);
|
|
994
1031
|
}
|
|
995
1032
|
}
|
|
996
1033
|
|
|
@@ -1006,7 +1043,7 @@ export function formatVersionMessage(versions) {
|
|
|
1006
1043
|
|
|
1007
1044
|
if (agentLines.length > 0) {
|
|
1008
1045
|
lines.push('');
|
|
1009
|
-
lines.push('
|
|
1046
|
+
lines.push(`*š ${vt('ai_agents', {}, vOptions)}*`);
|
|
1010
1047
|
lines.push(...agentLines);
|
|
1011
1048
|
}
|
|
1012
1049
|
|
|
@@ -1173,7 +1210,7 @@ export function formatVersionMessage(versions) {
|
|
|
1173
1210
|
|
|
1174
1211
|
if (browserLines.length > 0) {
|
|
1175
1212
|
lines.push('');
|
|
1176
|
-
lines.push('
|
|
1213
|
+
lines.push(`*š ${vt('browsers', {}, vOptions)}*`);
|
|
1177
1214
|
lines.push(...browserLines);
|
|
1178
1215
|
}
|
|
1179
1216
|
|
|
@@ -1184,15 +1221,15 @@ export function formatVersionMessage(versions) {
|
|
|
1184
1221
|
// Playwright MCP: show version with Claude Code and Codex connection status inline
|
|
1185
1222
|
if (versions.playwrightMcp) {
|
|
1186
1223
|
const mcpVersion = parseVersion('playwrightMcp', versions.playwrightMcp);
|
|
1187
|
-
const claudeStatus = versions.playwrightMcpClaudeStatus ? 'connected' : '
|
|
1188
|
-
const codexStatus = versions.playwrightMcpCodexStatus ? 'connected' : '
|
|
1224
|
+
const claudeStatus = versions.playwrightMcpClaudeStatus ? vt('connected', {}, vOptions) : vt('not_connected', {}, vOptions);
|
|
1225
|
+
const codexStatus = versions.playwrightMcpCodexStatus ? vt('connected', {}, vOptions) : vt('not_connected', {}, vOptions);
|
|
1189
1226
|
browserAutoLines.push(`⢠Playwright MCP: \`${mcpVersion} | Claude Code: ${claudeStatus} | Codex: ${codexStatus}\``);
|
|
1190
1227
|
}
|
|
1191
1228
|
addVersionLine(browserAutoLines, 'Puppeteer Browsers', versions.puppeteerBrowsers, 'puppeteerBrowsers');
|
|
1192
1229
|
|
|
1193
1230
|
if (browserAutoLines.length > 0) {
|
|
1194
1231
|
lines.push('');
|
|
1195
|
-
lines.push('
|
|
1232
|
+
lines.push(`*š ${vt('browser_automation', {}, vOptions)}*`);
|
|
1196
1233
|
lines.push(...browserAutoLines);
|
|
1197
1234
|
}
|
|
1198
1235
|
|
|
@@ -1212,24 +1249,24 @@ export function formatVersionMessage(versions) {
|
|
|
1212
1249
|
|
|
1213
1250
|
if (toolLines.length > 0) {
|
|
1214
1251
|
lines.push('');
|
|
1215
|
-
lines.push('
|
|
1252
|
+
lines.push(`*š ${vt('development_tools', {}, vOptions)}*`);
|
|
1216
1253
|
lines.push(...toolLines);
|
|
1217
1254
|
}
|
|
1218
1255
|
|
|
1219
1256
|
// === Platform (detailed) ===
|
|
1220
1257
|
{
|
|
1221
1258
|
const platformLines = [];
|
|
1222
|
-
if (versions.platformEnvironment) platformLines.push(`ā¢
|
|
1223
|
-
if (versions.platformArch) platformLines.push(`ā¢
|
|
1224
|
-
if (versions.platformOs) platformLines.push(`ā¢
|
|
1225
|
-
if (versions.platformKernel) platformLines.push(`ā¢
|
|
1259
|
+
if (versions.platformEnvironment) platformLines.push(`⢠${vt('environment', {}, vOptions)}: \`${versions.platformEnvironment}\``);
|
|
1260
|
+
if (versions.platformArch) platformLines.push(`⢠${vt('architecture', {}, vOptions)}: \`${versions.platformArch}\``);
|
|
1261
|
+
if (versions.platformOs) platformLines.push(`⢠${vt('os', {}, vOptions)}: \`${versions.platformOs}\``);
|
|
1262
|
+
if (versions.platformKernel) platformLines.push(`⢠${vt('kernel', {}, vOptions)}: \`${versions.platformKernel}\``);
|
|
1226
1263
|
// Fallback to legacy single-line format
|
|
1227
1264
|
if (platformLines.length === 0 && versions.platform) {
|
|
1228
|
-
platformLines.push(`ā¢
|
|
1265
|
+
platformLines.push(`⢠${vt('system', {}, vOptions)}: \`${versions.platform}\``);
|
|
1229
1266
|
}
|
|
1230
1267
|
if (platformLines.length > 0) {
|
|
1231
1268
|
lines.push('');
|
|
1232
|
-
lines.push('
|
|
1269
|
+
lines.push(`*š» ${vt('platform', {}, vOptions)}*`);
|
|
1233
1270
|
lines.push(...platformLines);
|
|
1234
1271
|
}
|
|
1235
1272
|
}
|