@link-assistant/hive-mind 0.46.1 → 0.47.1
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 +20 -15
- package/README.md +42 -8
- package/package.json +16 -3
- package/src/agent.lib.mjs +49 -70
- package/src/agent.prompts.lib.mjs +6 -20
- package/src/buildUserMention.lib.mjs +4 -17
- package/src/claude-limits.lib.mjs +15 -15
- package/src/claude.lib.mjs +617 -626
- package/src/claude.prompts.lib.mjs +7 -22
- package/src/codex.lib.mjs +39 -71
- package/src/codex.prompts.lib.mjs +6 -20
- package/src/config.lib.mjs +3 -16
- package/src/contributing-guidelines.lib.mjs +5 -18
- package/src/exit-handler.lib.mjs +4 -4
- package/src/git.lib.mjs +7 -7
- package/src/github-issue-creator.lib.mjs +17 -17
- package/src/github-linking.lib.mjs +8 -33
- package/src/github.batch.lib.mjs +20 -16
- package/src/github.graphql.lib.mjs +18 -18
- package/src/github.lib.mjs +89 -91
- package/src/hive.config.lib.mjs +50 -50
- package/src/hive.mjs +1293 -1296
- package/src/instrument.mjs +7 -11
- package/src/interactive-mode.lib.mjs +112 -138
- package/src/lenv-reader.lib.mjs +1 -6
- package/src/lib.mjs +36 -45
- package/src/lino.lib.mjs +2 -2
- package/src/local-ci-checks.lib.mjs +15 -14
- package/src/memory-check.mjs +52 -60
- package/src/model-mapping.lib.mjs +25 -32
- package/src/model-validation.lib.mjs +31 -31
- package/src/opencode.lib.mjs +37 -62
- package/src/opencode.prompts.lib.mjs +7 -21
- package/src/protect-branch.mjs +14 -15
- package/src/review.mjs +28 -27
- package/src/reviewers-hive.mjs +64 -69
- package/src/sentry.lib.mjs +13 -10
- package/src/solve.auto-continue.lib.mjs +48 -38
- package/src/solve.auto-pr.lib.mjs +111 -69
- package/src/solve.branch-errors.lib.mjs +17 -46
- package/src/solve.branch.lib.mjs +16 -23
- package/src/solve.config.lib.mjs +263 -261
- package/src/solve.error-handlers.lib.mjs +21 -79
- package/src/solve.execution.lib.mjs +10 -18
- package/src/solve.feedback.lib.mjs +25 -46
- package/src/solve.mjs +59 -60
- package/src/solve.preparation.lib.mjs +10 -36
- package/src/solve.repo-setup.lib.mjs +4 -19
- package/src/solve.repository.lib.mjs +37 -37
- package/src/solve.results.lib.mjs +32 -46
- package/src/solve.session.lib.mjs +7 -22
- package/src/solve.validation.lib.mjs +19 -17
- package/src/solve.watch.lib.mjs +20 -33
- package/src/start-screen.mjs +24 -24
- package/src/task.mjs +38 -44
- package/src/telegram-bot.mjs +125 -121
- package/src/telegram-top-command.lib.mjs +32 -48
- package/src/usage-limit.lib.mjs +9 -13
- package/src/version-info.lib.mjs +1 -1
- package/src/version.lib.mjs +1 -1
- package/src/youtrack/solve.youtrack.lib.mjs +3 -8
- package/src/youtrack/youtrack-sync.mjs +8 -14
- package/src/youtrack/youtrack.lib.mjs +26 -28
package/src/telegram-bot.mjs
CHANGED
|
@@ -59,61 +59,61 @@ const config = yargs(hideBin(process.argv))
|
|
|
59
59
|
type: 'string',
|
|
60
60
|
description: 'LINO configuration string for environment variables',
|
|
61
61
|
alias: 'c',
|
|
62
|
-
default: getenv('TELEGRAM_CONFIGURATION', '')
|
|
62
|
+
default: getenv('TELEGRAM_CONFIGURATION', ''),
|
|
63
63
|
})
|
|
64
64
|
.option('token', {
|
|
65
65
|
type: 'string',
|
|
66
66
|
description: 'Telegram bot token from @BotFather',
|
|
67
67
|
alias: 't',
|
|
68
|
-
default: getenv('TELEGRAM_BOT_TOKEN', '')
|
|
68
|
+
default: getenv('TELEGRAM_BOT_TOKEN', ''),
|
|
69
69
|
})
|
|
70
70
|
.option('allowedChats', {
|
|
71
71
|
type: 'string',
|
|
72
72
|
description: 'Allowed chat IDs in lino notation, e.g., "(\n 123456789\n 987654321\n)"',
|
|
73
73
|
alias: 'allowed-chats',
|
|
74
|
-
default: getenv('TELEGRAM_ALLOWED_CHATS', '')
|
|
74
|
+
default: getenv('TELEGRAM_ALLOWED_CHATS', ''),
|
|
75
75
|
})
|
|
76
76
|
.option('solveOverrides', {
|
|
77
77
|
type: 'string',
|
|
78
78
|
description: 'Override options for /solve command in lino notation, e.g., "(\n --auto-continue\n --attach-logs\n)"',
|
|
79
79
|
alias: 'solve-overrides',
|
|
80
|
-
default: getenv('TELEGRAM_SOLVE_OVERRIDES', '')
|
|
80
|
+
default: getenv('TELEGRAM_SOLVE_OVERRIDES', ''),
|
|
81
81
|
})
|
|
82
82
|
.option('hiveOverrides', {
|
|
83
83
|
type: 'string',
|
|
84
84
|
description: 'Override options for /hive command in lino notation, e.g., "(\n --verbose\n --all-issues\n)"',
|
|
85
85
|
alias: 'hive-overrides',
|
|
86
|
-
default: getenv('TELEGRAM_HIVE_OVERRIDES', '')
|
|
86
|
+
default: getenv('TELEGRAM_HIVE_OVERRIDES', ''),
|
|
87
87
|
})
|
|
88
88
|
.option('solve', {
|
|
89
89
|
type: 'boolean',
|
|
90
90
|
description: 'Enable /solve command (use --no-solve to disable)',
|
|
91
|
-
default: getenv('TELEGRAM_SOLVE', 'true') !== 'false'
|
|
91
|
+
default: getenv('TELEGRAM_SOLVE', 'true') !== 'false',
|
|
92
92
|
})
|
|
93
93
|
.option('hive', {
|
|
94
94
|
type: 'boolean',
|
|
95
95
|
description: 'Enable /hive command (use --no-hive to disable)',
|
|
96
|
-
default: getenv('TELEGRAM_HIVE', 'true') !== 'false'
|
|
96
|
+
default: getenv('TELEGRAM_HIVE', 'true') !== 'false',
|
|
97
97
|
})
|
|
98
98
|
.option('dryRun', {
|
|
99
99
|
type: 'boolean',
|
|
100
100
|
description: 'Validate configuration and options without starting the bot',
|
|
101
101
|
alias: 'dry-run',
|
|
102
|
-
default: false
|
|
102
|
+
default: false,
|
|
103
103
|
})
|
|
104
104
|
.option('verbose', {
|
|
105
105
|
type: 'boolean',
|
|
106
106
|
description: 'Enable verbose logging for debugging',
|
|
107
107
|
alias: 'v',
|
|
108
|
-
default: getenv('TELEGRAM_BOT_VERBOSE', 'false') === 'true'
|
|
108
|
+
default: getenv('TELEGRAM_BOT_VERBOSE', 'false') === 'true',
|
|
109
109
|
})
|
|
110
110
|
.help('h')
|
|
111
111
|
.alias('h', 'help')
|
|
112
112
|
.parserConfiguration({
|
|
113
113
|
'boolean-negation': true,
|
|
114
|
-
'strip-dashed': true
|
|
114
|
+
'strip-dashed': true, // Remove dashed keys from argv to simplify validation
|
|
115
115
|
})
|
|
116
|
-
.strict()
|
|
116
|
+
.strict() // Enable strict mode to reject unknown options (consistent with solve.mjs and hive.mjs)
|
|
117
117
|
.parse();
|
|
118
118
|
|
|
119
119
|
// Load configuration from --configuration option if provided
|
|
@@ -147,19 +147,23 @@ if (!BOT_TOKEN) {
|
|
|
147
147
|
// Priority: CLI option > environment variable (from .lenv or .env)
|
|
148
148
|
// NOTE: This section moved BEFORE loading telegraf for faster dry-run mode (issue #801)
|
|
149
149
|
const resolvedAllowedChats = config.allowedChats || getenv('TELEGRAM_ALLOWED_CHATS', '');
|
|
150
|
-
const allowedChats = resolvedAllowedChats
|
|
151
|
-
? lino.parseNumericIds(resolvedAllowedChats)
|
|
152
|
-
: null;
|
|
150
|
+
const allowedChats = resolvedAllowedChats ? lino.parseNumericIds(resolvedAllowedChats) : null;
|
|
153
151
|
|
|
154
152
|
// Parse override options
|
|
155
153
|
const resolvedSolveOverrides = config.solveOverrides || getenv('TELEGRAM_SOLVE_OVERRIDES', '');
|
|
156
154
|
const solveOverrides = resolvedSolveOverrides
|
|
157
|
-
? lino
|
|
155
|
+
? lino
|
|
156
|
+
.parse(resolvedSolveOverrides)
|
|
157
|
+
.map(line => line.trim())
|
|
158
|
+
.filter(line => line)
|
|
158
159
|
: [];
|
|
159
160
|
|
|
160
161
|
const resolvedHiveOverrides = config.hiveOverrides || getenv('TELEGRAM_HIVE_OVERRIDES', '');
|
|
161
162
|
const hiveOverrides = resolvedHiveOverrides
|
|
162
|
-
? lino
|
|
163
|
+
? lino
|
|
164
|
+
.parse(resolvedHiveOverrides)
|
|
165
|
+
.map(line => line.trim())
|
|
166
|
+
.filter(line => line)
|
|
163
167
|
: [];
|
|
164
168
|
|
|
165
169
|
// Command enable/disable flags
|
|
@@ -179,7 +183,7 @@ if (solveEnabled && solveOverrides.length > 0) {
|
|
|
179
183
|
// Temporarily suppress stderr to avoid yargs error output during validation
|
|
180
184
|
const originalStderrWrite = process.stderr.write;
|
|
181
185
|
const stderrBuffer = [];
|
|
182
|
-
process.stderr.write =
|
|
186
|
+
process.stderr.write = chunk => {
|
|
183
187
|
stderrBuffer.push(chunk);
|
|
184
188
|
return true;
|
|
185
189
|
};
|
|
@@ -219,7 +223,7 @@ if (hiveEnabled && hiveOverrides.length > 0) {
|
|
|
219
223
|
// Temporarily suppress stderr to avoid yargs error output during validation
|
|
220
224
|
const originalStderrWrite = process.stderr.write;
|
|
221
225
|
const stderrBuffer = [];
|
|
222
|
-
process.stderr.write =
|
|
226
|
+
process.stderr.write = chunk => {
|
|
223
227
|
stderrBuffer.push(chunk);
|
|
224
228
|
return true;
|
|
225
229
|
};
|
|
@@ -289,7 +293,7 @@ const { Telegraf } = telegrafModule;
|
|
|
289
293
|
const bot = new Telegraf(BOT_TOKEN, {
|
|
290
294
|
// Remove the default 90-second timeout for message handlers
|
|
291
295
|
// This is important because command handlers (like /solve) spawn long-running processes
|
|
292
|
-
handlerTimeout: Infinity
|
|
296
|
+
handlerTimeout: Infinity,
|
|
293
297
|
});
|
|
294
298
|
|
|
295
299
|
// Track bot startup time to ignore messages sent before bot started
|
|
@@ -351,9 +355,7 @@ function isForwardedOrReply(ctx) {
|
|
|
351
355
|
return true;
|
|
352
356
|
}
|
|
353
357
|
// Also check old forwarding API fields for backward compatibility
|
|
354
|
-
if (message.forward_from || message.forward_from_chat ||
|
|
355
|
-
message.forward_from_message_id || message.forward_signature ||
|
|
356
|
-
message.forward_sender_name || message.forward_date) {
|
|
358
|
+
if (message.forward_from || message.forward_from_chat || message.forward_from_message_id || message.forward_signature || message.forward_sender_name || message.forward_date) {
|
|
357
359
|
if (VERBOSE) {
|
|
358
360
|
console.log('[VERBOSE] isForwardedOrReply: TRUE - old forwarding API field detected');
|
|
359
361
|
if (message.forward_from) console.log('[VERBOSE] Triggered by: forward_from');
|
|
@@ -409,16 +411,14 @@ async function executeStartScreen(command, args) {
|
|
|
409
411
|
const whichPath = await findStartScreenCommand();
|
|
410
412
|
|
|
411
413
|
if (!whichPath) {
|
|
412
|
-
const warningMsg = '⚠️ WARNING: start-screen command not found in PATH\n' +
|
|
413
|
-
'Please ensure @link-assistant/hive-mind is properly installed\n' +
|
|
414
|
-
'You may need to run: npm install -g @link-assistant/hive-mind';
|
|
414
|
+
const warningMsg = '⚠️ WARNING: start-screen command not found in PATH\n' + 'Please ensure @link-assistant/hive-mind is properly installed\n' + 'You may need to run: npm install -g @link-assistant/hive-mind';
|
|
415
415
|
console.warn(warningMsg);
|
|
416
416
|
|
|
417
417
|
// Still try to execute with 'start-screen' in case it's available in PATH but 'which' failed
|
|
418
418
|
return {
|
|
419
419
|
success: false,
|
|
420
420
|
warning: warningMsg,
|
|
421
|
-
error: 'start-screen command not found in PATH'
|
|
421
|
+
error: 'start-screen command not found in PATH',
|
|
422
422
|
};
|
|
423
423
|
}
|
|
424
424
|
|
|
@@ -433,13 +433,13 @@ async function executeStartScreen(command, args) {
|
|
|
433
433
|
return {
|
|
434
434
|
success: false,
|
|
435
435
|
output: '',
|
|
436
|
-
error: error.message
|
|
436
|
+
error: error.message,
|
|
437
437
|
};
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
function executeWithCommand(startScreenCmd, command, args) {
|
|
442
|
-
return new Promise(
|
|
442
|
+
return new Promise(resolve => {
|
|
443
443
|
const allArgs = [command, ...args];
|
|
444
444
|
|
|
445
445
|
if (VERBOSE) {
|
|
@@ -451,39 +451,39 @@ function executeWithCommand(startScreenCmd, command, args) {
|
|
|
451
451
|
const child = spawn(startScreenCmd, allArgs, {
|
|
452
452
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
453
453
|
detached: false,
|
|
454
|
-
env: process.env
|
|
454
|
+
env: process.env,
|
|
455
455
|
});
|
|
456
456
|
|
|
457
457
|
let stdout = '';
|
|
458
458
|
let stderr = '';
|
|
459
459
|
|
|
460
|
-
child.stdout.on('data',
|
|
460
|
+
child.stdout.on('data', data => {
|
|
461
461
|
stdout += data.toString();
|
|
462
462
|
});
|
|
463
463
|
|
|
464
|
-
child.stderr.on('data',
|
|
464
|
+
child.stderr.on('data', data => {
|
|
465
465
|
stderr += data.toString();
|
|
466
466
|
});
|
|
467
467
|
|
|
468
|
-
child.on('error',
|
|
468
|
+
child.on('error', error => {
|
|
469
469
|
resolve({
|
|
470
470
|
success: false,
|
|
471
471
|
output: stdout,
|
|
472
|
-
error: error.message
|
|
472
|
+
error: error.message,
|
|
473
473
|
});
|
|
474
474
|
});
|
|
475
475
|
|
|
476
|
-
child.on('close',
|
|
476
|
+
child.on('close', code => {
|
|
477
477
|
if (code === 0) {
|
|
478
478
|
resolve({
|
|
479
479
|
success: true,
|
|
480
|
-
output: stdout
|
|
480
|
+
output: stdout,
|
|
481
481
|
});
|
|
482
482
|
} else {
|
|
483
483
|
resolve({
|
|
484
484
|
success: false,
|
|
485
485
|
output: stdout,
|
|
486
|
-
error: stderr || `Command exited with code ${code}
|
|
486
|
+
error: stderr || `Command exited with code ${code}`,
|
|
487
487
|
});
|
|
488
488
|
}
|
|
489
489
|
});
|
|
@@ -616,15 +616,12 @@ function mergeArgsWithOverrides(userArgs, overrides) {
|
|
|
616
616
|
*/
|
|
617
617
|
function validateGitHubUrl(args, options = {}) {
|
|
618
618
|
// Default options for /solve command (backward compatibility)
|
|
619
|
-
const {
|
|
620
|
-
allowedTypes = ['issue', 'pull'],
|
|
621
|
-
commandName = 'solve'
|
|
622
|
-
} = options;
|
|
619
|
+
const { allowedTypes = ['issue', 'pull'], commandName = 'solve' } = options;
|
|
623
620
|
|
|
624
621
|
if (args.length === 0) {
|
|
625
622
|
return {
|
|
626
623
|
valid: false,
|
|
627
|
-
error: `Missing GitHub URL. Usage: /${commandName} <github-url> [options]
|
|
624
|
+
error: `Missing GitHub URL. Usage: /${commandName} <github-url> [options]`,
|
|
628
625
|
};
|
|
629
626
|
}
|
|
630
627
|
|
|
@@ -632,7 +629,7 @@ function validateGitHubUrl(args, options = {}) {
|
|
|
632
629
|
if (!url.includes('github.com')) {
|
|
633
630
|
return {
|
|
634
631
|
valid: false,
|
|
635
|
-
error: 'First argument must be a GitHub URL'
|
|
632
|
+
error: 'First argument must be a GitHub URL',
|
|
636
633
|
};
|
|
637
634
|
}
|
|
638
635
|
|
|
@@ -642,16 +639,16 @@ function validateGitHubUrl(args, options = {}) {
|
|
|
642
639
|
return {
|
|
643
640
|
valid: false,
|
|
644
641
|
error: parsed.error || 'Invalid GitHub URL',
|
|
645
|
-
suggestion: parsed.suggestion
|
|
642
|
+
suggestion: parsed.suggestion,
|
|
646
643
|
};
|
|
647
644
|
}
|
|
648
645
|
|
|
649
646
|
// Check if the URL type is allowed for this command
|
|
650
647
|
if (!allowedTypes.includes(parsed.type)) {
|
|
651
|
-
const allowedTypesStr = allowedTypes.map(t => t === 'pull' ? 'pull request' : t).join(', ');
|
|
648
|
+
const allowedTypesStr = allowedTypes.map(t => (t === 'pull' ? 'pull request' : t)).join(', ');
|
|
652
649
|
return {
|
|
653
650
|
valid: false,
|
|
654
|
-
error: `URL must be a GitHub ${allowedTypesStr} (not ${parsed.type})
|
|
651
|
+
error: `URL must be a GitHub ${allowedTypesStr} (not ${parsed.type})`,
|
|
655
652
|
};
|
|
656
653
|
}
|
|
657
654
|
|
|
@@ -702,12 +699,12 @@ function extractGitHubUrl(text) {
|
|
|
702
699
|
return {
|
|
703
700
|
url: null,
|
|
704
701
|
error: `Found ${foundUrls.length} GitHub links in the message. Please reply to a message with only one GitHub issue or PR link.`,
|
|
705
|
-
linkCount: foundUrls.length
|
|
702
|
+
linkCount: foundUrls.length,
|
|
706
703
|
};
|
|
707
704
|
}
|
|
708
705
|
}
|
|
709
706
|
|
|
710
|
-
bot.command('help', async
|
|
707
|
+
bot.command('help', async ctx => {
|
|
711
708
|
if (VERBOSE) {
|
|
712
709
|
console.log('[VERBOSE] /help command received');
|
|
713
710
|
}
|
|
@@ -795,7 +792,7 @@ bot.command('help', async (ctx) => {
|
|
|
795
792
|
await ctx.reply(message, { parse_mode: 'Markdown' });
|
|
796
793
|
});
|
|
797
794
|
|
|
798
|
-
bot.command('limits', async
|
|
795
|
+
bot.command('limits', async ctx => {
|
|
799
796
|
if (VERBOSE) {
|
|
800
797
|
console.log('[VERBOSE] /limits command received');
|
|
801
798
|
}
|
|
@@ -847,7 +844,9 @@ bot.command('limits', async (ctx) => {
|
|
|
847
844
|
}
|
|
848
845
|
|
|
849
846
|
// Send "fetching" message to indicate work is in progress
|
|
850
|
-
const fetchingMessage = await ctx.reply('🔄 Fetching Claude usage limits...', {
|
|
847
|
+
const fetchingMessage = await ctx.reply('🔄 Fetching Claude usage limits...', {
|
|
848
|
+
reply_to_message_id: ctx.message.message_id,
|
|
849
|
+
});
|
|
851
850
|
|
|
852
851
|
// Get the usage limits using the library function
|
|
853
852
|
const result = await getClaudeUsageLimits(VERBOSE);
|
|
@@ -856,39 +855,36 @@ bot.command('limits', async (ctx) => {
|
|
|
856
855
|
// Edit the fetching message to show the error
|
|
857
856
|
// Escape the error message for MarkdownV2, preserving inline code blocks
|
|
858
857
|
const escapedError = escapeMarkdownV2(result.error, { preserveCodeBlocks: true });
|
|
859
|
-
await ctx.telegram.editMessageText(
|
|
860
|
-
fetchingMessage.chat.id,
|
|
861
|
-
fetchingMessage.message_id,
|
|
862
|
-
undefined,
|
|
863
|
-
`❌ ${escapedError}`,
|
|
864
|
-
{ parse_mode: 'MarkdownV2' }
|
|
865
|
-
);
|
|
858
|
+
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, `❌ ${escapedError}`, { parse_mode: 'MarkdownV2' });
|
|
866
859
|
return;
|
|
867
860
|
}
|
|
868
861
|
|
|
869
862
|
// Format and edit the fetching message with the results
|
|
870
863
|
const message = '📊 *Claude Usage Limits*\n\n' + formatUsageMessage(result.usage);
|
|
871
|
-
await ctx.telegram.editMessageText(
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
undefined,
|
|
875
|
-
message,
|
|
876
|
-
{ parse_mode: 'Markdown' }
|
|
877
|
-
);
|
|
864
|
+
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, message, {
|
|
865
|
+
parse_mode: 'Markdown',
|
|
866
|
+
});
|
|
878
867
|
});
|
|
879
|
-
bot.command('version', async
|
|
868
|
+
bot.command('version', async ctx => {
|
|
880
869
|
VERBOSE && console.log('[VERBOSE] /version command received');
|
|
881
|
-
await addBreadcrumb({
|
|
870
|
+
await addBreadcrumb({
|
|
871
|
+
category: 'telegram.command',
|
|
872
|
+
message: '/version command received',
|
|
873
|
+
level: 'info',
|
|
874
|
+
data: { chatId: ctx.chat?.id, chatType: ctx.chat?.type, userId: ctx.from?.id, username: ctx.from?.username },
|
|
875
|
+
});
|
|
882
876
|
if (isOldMessage(ctx) || isForwardedOrReply(ctx)) return;
|
|
883
877
|
if (!isGroupChat(ctx)) return await ctx.reply('❌ The /version command only works in group chats. Please add this bot to a group and make it an admin.', { reply_to_message_id: ctx.message.message_id });
|
|
884
878
|
const chatId = ctx.chat.id;
|
|
885
879
|
if (!isChatAuthorized(chatId)) return await ctx.reply(`❌ This chat (ID: ${chatId}) is not authorized to use this bot. Please contact the bot administrator.`, { reply_to_message_id: ctx.message.message_id });
|
|
886
|
-
const fetchingMessage = await ctx.reply('🔄 Gathering version information...', {
|
|
880
|
+
const fetchingMessage = await ctx.reply('🔄 Gathering version information...', {
|
|
881
|
+
reply_to_message_id: ctx.message.message_id,
|
|
882
|
+
});
|
|
887
883
|
const result = await getVersionInfo(VERBOSE);
|
|
888
884
|
if (!result.success) return await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, `❌ ${escapeMarkdownV2(result.error, { preserveCodeBlocks: true })}`, { parse_mode: 'MarkdownV2' });
|
|
889
885
|
await ctx.telegram.editMessageText(fetchingMessage.chat.id, fetchingMessage.message_id, undefined, '🤖 *Version Information*\n\n' + formatVersionMessage(result.versions), { parse_mode: 'Markdown' });
|
|
890
886
|
});
|
|
891
|
-
bot.command(/^solve$/i, async
|
|
887
|
+
bot.command(/^solve$/i, async ctx => {
|
|
892
888
|
if (VERBOSE) {
|
|
893
889
|
console.log('[VERBOSE] /solve command received');
|
|
894
890
|
}
|
|
@@ -926,9 +922,7 @@ bot.command(/^solve$/i, async (ctx) => {
|
|
|
926
922
|
// But allow reply messages for URL extraction feature
|
|
927
923
|
const message = ctx.message;
|
|
928
924
|
const isForwarded = message.forward_origin && message.forward_origin.type;
|
|
929
|
-
const isOldApiForwarded = message.forward_from || message.forward_from_chat ||
|
|
930
|
-
message.forward_from_message_id || message.forward_signature ||
|
|
931
|
-
message.forward_sender_name || message.forward_date;
|
|
925
|
+
const isOldApiForwarded = message.forward_from || message.forward_from_chat || message.forward_from_message_id || message.forward_signature || message.forward_sender_name || message.forward_date;
|
|
932
926
|
|
|
933
927
|
if (isForwarded || isOldApiForwarded) {
|
|
934
928
|
if (VERBOSE) {
|
|
@@ -962,9 +956,7 @@ bot.command(/^solve$/i, async (ctx) => {
|
|
|
962
956
|
|
|
963
957
|
// Check if this is a reply to a message and user didn't provide URL
|
|
964
958
|
// In that case, try to extract GitHub URL from the replied message
|
|
965
|
-
const isReply = message.reply_to_message &&
|
|
966
|
-
message.reply_to_message.message_id &&
|
|
967
|
-
!message.reply_to_message.forum_topic_created;
|
|
959
|
+
const isReply = message.reply_to_message && message.reply_to_message.message_id && !message.reply_to_message.forum_topic_created;
|
|
968
960
|
|
|
969
961
|
if (isReply && userArgs.length === 0) {
|
|
970
962
|
if (VERBOSE) {
|
|
@@ -979,7 +971,10 @@ bot.command(/^solve$/i, async (ctx) => {
|
|
|
979
971
|
if (VERBOSE) {
|
|
980
972
|
console.log('[VERBOSE] Multiple GitHub URLs found in replied message');
|
|
981
973
|
}
|
|
982
|
-
await ctx.reply(`❌ ${extraction.error}`, {
|
|
974
|
+
await ctx.reply(`❌ ${extraction.error}`, {
|
|
975
|
+
parse_mode: 'Markdown',
|
|
976
|
+
reply_to_message_id: ctx.message.message_id,
|
|
977
|
+
});
|
|
983
978
|
return;
|
|
984
979
|
} else if (extraction.url) {
|
|
985
980
|
// Single link found
|
|
@@ -1037,17 +1032,18 @@ bot.command(/^solve$/i, async (ctx) => {
|
|
|
1037
1032
|
// Configure yargs to throw errors instead of trying to exit the process
|
|
1038
1033
|
// This prevents confusing error messages when validation fails but execution continues
|
|
1039
1034
|
let failureMessage = null;
|
|
1040
|
-
testYargs
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
throw new Error(failureMessage);
|
|
1046
|
-
});
|
|
1035
|
+
testYargs.exitProcess(false).fail((msg, err) => {
|
|
1036
|
+
// Capture the failure message instead of letting yargs print it
|
|
1037
|
+
failureMessage = msg || (err && err.message) || 'Unknown validation error';
|
|
1038
|
+
throw new Error(failureMessage);
|
|
1039
|
+
});
|
|
1047
1040
|
|
|
1048
1041
|
testYargs.parse(args);
|
|
1049
1042
|
} catch (error) {
|
|
1050
|
-
await ctx.reply(`❌ Invalid options: ${error.message || String(error)}\n\nUse /help to see available options`, {
|
|
1043
|
+
await ctx.reply(`❌ Invalid options: ${error.message || String(error)}\n\nUse /help to see available options`, {
|
|
1044
|
+
parse_mode: 'Markdown',
|
|
1045
|
+
reply_to_message_id: ctx.message.message_id,
|
|
1046
|
+
});
|
|
1051
1047
|
return;
|
|
1052
1048
|
}
|
|
1053
1049
|
|
|
@@ -1068,8 +1064,7 @@ bot.command(/^solve$/i, async (ctx) => {
|
|
|
1068
1064
|
}
|
|
1069
1065
|
|
|
1070
1066
|
if (result.success) {
|
|
1071
|
-
const sessionNameMatch = result.output.match(/session:\s*(\S+)/i) ||
|
|
1072
|
-
result.output.match(/screen -r\s+(\S+)/);
|
|
1067
|
+
const sessionNameMatch = result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -r\s+(\S+)/);
|
|
1073
1068
|
const sessionName = sessionNameMatch ? sessionNameMatch[1] : 'unknown';
|
|
1074
1069
|
|
|
1075
1070
|
let response = '✅ Solve command started successfully!\n\n';
|
|
@@ -1083,7 +1078,7 @@ bot.command(/^solve$/i, async (ctx) => {
|
|
|
1083
1078
|
}
|
|
1084
1079
|
});
|
|
1085
1080
|
|
|
1086
|
-
bot.command(/^hive$/i, async
|
|
1081
|
+
bot.command(/^hive$/i, async ctx => {
|
|
1087
1082
|
if (VERBOSE) {
|
|
1088
1083
|
console.log('[VERBOSE] /hive command received');
|
|
1089
1084
|
}
|
|
@@ -1151,7 +1146,7 @@ bot.command(/^hive$/i, async (ctx) => {
|
|
|
1151
1146
|
const validation = validateGitHubUrl(userArgs, {
|
|
1152
1147
|
allowedTypes: ['repo', 'organization', 'user'],
|
|
1153
1148
|
commandName: 'hive',
|
|
1154
|
-
exampleUrl: 'https://github.com/owner/repo'
|
|
1149
|
+
exampleUrl: 'https://github.com/owner/repo',
|
|
1155
1150
|
});
|
|
1156
1151
|
if (!validation.valid) {
|
|
1157
1152
|
let errorMsg = `❌ ${validation.error}`;
|
|
@@ -1191,17 +1186,18 @@ bot.command(/^hive$/i, async (ctx) => {
|
|
|
1191
1186
|
// Configure yargs to throw errors instead of trying to exit the process
|
|
1192
1187
|
// This prevents confusing error messages when validation fails but execution continues
|
|
1193
1188
|
let failureMessage = null;
|
|
1194
|
-
testYargs
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
throw new Error(failureMessage);
|
|
1200
|
-
});
|
|
1189
|
+
testYargs.exitProcess(false).fail((msg, err) => {
|
|
1190
|
+
// Capture the failure message instead of letting yargs print it
|
|
1191
|
+
failureMessage = msg || (err && err.message) || 'Unknown validation error';
|
|
1192
|
+
throw new Error(failureMessage);
|
|
1193
|
+
});
|
|
1201
1194
|
|
|
1202
1195
|
testYargs.parse(args);
|
|
1203
1196
|
} catch (error) {
|
|
1204
|
-
await ctx.reply(`❌ Invalid options: ${error.message || String(error)}\n\nUse /help to see available options`, {
|
|
1197
|
+
await ctx.reply(`❌ Invalid options: ${error.message || String(error)}\n\nUse /help to see available options`, {
|
|
1198
|
+
parse_mode: 'Markdown',
|
|
1199
|
+
reply_to_message_id: ctx.message.message_id,
|
|
1200
|
+
});
|
|
1205
1201
|
return;
|
|
1206
1202
|
}
|
|
1207
1203
|
|
|
@@ -1222,8 +1218,7 @@ bot.command(/^hive$/i, async (ctx) => {
|
|
|
1222
1218
|
}
|
|
1223
1219
|
|
|
1224
1220
|
if (result.success) {
|
|
1225
|
-
const sessionNameMatch = result.output.match(/session:\s*(\S+)/i) ||
|
|
1226
|
-
result.output.match(/screen -r\s+(\S+)/);
|
|
1221
|
+
const sessionNameMatch = result.output.match(/session:\s*(\S+)/i) || result.output.match(/screen -r\s+(\S+)/);
|
|
1227
1222
|
const sessionName = sessionNameMatch ? sessionNameMatch[1] : 'unknown';
|
|
1228
1223
|
|
|
1229
1224
|
let response = '✅ Hive command started successfully!\n\n';
|
|
@@ -1245,7 +1240,7 @@ registerTopCommand(bot, {
|
|
|
1245
1240
|
isOldMessage,
|
|
1246
1241
|
isForwardedOrReply,
|
|
1247
1242
|
isGroupChat,
|
|
1248
|
-
isChatAuthorized
|
|
1243
|
+
isChatAuthorized,
|
|
1249
1244
|
});
|
|
1250
1245
|
|
|
1251
1246
|
// Add message listener for verbose debugging
|
|
@@ -1253,19 +1248,29 @@ if (VERBOSE) {
|
|
|
1253
1248
|
bot.on('message', (ctx, next) => {
|
|
1254
1249
|
const msg = ctx.message;
|
|
1255
1250
|
console.log('[VERBOSE] Message:', {
|
|
1256
|
-
chatId: ctx.chat?.id,
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1251
|
+
chatId: ctx.chat?.id,
|
|
1252
|
+
chatType: ctx.chat?.type,
|
|
1253
|
+
isForum: ctx.chat?.is_forum,
|
|
1254
|
+
isTopicMsg: msg?.is_topic_message,
|
|
1255
|
+
threadId: msg?.message_thread_id,
|
|
1256
|
+
date: msg?.date,
|
|
1257
|
+
text: msg?.text?.substring(0, 100),
|
|
1258
|
+
user: ctx.from?.username || ctx.from?.id,
|
|
1259
|
+
botStartTime: BOT_START_TIME,
|
|
1260
|
+
isOld: isOldMessage(ctx),
|
|
1261
|
+
isForwarded: isForwardedOrReply(ctx),
|
|
1262
|
+
isAuthorized: isChatAuthorized(ctx.chat?.id),
|
|
1261
1263
|
});
|
|
1262
1264
|
if (msg) {
|
|
1263
1265
|
console.log('[VERBOSE] Msg fields:', Object.keys(msg));
|
|
1264
1266
|
console.log('[VERBOSE] Forward/reply:', {
|
|
1265
|
-
forward_origin: msg.forward_origin,
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1267
|
+
forward_origin: msg.forward_origin,
|
|
1268
|
+
forward_from: msg.forward_from,
|
|
1269
|
+
forward_from_chat: msg.forward_from_chat,
|
|
1270
|
+
forward_date: msg.forward_date,
|
|
1271
|
+
reply_to_message: msg.reply_to_message,
|
|
1272
|
+
reply_id: msg.reply_to_message?.message_id,
|
|
1273
|
+
forum_topic_created: msg.reply_to_message?.forum_topic_created,
|
|
1269
1274
|
});
|
|
1270
1275
|
}
|
|
1271
1276
|
return next();
|
|
@@ -1335,15 +1340,13 @@ bot.catch((error, ctx) => {
|
|
|
1335
1340
|
errorMessage += `\n🔍 **Debug info:** Update ID: ${ctx.update.update_id}`;
|
|
1336
1341
|
}
|
|
1337
1342
|
|
|
1338
|
-
ctx.reply(errorMessage, { parse_mode: 'Markdown' })
|
|
1339
|
-
.
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
.catch(fallbackError => {
|
|
1344
|
-
console.error('Failed to send fallback error message:', fallbackError);
|
|
1345
|
-
});
|
|
1343
|
+
ctx.reply(errorMessage, { parse_mode: 'Markdown' }).catch(replyError => {
|
|
1344
|
+
console.error('Failed to send error message to user:', replyError);
|
|
1345
|
+
// Try sending a simple text message without Markdown if Markdown parsing failed
|
|
1346
|
+
ctx.reply('❌ An error occurred while processing your request. Please try again or contact support.').catch(fallbackError => {
|
|
1347
|
+
console.error('Failed to send fallback error message:', fallbackError);
|
|
1346
1348
|
});
|
|
1349
|
+
});
|
|
1347
1350
|
}
|
|
1348
1351
|
});
|
|
1349
1352
|
|
|
@@ -1359,7 +1362,7 @@ if (allowedChats && allowedChats.length > 0) {
|
|
|
1359
1362
|
}
|
|
1360
1363
|
console.log('Commands enabled:', {
|
|
1361
1364
|
solve: solveEnabled,
|
|
1362
|
-
hive: hiveEnabled
|
|
1365
|
+
hive: hiveEnabled,
|
|
1363
1366
|
});
|
|
1364
1367
|
if (solveOverrides.length > 0) {
|
|
1365
1368
|
console.log('Solve overrides (lino):', lino.format(solveOverrides));
|
|
@@ -1380,8 +1383,9 @@ if (VERBOSE) {
|
|
|
1380
1383
|
if (VERBOSE) {
|
|
1381
1384
|
console.log('[VERBOSE] Deleting webhook...');
|
|
1382
1385
|
}
|
|
1383
|
-
bot.telegram
|
|
1384
|
-
.
|
|
1386
|
+
bot.telegram
|
|
1387
|
+
.deleteWebhook({ drop_pending_updates: true })
|
|
1388
|
+
.then(result => {
|
|
1385
1389
|
if (VERBOSE) {
|
|
1386
1390
|
console.log('[VERBOSE] Webhook deletion result:', result);
|
|
1387
1391
|
}
|
|
@@ -1389,7 +1393,7 @@ bot.telegram.deleteWebhook({ drop_pending_updates: true })
|
|
|
1389
1393
|
if (VERBOSE) {
|
|
1390
1394
|
console.log('[VERBOSE] Launching bot with config:', {
|
|
1391
1395
|
allowedUpdates: ['message'],
|
|
1392
|
-
dropPendingUpdates: true
|
|
1396
|
+
dropPendingUpdates: true,
|
|
1393
1397
|
});
|
|
1394
1398
|
}
|
|
1395
1399
|
return bot.launch({
|
|
@@ -1398,7 +1402,7 @@ bot.telegram.deleteWebhook({ drop_pending_updates: true })
|
|
|
1398
1402
|
allowedUpdates: ['message', 'callback_query'],
|
|
1399
1403
|
// Drop any pending updates that were sent before the bot started
|
|
1400
1404
|
// This ensures we only process new messages sent after this bot instance started
|
|
1401
|
-
dropPendingUpdates: true
|
|
1405
|
+
dropPendingUpdates: true,
|
|
1402
1406
|
});
|
|
1403
1407
|
})
|
|
1404
1408
|
.then(async () => {
|
|
@@ -1446,12 +1450,12 @@ bot.telegram.deleteWebhook({ drop_pending_updates: true })
|
|
|
1446
1450
|
console.log('[VERBOSE] Send a message to the bot to test message reception');
|
|
1447
1451
|
}
|
|
1448
1452
|
})
|
|
1449
|
-
.catch(
|
|
1453
|
+
.catch(error => {
|
|
1450
1454
|
console.error('❌ Failed to start bot:', error);
|
|
1451
1455
|
console.error('Error details:', {
|
|
1452
1456
|
message: error.message,
|
|
1453
1457
|
code: error.code,
|
|
1454
|
-
stack: error.stack?.split('\n').slice(0, 5).join('\n')
|
|
1458
|
+
stack: error.stack?.split('\n').slice(0, 5).join('\n'),
|
|
1455
1459
|
});
|
|
1456
1460
|
if (VERBOSE) {
|
|
1457
1461
|
console.error('[VERBOSE] Full error:', error);
|