@link-assistant/hive-mind 2.0.26 → 2.0.28
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/agent.prompts.lib.mjs +6 -0
- package/src/bidirectional-interactive.lib.mjs +15 -0
- package/src/claude.lib.mjs +60 -64
- package/src/claude.prompts.lib.mjs +6 -0
- package/src/codex.lib.mjs +44 -1
- package/src/codex.prompts.lib.mjs +6 -0
- package/src/gemini.prompts.lib.mjs +6 -0
- package/src/locales/en.lino +5 -5
- package/src/locales/hi.lino +5 -5
- package/src/locales/ru.lino +5 -5
- package/src/locales/zh.lino +5 -5
- package/src/opencode.prompts.lib.mjs +6 -1
- package/src/queue-config.lib.mjs +1 -1
- package/src/qwen.prompts.lib.mjs +6 -1
- package/src/session-monitor.lib.mjs +1 -1
- package/src/solve-option-contract.prompts.lib.mjs +19 -0
- package/src/solve.auto-merge.lib.mjs +13 -0
- package/src/solve.auto-pr.lib.mjs +1 -1
- package/src/solve.finalize.lib.mjs +21 -0
- package/src/solve.mjs +11 -19
- package/src/solve.pr-base-command-intervention.lib.mjs +62 -0
- package/src/solve.pr-base-guard.lib.mjs +266 -0
- package/src/solve.restart-shared.lib.mjs +2 -0
- package/src/telegram-bot.mjs +1 -2
- package/src/telegram-solve-queue-command.lib.mjs +12 -17
- package/src/telegram-solve-queue.helpers.lib.mjs +5 -5
- package/src/telegram-solve-queue.lib.mjs +2 -2
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guard PR base branch changes made during an agent session.
|
|
3
|
+
*
|
|
4
|
+
* The solve command creates or continues work against a target base branch.
|
|
5
|
+
* When --base-branch is explicit, that target is a user request, not a
|
|
6
|
+
* suggestion for the agent to retarget later.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ghWithRateLimitRetry } from './github-rate-limit.lib.mjs';
|
|
10
|
+
|
|
11
|
+
function normalizeBranchName(value) {
|
|
12
|
+
return String(value || '').trim();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function commandOutput(result) {
|
|
16
|
+
return [result?.stderr, result?.stdout]
|
|
17
|
+
.filter(Boolean)
|
|
18
|
+
.map(output => output.toString().trim())
|
|
19
|
+
.filter(Boolean)
|
|
20
|
+
.join('\n')
|
|
21
|
+
.trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function fallbackFormatAligned(icon, label, value) {
|
|
25
|
+
return [icon, label, value].filter(Boolean).join(' ');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stripShellTokenPunctuation(value) {
|
|
29
|
+
const token = String(value || '');
|
|
30
|
+
if (isCommandBoundary(token)) return token;
|
|
31
|
+
return token.replace(/^(?:\(|\{|\[)+|(?:;|\)|&|\||\])+$/g, '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function tokenizeShellCommand(command) {
|
|
35
|
+
const tokens = [];
|
|
36
|
+
const pattern = /"((?:\\.|[^"\\])*)"|'((?:\\.|[^'\\])*)'|(\S+)/g;
|
|
37
|
+
let match;
|
|
38
|
+
while ((match = pattern.exec(String(command || ''))) !== null) {
|
|
39
|
+
const rawToken = match[1] ?? match[2] ?? match[3] ?? '';
|
|
40
|
+
const token = stripShellTokenPunctuation(rawToken);
|
|
41
|
+
if (token) tokens.push(token);
|
|
42
|
+
}
|
|
43
|
+
return tokens;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isCommandBoundary(token) {
|
|
47
|
+
return token === '&&' || token === '||' || token === ';' || token === '|';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isGhToken(token) {
|
|
51
|
+
return token === 'gh' || token.endsWith('/gh');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function commandTargetsPullRequest(target, prNumber) {
|
|
55
|
+
if (!prNumber || !target) return true;
|
|
56
|
+
const normalizedTarget = String(target);
|
|
57
|
+
const normalizedPrNumber = String(prNumber);
|
|
58
|
+
return normalizedTarget === normalizedPrNumber || normalizedTarget.endsWith(`/pull/${normalizedPrNumber}`) || normalizedTarget.endsWith(`/pulls/${normalizedPrNumber}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function parseGhPrEditBaseChange(tokens, startIndex, prNumber) {
|
|
62
|
+
if (tokens[startIndex + 1] !== 'pr' || tokens[startIndex + 2] !== 'edit') return null;
|
|
63
|
+
|
|
64
|
+
let targetPullRequest = null;
|
|
65
|
+
let attemptedBaseBranch = null;
|
|
66
|
+
const optionsWithValues = new Set(['--repo', '-R', '--title', '--body', '--body-file', '--add-label', '--remove-label', '--add-assignee', '--remove-assignee', '--milestone', '--project']);
|
|
67
|
+
for (let index = startIndex + 3; index < tokens.length; index++) {
|
|
68
|
+
const token = tokens[index];
|
|
69
|
+
if (isCommandBoundary(token)) break;
|
|
70
|
+
|
|
71
|
+
if (token === '--base' || token === '-B') {
|
|
72
|
+
attemptedBaseBranch = normalizeBranchName(tokens[index + 1]);
|
|
73
|
+
index++;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (token.startsWith('--base=')) {
|
|
77
|
+
attemptedBaseBranch = normalizeBranchName(token.slice('--base='.length));
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (optionsWithValues.has(token)) {
|
|
81
|
+
index++;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (!targetPullRequest && !token.startsWith('-')) {
|
|
85
|
+
targetPullRequest = token;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!attemptedBaseBranch || !commandTargetsPullRequest(targetPullRequest, prNumber)) return null;
|
|
90
|
+
return { attemptedBaseBranch, commandKind: 'gh_pr_edit' };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseGhApiPullRequestBaseChange(tokens, startIndex, prNumber) {
|
|
94
|
+
if (tokens[startIndex + 1] !== 'api') return null;
|
|
95
|
+
|
|
96
|
+
let endpointTargetsPullRequest = false;
|
|
97
|
+
let attemptedBaseBranch = null;
|
|
98
|
+
for (let index = startIndex + 2; index < tokens.length; index++) {
|
|
99
|
+
const token = tokens[index];
|
|
100
|
+
if (isCommandBoundary(token)) break;
|
|
101
|
+
|
|
102
|
+
if (commandTargetsPullRequest(token, prNumber) && token.includes('/pulls/')) {
|
|
103
|
+
endpointTargetsPullRequest = true;
|
|
104
|
+
}
|
|
105
|
+
if (token === '-f' || token === '--field' || token === '-F' || token === '--raw-field') {
|
|
106
|
+
const field = tokens[index + 1] || '';
|
|
107
|
+
if (field.startsWith('base=')) {
|
|
108
|
+
attemptedBaseBranch = normalizeBranchName(field.slice('base='.length));
|
|
109
|
+
}
|
|
110
|
+
index++;
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
if (token.startsWith('-fbase=') || token.startsWith('--field=base=')) {
|
|
114
|
+
attemptedBaseBranch = normalizeBranchName(token.split('base=').at(-1));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (!endpointTargetsPullRequest || !attemptedBaseBranch) return null;
|
|
119
|
+
return { attemptedBaseBranch, commandKind: 'gh_api_pull_update' };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function getExpectedPullRequestBaseBranch({ argv = {} } = {}) {
|
|
123
|
+
const requestedBaseBranch = normalizeBranchName(argv?.baseBranch);
|
|
124
|
+
return requestedBaseBranch || null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function detectForbiddenPullRequestBaseChangeCommand(command, { expectedBaseBranch, prNumber } = {}) {
|
|
128
|
+
const normalizedExpectedBaseBranch = normalizeBranchName(expectedBaseBranch);
|
|
129
|
+
if (!normalizedExpectedBaseBranch || typeof command !== 'string' || !command.trim()) return null;
|
|
130
|
+
|
|
131
|
+
const tokens = tokenizeShellCommand(command);
|
|
132
|
+
for (let index = 0; index < tokens.length; index++) {
|
|
133
|
+
if (!isGhToken(tokens[index])) continue;
|
|
134
|
+
const parsed = parseGhPrEditBaseChange(tokens, index, prNumber) || parseGhApiPullRequestBaseChange(tokens, index, prNumber);
|
|
135
|
+
if (!parsed) continue;
|
|
136
|
+
if (parsed.attemptedBaseBranch === normalizedExpectedBaseBranch) continue;
|
|
137
|
+
return {
|
|
138
|
+
command,
|
|
139
|
+
commandKind: parsed.commandKind,
|
|
140
|
+
attemptedBaseBranch: parsed.attemptedBaseBranch,
|
|
141
|
+
expectedBaseBranch: normalizedExpectedBaseBranch,
|
|
142
|
+
prNumber: prNumber || null,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
export function extractToolCommandTextsFromStreamEvent(event) {
|
|
150
|
+
const commands = [];
|
|
151
|
+
const seen = new Set();
|
|
152
|
+
const visit = value => {
|
|
153
|
+
if (!value || typeof value !== 'object') return;
|
|
154
|
+
if (typeof value.command === 'string' && value.command.trim() && !seen.has(value.command)) {
|
|
155
|
+
seen.add(value.command);
|
|
156
|
+
commands.push(value.command);
|
|
157
|
+
}
|
|
158
|
+
if (Array.isArray(value)) {
|
|
159
|
+
for (const item of value) visit(item);
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
for (const child of Object.values(value)) {
|
|
163
|
+
visit(child);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
visit(event);
|
|
167
|
+
return commands;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export function buildPullRequestBaseBranchInterventionMessage({ prNumber, expectedBaseBranch, attemptedBaseBranch, command } = {}) {
|
|
171
|
+
const expected = normalizeBranchName(expectedBaseBranch);
|
|
172
|
+
const attempted = normalizeBranchName(attemptedBaseBranch);
|
|
173
|
+
const pullRequestLabel = prNumber ? `PR #${prNumber}` : 'the pull request';
|
|
174
|
+
const attemptedText = attempted ? ` to ${attempted}` : '';
|
|
175
|
+
const commandText = command ? `\nForbidden command observed: ${command}` : '';
|
|
176
|
+
|
|
177
|
+
return `The user requested --base-branch ${expected}. ${pullRequestLabel} must keep that base branch. Do not change ${pullRequestLabel}'s base${attemptedText}. Restore or keep the base as ${expected}, then continue finishing the pull request and make it ready for review.${commandText}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
export function buildPullRequestBaseBranchMismatchMessage({ prNumber, currentBaseBranch, expectedBaseBranch, operation = 'verify' } = {}) {
|
|
181
|
+
const action = operation === 'auto-merge' ? 'auto-merge' : 'continue';
|
|
182
|
+
return `Cannot ${action} PR #${prNumber} because its base branch changed to ${currentBaseBranch}. The user requested --base-branch ${expectedBaseBranch}; restore the PR base to ${expectedBaseBranch} before ${action}.`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
export async function getPullRequestBaseBranch({ owner, repo, prNumber, $, log }) {
|
|
186
|
+
if (typeof $ !== 'function') {
|
|
187
|
+
throw new Error('Cannot verify pull request base branch without a command runner');
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const result = await ghWithRateLimitRetry(() => $`gh pr view ${prNumber} --repo ${owner}/${repo} --json baseRefName --jq .baseRefName`, {
|
|
191
|
+
label: 'gh pr view baseRefName',
|
|
192
|
+
log,
|
|
193
|
+
});
|
|
194
|
+
if (result.code !== 0) {
|
|
195
|
+
const details = commandOutput(result) || 'unknown error';
|
|
196
|
+
throw new Error(`Could not verify pull request base branch for #${prNumber}: ${details}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const baseBranch = normalizeBranchName(result.stdout);
|
|
200
|
+
if (!baseBranch) {
|
|
201
|
+
throw new Error(`Could not verify pull request base branch for #${prNumber}: gh returned an empty baseRefName`);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return baseBranch;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export async function ensurePullRequestBaseBranch({ owner, repo, prNumber, argv = {}, log = async () => {}, formatAligned = fallbackFormatAligned, $, onMismatch = 'restore', operation = 'verify' }) {
|
|
208
|
+
const expectedBaseBranch = getExpectedPullRequestBaseBranch({ argv });
|
|
209
|
+
if (!expectedBaseBranch) {
|
|
210
|
+
return { checked: false, restored: false, reason: 'no_explicit_base_branch' };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!owner || !repo || !prNumber) {
|
|
214
|
+
return { checked: false, restored: false, reason: 'missing_pull_request_context' };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const currentBaseBranch = await getPullRequestBaseBranch({ owner, repo, prNumber, $, log });
|
|
218
|
+
if (currentBaseBranch === expectedBaseBranch) {
|
|
219
|
+
await log(formatAligned('🎯', 'Base branch locked:', `${expectedBaseBranch} (verified)`, 2), { verbose: true });
|
|
220
|
+
return {
|
|
221
|
+
checked: true,
|
|
222
|
+
restored: false,
|
|
223
|
+
currentBaseBranch,
|
|
224
|
+
expectedBaseBranch,
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
await log(formatAligned('⚠️', 'Base branch changed:', `PR #${prNumber} targets ${currentBaseBranch}, expected ${expectedBaseBranch}`, 2), { level: 'warning' });
|
|
229
|
+
|
|
230
|
+
if (onMismatch === 'throw' || onMismatch === 'fail') {
|
|
231
|
+
throw new Error(
|
|
232
|
+
buildPullRequestBaseBranchMismatchMessage({
|
|
233
|
+
prNumber,
|
|
234
|
+
currentBaseBranch,
|
|
235
|
+
expectedBaseBranch,
|
|
236
|
+
operation,
|
|
237
|
+
})
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await log(formatAligned('🔁', 'Restoring PR base:', expectedBaseBranch, 2));
|
|
242
|
+
|
|
243
|
+
const editResult = await ghWithRateLimitRetry(() => $`gh pr edit ${prNumber} --repo ${owner}/${repo} --base ${expectedBaseBranch}`, {
|
|
244
|
+
label: 'gh pr edit base',
|
|
245
|
+
log,
|
|
246
|
+
});
|
|
247
|
+
if (editResult.code !== 0) {
|
|
248
|
+
const details = commandOutput(editResult) || 'unknown error';
|
|
249
|
+
throw new Error(`Could not restore pull request #${prNumber} base branch to ${expectedBaseBranch}: ${details}`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const restoredBaseBranch = await getPullRequestBaseBranch({ owner, repo, prNumber, $, log });
|
|
253
|
+
if (restoredBaseBranch !== expectedBaseBranch) {
|
|
254
|
+
throw new Error(`Pull request #${prNumber} still targets ${restoredBaseBranch} after attempting to restore ${expectedBaseBranch}`);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
await log(formatAligned('✅', 'Base branch restored:', `PR #${prNumber} now targets ${expectedBaseBranch}`, 2));
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
checked: true,
|
|
261
|
+
restored: true,
|
|
262
|
+
previousBaseBranch: currentBaseBranch,
|
|
263
|
+
currentBaseBranch: restoredBaseBranch,
|
|
264
|
+
expectedBaseBranch,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -31,6 +31,7 @@ const fs = (await use('fs')).promises;
|
|
|
31
31
|
// Import shared library functions
|
|
32
32
|
const lib = await import('./lib.mjs');
|
|
33
33
|
const { log, formatAligned, extractToolErrorCore } = lib;
|
|
34
|
+
const { ensurePullRequestBaseBranch } = await import('./solve.pr-base-guard.lib.mjs');
|
|
34
35
|
|
|
35
36
|
// Import Sentry integration
|
|
36
37
|
const sentryLib = await import('./sentry.lib.mjs');
|
|
@@ -460,6 +461,7 @@ export const executeToolIteration = async params => {
|
|
|
460
461
|
});
|
|
461
462
|
}
|
|
462
463
|
|
|
464
|
+
await ensurePullRequestBaseBranch({ owner, repo, prNumber, argv, log, formatAligned, $ });
|
|
463
465
|
return toolResult;
|
|
464
466
|
};
|
|
465
467
|
|
package/src/telegram-bot.mjs
CHANGED
|
@@ -1234,8 +1234,7 @@ bot.on('message', async (ctx, next) => {
|
|
|
1234
1234
|
// /subscribe + /unsubscribe (#1688) are intentionally not in the text fallback — Telegraf's bot.command() is sufficient.
|
|
1235
1235
|
const solveHandlers = Object.fromEntries(SOLVE_COMMAND_NAMES.map(command => [command, handleSolveCommand]));
|
|
1236
1236
|
const taskHandlers = Object.fromEntries(TASK_COMMAND_NAMES.map(command => [command, handleTaskCommand]));
|
|
1237
|
-
|
|
1238
|
-
const handlers = { ...solveHandlers, ...taskHandlers, auth: handleAuthCommand, hive: handleHiveCommand, solve_queue: handleSolveQueueCommand, solvequeue: handleSolveQueueCommand, queue: handleSolveQueueCommand };
|
|
1237
|
+
const handlers = { ...solveHandlers, ...taskHandlers, auth: handleAuthCommand, hive: handleHiveCommand, queue: handleSolveQueueCommand };
|
|
1239
1238
|
|
|
1240
1239
|
const handler = handlers[extracted.command];
|
|
1241
1240
|
if (!handler) return next();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Telegram /
|
|
2
|
+
* Telegram /queue command implementation
|
|
3
3
|
*
|
|
4
|
-
* This module provides the /
|
|
4
|
+
* This module provides the /queue command functionality for the Telegram bot,
|
|
5
5
|
* allowing users to view the current solve queue status.
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { t } from './i18n.lib.mjs';
|
|
17
17
|
|
|
18
|
-
const GROUP_ONLY_MESSAGE = '❌ The /
|
|
18
|
+
const GROUP_ONLY_MESSAGE = '❌ The /queue command only works in group chats. Please add this bot to a group and make it an admin.';
|
|
19
19
|
|
|
20
20
|
function commandText(key, params = {}, locale = null, fallback = key) {
|
|
21
21
|
const translated = t(key, params, locale ? { locale } : {});
|
|
@@ -23,7 +23,7 @@ function commandText(key, params = {}, locale = null, fallback = key) {
|
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
/**
|
|
26
|
-
* Registers the /
|
|
26
|
+
* Registers the /queue command handler with the bot
|
|
27
27
|
* @param {Object} bot - The Telegraf bot instance
|
|
28
28
|
* @param {Object} options - Options object
|
|
29
29
|
* @param {boolean} options.VERBOSE - Whether to enable verbose logging
|
|
@@ -41,11 +41,11 @@ export function registerSolveQueueCommand(bot, options) {
|
|
|
41
41
|
const { VERBOSE = false, isOldMessage, isForwardedOrReply, isGroupChat, isChatAuthorized, isTopicAuthorized, buildAuthErrorMessage, addBreadcrumb, getSolveQueue, safeReply, resolveLocale } = options;
|
|
42
42
|
|
|
43
43
|
async function handleSolveQueueCommand(ctx) {
|
|
44
|
-
VERBOSE && console.log('[VERBOSE] /
|
|
44
|
+
VERBOSE && console.log('[VERBOSE] /queue command received');
|
|
45
45
|
|
|
46
46
|
await addBreadcrumb({
|
|
47
47
|
category: 'telegram.command',
|
|
48
|
-
message: '/
|
|
48
|
+
message: '/queue command received',
|
|
49
49
|
level: 'info',
|
|
50
50
|
data: { chatId: ctx.chat?.id, chatType: ctx.chat?.type, userId: ctx.from?.id, username: ctx.from?.username },
|
|
51
51
|
});
|
|
@@ -54,18 +54,18 @@ export function registerSolveQueueCommand(bot, options) {
|
|
|
54
54
|
|
|
55
55
|
// Ignore messages sent before bot started
|
|
56
56
|
if (isOldMessage(ctx)) {
|
|
57
|
-
VERBOSE && console.log('[VERBOSE] /
|
|
57
|
+
VERBOSE && console.log('[VERBOSE] /queue ignored: old message');
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
// Ignore forwarded or reply messages
|
|
62
62
|
if (isForwardedOrReply(ctx)) {
|
|
63
|
-
VERBOSE && console.log('[VERBOSE] /
|
|
63
|
+
VERBOSE && console.log('[VERBOSE] /queue ignored: forwarded or reply');
|
|
64
64
|
return;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
if (!isGroupChat(ctx)) {
|
|
68
|
-
VERBOSE && console.log('[VERBOSE] /
|
|
68
|
+
VERBOSE && console.log('[VERBOSE] /queue ignored: not a group chat');
|
|
69
69
|
await replyWithFallback(commandText('telegram.solve_queue_only_in_groups', {}, locale, GROUP_ONLY_MESSAGE), {
|
|
70
70
|
reply_to_message_id: ctx.message.message_id,
|
|
71
71
|
fallbackLocale: locale,
|
|
@@ -75,13 +75,13 @@ export function registerSolveQueueCommand(bot, options) {
|
|
|
75
75
|
|
|
76
76
|
const authorize = isTopicAuthorized || (ctx => isChatAuthorized(ctx.chat.id));
|
|
77
77
|
if (!authorize(ctx)) {
|
|
78
|
-
VERBOSE && console.log('[VERBOSE] /
|
|
78
|
+
VERBOSE && console.log('[VERBOSE] /queue ignored: not authorized');
|
|
79
79
|
const errMsg = buildAuthErrorMessage ? buildAuthErrorMessage(ctx) : `❌ This chat (ID: ${ctx.chat.id}) is not authorized.`;
|
|
80
80
|
await replyWithFallback(errMsg, { reply_to_message_id: ctx.message.message_id, fallbackLocale: locale });
|
|
81
81
|
return;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
-
VERBOSE && console.log('[VERBOSE] /
|
|
84
|
+
VERBOSE && console.log('[VERBOSE] /queue passed all checks, generating status...');
|
|
85
85
|
|
|
86
86
|
const solveQueue = getSolveQueue({ verbose: VERBOSE });
|
|
87
87
|
|
|
@@ -97,12 +97,7 @@ export function registerSolveQueueCommand(bot, options) {
|
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
101
|
-
// Note: Telegram Bot API only supports underscores in command names, not hyphens.
|
|
102
|
-
// The entity-based matching handles /solve_queue, /solvequeue, and /queue.
|
|
103
|
-
// /solve-queue is handled by the text-based fallback in telegram-bot.mjs (issue #1232).
|
|
104
|
-
// The /queue alias was added in issue #1837 to make checking the queue faster to type.
|
|
105
|
-
bot.command(/^(?:solve[_-]?queue|queue)$/i, handleSolveQueueCommand);
|
|
100
|
+
bot.command(/^queue$/i, handleSolveQueueCommand);
|
|
106
101
|
|
|
107
102
|
return { handleSolveQueueCommand };
|
|
108
103
|
}
|
|
@@ -8,7 +8,7 @@ const execAsync = promisify(exec);
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Build a clickable, human-readable link to a queued issue/PR for the
|
|
11
|
-
* /
|
|
11
|
+
* /queue detailed status (issue #1837).
|
|
12
12
|
*
|
|
13
13
|
* For GitHub issue/PR URLs we render a compact `[owner/repo#number](url)`
|
|
14
14
|
* Markdown link so the list is scannable and clickable. When the label would
|
|
@@ -269,7 +269,7 @@ export async function getRunningSessionItems(verbose = false) {
|
|
|
269
269
|
return await impl(verbose);
|
|
270
270
|
} catch (error) {
|
|
271
271
|
if (verbose) {
|
|
272
|
-
console.error('[VERBOSE] /
|
|
272
|
+
console.error('[VERBOSE] /queue error getting running session items:', error.message);
|
|
273
273
|
}
|
|
274
274
|
return [];
|
|
275
275
|
}
|
|
@@ -300,9 +300,9 @@ export async function getRunningProcesses(processName, verbose = false) {
|
|
|
300
300
|
.filter(p => p.pid);
|
|
301
301
|
|
|
302
302
|
if (verbose) {
|
|
303
|
-
console.log(`[VERBOSE] /
|
|
303
|
+
console.log(`[VERBOSE] /queue found ${processes.length} running ${processName} processes`);
|
|
304
304
|
if (processes.length > 0) {
|
|
305
|
-
console.log(`[VERBOSE] /
|
|
305
|
+
console.log(`[VERBOSE] /queue processes: ${JSON.stringify(processes)}`);
|
|
306
306
|
}
|
|
307
307
|
}
|
|
308
308
|
|
|
@@ -312,7 +312,7 @@ export async function getRunningProcesses(processName, verbose = false) {
|
|
|
312
312
|
};
|
|
313
313
|
} catch (error) {
|
|
314
314
|
if (verbose) {
|
|
315
|
-
console.error(`[VERBOSE] /
|
|
315
|
+
console.error(`[VERBOSE] /queue error counting ${processName} processes:`, error.message);
|
|
316
316
|
}
|
|
317
317
|
return { count: 0, processes: [] };
|
|
318
318
|
}
|
|
@@ -250,7 +250,7 @@ export class SolveQueue {
|
|
|
250
250
|
*/
|
|
251
251
|
log(message) {
|
|
252
252
|
if (this.verbose) {
|
|
253
|
-
console.log(`[VERBOSE] /
|
|
253
|
+
console.log(`[VERBOSE] /queue: ${message}`);
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
@@ -1469,7 +1469,7 @@ export async function getRunningIsolatedSessions(verbose = false) {
|
|
|
1469
1469
|
return await getRunningTrackedIsolationSessions(verbose);
|
|
1470
1470
|
} catch (error) {
|
|
1471
1471
|
if (verbose) {
|
|
1472
|
-
console.error(`[VERBOSE] /
|
|
1472
|
+
console.error(`[VERBOSE] /queue error getting isolated sessions:`, error.message);
|
|
1473
1473
|
}
|
|
1474
1474
|
return { count: 0, sessions: [], byTool: {} };
|
|
1475
1475
|
}
|