@link-assistant/hive-mind 1.7.0 → 1.7.2
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 +16 -0
- package/README.md +2 -2
- package/package.json +1 -1
- package/src/start-screen.mjs +2 -2
- package/src/telegram-bot.mjs +17 -3
- package/src/telegram-solve-queue.lib.mjs +31 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @link-assistant/hive-mind
|
|
2
2
|
|
|
3
|
+
## 1.7.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- e6a656f: Use `screen -R` instead of `screen -S` and `screen -r` in all docs and code for better session management. The `-R` flag ensures we open existing screen if created, and new if not yet created, making it the most safe and universal option.
|
|
8
|
+
|
|
9
|
+
## 1.7.1
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- d86ba79: Prevent duplicate URLs from being added to the /solve queue (Issue #1080)
|
|
14
|
+
- Added `findByUrl()` method to SolveQueue to detect existing items by URL
|
|
15
|
+
- Updated /solve command handler to check for duplicates before queueing
|
|
16
|
+
- Uses normalized URLs for consistent comparison
|
|
17
|
+
- Returns informative error message when duplicate is detected
|
|
18
|
+
|
|
3
19
|
## 1.7.0
|
|
4
20
|
|
|
5
21
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -209,7 +209,7 @@ See [docs/HELM.md](./docs/HELM.md) for detailed Helm configuration options.
|
|
|
209
209
|
**Using Links Notation (recommended):**
|
|
210
210
|
|
|
211
211
|
```
|
|
212
|
-
screen -
|
|
212
|
+
screen -R bot # Enter new screen for bot
|
|
213
213
|
|
|
214
214
|
hive-telegram-bot --configuration "
|
|
215
215
|
TELEGRAM_BOT_TOKEN: '849...355:AAG...rgk_YZk...aPU'
|
|
@@ -238,7 +238,7 @@ See [docs/HELM.md](./docs/HELM.md) for detailed Helm configuration options.
|
|
|
238
238
|
**Using individual command-line options:**
|
|
239
239
|
|
|
240
240
|
```
|
|
241
|
-
screen -
|
|
241
|
+
screen -R bot # Enter new screen for bot
|
|
242
242
|
|
|
243
243
|
hive-telegram-bot --token 849...355:AAG...rgk_YZk...aPU --allowed-chats "(
|
|
244
244
|
-1002975819706
|
package/package.json
CHANGED
package/src/start-screen.mjs
CHANGED
|
@@ -163,7 +163,7 @@ async function createOrEnterScreen(sessionName, command, args, autoTerminate = f
|
|
|
163
163
|
// The \n at the end simulates pressing Enter
|
|
164
164
|
await execAsync(`screen -S ${sessionName} -X stuff '${escapedCommand}\n'`);
|
|
165
165
|
console.log(`Command sent to session '${sessionName}' successfully.`);
|
|
166
|
-
console.log(`To attach and view the session, run: screen -
|
|
166
|
+
console.log(`To attach and view the session, run: screen -R ${sessionName}`);
|
|
167
167
|
} catch (error) {
|
|
168
168
|
console.error('Failed to send command to existing screen session:', error.message);
|
|
169
169
|
console.error('You may need to terminate the old session and try again.');
|
|
@@ -208,7 +208,7 @@ async function createOrEnterScreen(sessionName, command, args, autoTerminate = f
|
|
|
208
208
|
} else {
|
|
209
209
|
console.log('Session will remain active after command completes');
|
|
210
210
|
}
|
|
211
|
-
console.log(`To attach to this session, run: screen -
|
|
211
|
+
console.log(`To attach to this session, run: screen -R ${sessionName}`);
|
|
212
212
|
} catch (error) {
|
|
213
213
|
console.error('Failed to create screen session:', error.message);
|
|
214
214
|
process.exit(1);
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -654,7 +654,7 @@ async function executeAndUpdateMessage(ctx, startingMessage, commandName, args,
|
|
|
654
654
|
if (result.warning) return safeEdit(`⚠️ ${result.warning}`);
|
|
655
655
|
|
|
656
656
|
if (result.success) {
|
|
657
|
-
const match = result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -
|
|
657
|
+
const match = result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -R\s+(\S+)/);
|
|
658
658
|
const session = match ? match[1] : 'unknown';
|
|
659
659
|
await safeEdit(`✅ ${commandName.charAt(0).toUpperCase() + commandName.slice(1)} command started successfully!\n\n📊 Session: \`${session}\`\n\n${infoBlock}`);
|
|
660
660
|
} else {
|
|
@@ -1069,18 +1069,32 @@ bot.command(/^solve$/i, async ctx => {
|
|
|
1069
1069
|
return;
|
|
1070
1070
|
}
|
|
1071
1071
|
|
|
1072
|
+
// Use normalized URL from validation to ensure consistent duplicate detection
|
|
1073
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1080
|
|
1074
|
+
const normalizedUrl = validation.parsed.normalized;
|
|
1075
|
+
|
|
1072
1076
|
const requester = buildUserMention({ user: ctx.from, parseMode: 'Markdown' });
|
|
1073
1077
|
const optionsText = args.slice(1).join(' ') || 'none';
|
|
1074
|
-
let infoBlock = `Requested by: ${requester}\nURL: ${escapeMarkdown(
|
|
1078
|
+
let infoBlock = `Requested by: ${requester}\nURL: ${escapeMarkdown(normalizedUrl)}\nOptions: ${optionsText}`;
|
|
1075
1079
|
if (solveOverrides.length > 0) infoBlock += `\n🔒 Locked options: ${solveOverrides.join(' ')}`;
|
|
1076
1080
|
const solveQueue = getSolveQueue({ verbose: VERBOSE });
|
|
1081
|
+
|
|
1082
|
+
// Check for duplicate URL in queue
|
|
1083
|
+
// See: https://github.com/link-assistant/hive-mind/issues/1080
|
|
1084
|
+
const existingItem = solveQueue.findByUrl(normalizedUrl);
|
|
1085
|
+
if (existingItem) {
|
|
1086
|
+
const statusText = existingItem.status === 'starting' || existingItem.status === 'started' ? 'being processed' : 'already in the queue';
|
|
1087
|
+
await ctx.reply(`❌ This URL is ${statusText}.\n\nURL: ${escapeMarkdown(normalizedUrl)}\nStatus: ${existingItem.status}\n\n💡 Use /solve-queue to check the queue status.`, { parse_mode: 'Markdown', reply_to_message_id: ctx.message.message_id });
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1077
1091
|
const check = await solveQueue.canStartCommand();
|
|
1078
1092
|
const queueStats = solveQueue.getStats();
|
|
1079
1093
|
if (check.canStart && queueStats.queued === 0) {
|
|
1080
1094
|
const startingMessage = await ctx.reply(`🚀 Starting solve command...\n\n${infoBlock}`, { parse_mode: 'Markdown', reply_to_message_id: ctx.message.message_id });
|
|
1081
1095
|
await executeAndUpdateMessage(ctx, startingMessage, 'solve', args, infoBlock);
|
|
1082
1096
|
} else {
|
|
1083
|
-
const queueItem = solveQueue.enqueue({ url:
|
|
1097
|
+
const queueItem = solveQueue.enqueue({ url: normalizedUrl, args, ctx, requester, infoBlock, tool: solveTool });
|
|
1084
1098
|
let queueMessage = `📋 Solve command queued (position #${queueStats.queued + 1})\n\n${infoBlock}`;
|
|
1085
1099
|
if (check.reason) queueMessage += `\n\n⏳ Waiting: ${check.reason}`;
|
|
1086
1100
|
const queuedMessage = await ctx.reply(queueMessage, { parse_mode: 'Markdown', reply_to_message_id: ctx.message.message_id });
|
|
@@ -34,22 +34,22 @@ import { getCachedClaudeLimits, getCachedGitHubLimits, getCachedMemoryInfo, getC
|
|
|
34
34
|
export const QUEUE_CONFIG = {
|
|
35
35
|
// Resource thresholds (usage ratios: 0.0 - 1.0)
|
|
36
36
|
// All thresholds use >= comparison (inclusive)
|
|
37
|
-
RAM_THRESHOLD: 0.
|
|
37
|
+
RAM_THRESHOLD: 0.65, // Stop if RAM usage >= 65%
|
|
38
38
|
// CPU threshold uses 5-minute load average, not instantaneous CPU usage
|
|
39
|
-
CPU_THRESHOLD: 0.
|
|
39
|
+
CPU_THRESHOLD: 0.75, // Stop if 5-minute load average >= 75% of CPU count
|
|
40
40
|
DISK_THRESHOLD: 0.95, // One-at-a-time if disk usage >= 95%
|
|
41
41
|
|
|
42
42
|
// API limit thresholds (usage ratios: 0.0 - 1.0)
|
|
43
43
|
// All thresholds use >= comparison (inclusive)
|
|
44
|
-
CLAUDE_5_HOUR_SESSION_THRESHOLD: 0.
|
|
45
|
-
CLAUDE_WEEKLY_THRESHOLD: 0.
|
|
44
|
+
CLAUDE_5_HOUR_SESSION_THRESHOLD: 0.85, // Stop if 5-hour limit >= 85%
|
|
45
|
+
CLAUDE_WEEKLY_THRESHOLD: 0.98, // One-at-a-time if weekly limit >= 98%
|
|
46
46
|
GITHUB_API_THRESHOLD: 0.8, // Stop if GitHub >= 80% with parallel claude
|
|
47
47
|
|
|
48
48
|
// Timing
|
|
49
49
|
// MIN_START_INTERVAL_MS: Time to allow solve command to start actual claude process
|
|
50
50
|
// This ensures that when API limits are checked, the running process is counted
|
|
51
|
-
MIN_START_INTERVAL_MS:
|
|
52
|
-
CONSUMER_POLL_INTERVAL_MS: 60000, // 1 minute between queue checks
|
|
51
|
+
MIN_START_INTERVAL_MS: 60000, // 1 minutes between starts
|
|
52
|
+
CONSUMER_POLL_INTERVAL_MS: 60000, // 1 minute between queue checks
|
|
53
53
|
MESSAGE_UPDATE_INTERVAL_MS: 60000, // 1 minute between status message updates
|
|
54
54
|
|
|
55
55
|
// Process detection
|
|
@@ -303,6 +303,30 @@ export class SolveQueue {
|
|
|
303
303
|
return item;
|
|
304
304
|
}
|
|
305
305
|
|
|
306
|
+
/**
|
|
307
|
+
* Find an item by URL in the queue or processing items
|
|
308
|
+
* Used to prevent duplicate URLs from being added to the queue
|
|
309
|
+
* @param {string} url - The URL to search for
|
|
310
|
+
* @returns {SolveQueueItem|null} The found item or null
|
|
311
|
+
* @see https://github.com/link-assistant/hive-mind/issues/1080
|
|
312
|
+
*/
|
|
313
|
+
findByUrl(url) {
|
|
314
|
+
// Check queued items
|
|
315
|
+
const queuedItem = this.queue.find(item => item.url === url);
|
|
316
|
+
if (queuedItem) {
|
|
317
|
+
return queuedItem;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check processing items
|
|
321
|
+
for (const item of this.processing.values()) {
|
|
322
|
+
if (item.url === url) {
|
|
323
|
+
return item;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return null;
|
|
328
|
+
}
|
|
329
|
+
|
|
306
330
|
/**
|
|
307
331
|
* Cancel a queued item by ID
|
|
308
332
|
* @param {string} id - Item ID
|
|
@@ -734,7 +758,7 @@ export class SolveQueue {
|
|
|
734
758
|
// Extract session name from result
|
|
735
759
|
let sessionName = 'unknown';
|
|
736
760
|
if (result && result.output) {
|
|
737
|
-
const sessionMatch = result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -
|
|
761
|
+
const sessionMatch = result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -R\s+(\S+)/);
|
|
738
762
|
if (sessionMatch) sessionName = sessionMatch[1];
|
|
739
763
|
}
|
|
740
764
|
|