@grinev/opencode-telegram-bot 0.19.3 → 0.20.0
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/.env.example +5 -0
- package/README.md +5 -0
- package/dist/attach/service.js +0 -2
- package/dist/background-session/tracker.js +137 -0
- package/dist/bot/commands/definitions.js +2 -0
- package/dist/bot/commands/detach.js +54 -0
- package/dist/bot/commands/ls.js +445 -0
- package/dist/bot/commands/open.js +9 -4
- package/dist/bot/commands/projects.js +6 -2
- package/dist/bot/commands/sessions.js +233 -82
- package/dist/bot/commands/worktree.js +7 -2
- package/dist/bot/handlers/inline-menu.js +1 -0
- package/dist/bot/index.js +70 -8
- package/dist/bot/utils/send-downloaded-file.js +57 -0
- package/dist/bot/utils/switch-project.js +9 -14
- package/dist/config.js +1 -0
- package/dist/i18n/de.js +26 -0
- package/dist/i18n/en.js +26 -0
- package/dist/i18n/es.js +26 -0
- package/dist/i18n/fr.js +26 -0
- package/dist/i18n/ru.js +26 -0
- package/dist/i18n/zh.js +26 -0
- package/dist/interaction/busy.js +1 -1
- package/dist/interaction/manager.js +1 -1
- package/dist/scheduled-task/executor.js +3 -0
- package/dist/scheduled-task/runtime.js +2 -0
- package/dist/scheduled-task/schedule-parser.js +6 -0
- package/dist/scheduled-task/session-ignore.js +53 -0
- package/dist/settings/manager.js +12 -0
- package/package.json +1 -1
|
@@ -4,27 +4,19 @@ import { summaryAggregator } from "../../summary/aggregator.js";
|
|
|
4
4
|
import { pinnedMessageManager } from "../../pinned/manager.js";
|
|
5
5
|
import { keyboardManager } from "../../keyboard/manager.js";
|
|
6
6
|
import { detachAttachedSession } from "../../attach/service.js";
|
|
7
|
+
import { stopEventListening } from "../../opencode/events.js";
|
|
8
|
+
import { backgroundSessionTracker } from "../../background-session/tracker.js";
|
|
7
9
|
import { getStoredAgent, resolveProjectAgent } from "../../agent/manager.js";
|
|
8
10
|
import { getStoredModel } from "../../model/manager.js";
|
|
9
11
|
import { formatVariantForButton } from "../../variant/manager.js";
|
|
10
12
|
import { clearAllInteractionState } from "../../interaction/cleanup.js";
|
|
11
13
|
import { createMainKeyboard } from "./keyboard.js";
|
|
12
14
|
import { logger } from "../../utils/logger.js";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
* Called by both `/projects` (selecting an existing project) and `/open`
|
|
17
|
-
* (browsing and adding a new directory). Performs the full state transition:
|
|
18
|
-
* persists the project, clears the session, resets the pinned message and
|
|
19
|
-
* keyboard, and returns a fresh reply keyboard for the caller to attach to
|
|
20
|
-
* its confirmation message.
|
|
21
|
-
*
|
|
22
|
-
* @param ctx grammY callback context (used for `ctx.chat` / `ctx.api`)
|
|
23
|
-
* @param project the project to switch to
|
|
24
|
-
* @param reason short tag for `clearAllInteractionState` (e.g. "project_switched")
|
|
25
|
-
*/
|
|
26
|
-
export async function switchToProject(ctx, project, reason) {
|
|
15
|
+
import { config } from "../../config.js";
|
|
16
|
+
export async function switchToProject(ctx, project, reason, options = {}) {
|
|
27
17
|
detachAttachedSession(reason);
|
|
18
|
+
stopEventListening();
|
|
19
|
+
backgroundSessionTracker.clear();
|
|
28
20
|
setCurrentProject(project);
|
|
29
21
|
clearSession();
|
|
30
22
|
summaryAggregator.clear();
|
|
@@ -46,5 +38,8 @@ export async function switchToProject(ctx, project, reason) {
|
|
|
46
38
|
const contextInfo = { tokensUsed: 0, tokensLimit: contextLimit };
|
|
47
39
|
const variantName = formatVariantForButton(currentModel.variant || "default");
|
|
48
40
|
keyboardManager.updateAgent(currentAgent);
|
|
41
|
+
if (config.bot.trackBackgroundSessions && options.ensureEventSubscription) {
|
|
42
|
+
await options.ensureEventSubscription(project.worktree);
|
|
43
|
+
}
|
|
49
44
|
return createMainKeyboard(currentAgent, currentModel, contextInfo, variantName);
|
|
50
45
|
}
|
package/dist/config.js
CHANGED
|
@@ -94,6 +94,7 @@ export const config = {
|
|
|
94
94
|
hideThinkingMessages: getOptionalBooleanEnvVar("HIDE_THINKING_MESSAGES", false),
|
|
95
95
|
hideToolCallMessages: getOptionalBooleanEnvVar("HIDE_TOOL_CALL_MESSAGES", false),
|
|
96
96
|
hideToolFileMessages: getOptionalBooleanEnvVar("HIDE_TOOL_FILE_MESSAGES", false),
|
|
97
|
+
trackBackgroundSessions: getOptionalBooleanEnvVar("TRACK_BACKGROUND_SESSIONS", true),
|
|
97
98
|
messageFormatMode: getOptionalMessageFormatModeEnvVar("MESSAGE_FORMAT_MODE", "markdown"),
|
|
98
99
|
},
|
|
99
100
|
files: {
|
package/dist/i18n/de.js
CHANGED
|
@@ -2,6 +2,7 @@ export const de = {
|
|
|
2
2
|
"cmd.description.status": "Server- und Sitzungsstatus",
|
|
3
3
|
"cmd.description.new": "Neue Sitzung erstellen",
|
|
4
4
|
"cmd.description.stop": "Aktuelle Aktion stoppen",
|
|
5
|
+
"cmd.description.detach": "Von aktueller Sitzung trennen",
|
|
5
6
|
"cmd.description.sessions": "Sitzungen auflisten",
|
|
6
7
|
"cmd.description.tts": "Audioantworten umschalten",
|
|
7
8
|
"cmd.description.projects": "Projekte auflisten",
|
|
@@ -13,6 +14,7 @@ export const de = {
|
|
|
13
14
|
"cmd.description.mcps": "MCP servers",
|
|
14
15
|
"cmd.description.opencode_start": "OpenCode-Server starten",
|
|
15
16
|
"cmd.description.opencode_stop": "OpenCode-Server stoppen",
|
|
17
|
+
"cmd.description.ls": "Verzeichnisinhalt auflisten",
|
|
16
18
|
"cmd.description.help": "Hilfe",
|
|
17
19
|
"callback.unknown_command": "Unbekannter Befehl",
|
|
18
20
|
"callback.processing_error": "Verarbeitungsfehler",
|
|
@@ -50,6 +52,11 @@ export const de = {
|
|
|
50
52
|
"bot.session_error": "🔴 OpenCode meldete einen Fehler: {message}",
|
|
51
53
|
"bot.session_retry": "🔁 {message}\n\nDer Provider liefert bei wiederholten Versuchen immer wieder denselben Fehler. Mit /abort abbrechen.",
|
|
52
54
|
"bot.external_user_input": "Externe Benutzereingabe",
|
|
55
|
+
"background.session_fallback": "Sitzung {id}",
|
|
56
|
+
"background.assistant_response": "🔔 Assistent hat in einer Hintergrundsitzung geantwortet: {session}",
|
|
57
|
+
"background.question_asked": "❓ Hintergrundsitzung benötigt eine Antwort: {session}",
|
|
58
|
+
"background.permission_asked": "🔐 Hintergrundsitzung hat Berechtigungen angefordert: {session}",
|
|
59
|
+
"background.open_session_button": "Sitzung öffnen",
|
|
53
60
|
"bot.unknown_command": "⚠️ Unbekannter Befehl: {command}. Nutze /help, um verfügbare Befehle zu sehen.",
|
|
54
61
|
"bot.photo_downloading": "⏳ Lade Foto herunter...",
|
|
55
62
|
"bot.photo_too_large": "⚠️ Foto ist zu groß (max. {maxSizeMb}MB)",
|
|
@@ -126,6 +133,10 @@ export const de = {
|
|
|
126
133
|
"attach.restored_permissions": "Ausstehende Berechtigungsanfragen wiederhergestellt: {count}.",
|
|
127
134
|
"attach.disconnect_hint": "Zum Trennen einfach zu einer anderen Sitzung oder einem anderen Projekt wechseln.",
|
|
128
135
|
"attach.error": "🔴 Verbindung mit der aktuellen Sitzung fehlgeschlagen.",
|
|
136
|
+
"detach.project_not_selected": "🏗 Projekt ist nicht ausgewählt.\n\nWähle zuerst ein Projekt mit /projects.",
|
|
137
|
+
"detach.no_active_session": "ℹ️ Der Bot ist bereits von allen Sitzungen getrennt.",
|
|
138
|
+
"detach.success": "✅ Von Sitzung getrennt: {title}\n\nDie OpenCode-Sitzung wurde nicht gestoppt. Falls sie noch läuft, läuft sie separat weiter. Um sie später zu prüfen, wähle sie erneut über /sessions aus.",
|
|
139
|
+
"detach.error": "🔴 Trennen von der aktuellen Sitzung fehlgeschlagen.",
|
|
129
140
|
"new.project_not_selected": "🏗 Projekt ist nicht ausgewählt.\n\nWähle zuerst ein Projekt mit /projects.",
|
|
130
141
|
"new.created": "✅ Neue Sitzung erstellt: {title}",
|
|
131
142
|
"new.create_error": "🔴 OpenCode-Server ist nicht verfügbar oder beim Erstellen der Sitzung ist ein Fehler aufgetreten.",
|
|
@@ -350,6 +361,14 @@ export const de = {
|
|
|
350
361
|
"commands.button.next_page": "Weiter ➡️",
|
|
351
362
|
"commands.page_empty_callback": "Keine Befehle auf dieser Seite",
|
|
352
363
|
"commands.page_load_error_callback": "Diese Seite konnte nicht geladen werden. Bitte versuche es erneut.",
|
|
364
|
+
"commands.download.no_roots": "Es sind keine erlaubten Browser-Wurzeln konfiguriert.",
|
|
365
|
+
"commands.download.downloading": "Datei wird heruntergeladen...",
|
|
366
|
+
"commands.download.not_found": "Datei nicht gefunden",
|
|
367
|
+
"commands.download.not_file": "Pfad ist keine Datei",
|
|
368
|
+
"commands.download.file_too_large": "Datei ist zu groß",
|
|
369
|
+
"commands.download.size": "Größe",
|
|
370
|
+
"commands.download.modified": "Geändert",
|
|
371
|
+
"commands.download.error": "Datei konnte nicht heruntergeladen werden.",
|
|
353
372
|
"skills.select": "Wähle einen OpenCode-Skill:",
|
|
354
373
|
"skills.empty": "📭 Für dieses Projekt sind keine OpenCode-Skills verfügbar.",
|
|
355
374
|
"skills.fetch_error": "🔴 OpenCode-Skills konnten nicht geladen werden.",
|
|
@@ -424,4 +443,11 @@ export const de = {
|
|
|
424
443
|
"open.no_subfolders": "📭 Keine Unterordner",
|
|
425
444
|
"open.subfolder_count": "{count} Unterordner",
|
|
426
445
|
"open.subfolders_count": "{count} Unterordner",
|
|
446
|
+
"ls.access_denied": "⛔ Zugriff verweigert: Pfad liegt außerhalb des aktuellen Projekts",
|
|
447
|
+
"ls.scan_error": "🔴 Verzeichnis kann nicht aufgelistet werden",
|
|
448
|
+
"ls.header": "Verzeichnisinhalt",
|
|
449
|
+
"ls.total": "Gesamt: {count} Einträge",
|
|
450
|
+
"ls.file.header": "Dateidetails",
|
|
451
|
+
"ls.file.download": "📥 Herunterladen",
|
|
452
|
+
"ls.file.back": "⬅️ Zurück",
|
|
427
453
|
};
|
package/dist/i18n/en.js
CHANGED
|
@@ -2,6 +2,7 @@ export const en = {
|
|
|
2
2
|
"cmd.description.status": "Server and session status",
|
|
3
3
|
"cmd.description.new": "Create a new session",
|
|
4
4
|
"cmd.description.stop": "Stop current action",
|
|
5
|
+
"cmd.description.detach": "Detach from current session",
|
|
5
6
|
"cmd.description.sessions": "List sessions",
|
|
6
7
|
"cmd.description.tts": "Toggle audio replies",
|
|
7
8
|
"cmd.description.projects": "List projects",
|
|
@@ -13,6 +14,7 @@ export const en = {
|
|
|
13
14
|
"cmd.description.mcps": "MCP servers",
|
|
14
15
|
"cmd.description.opencode_start": "Start OpenCode server",
|
|
15
16
|
"cmd.description.opencode_stop": "Stop OpenCode server",
|
|
17
|
+
"cmd.description.ls": "List directory contents",
|
|
16
18
|
"cmd.description.help": "Help",
|
|
17
19
|
"callback.unknown_command": "Unknown command",
|
|
18
20
|
"callback.processing_error": "Processing error",
|
|
@@ -50,6 +52,11 @@ export const en = {
|
|
|
50
52
|
"bot.session_error": "🔴 OpenCode returned an error: {message}",
|
|
51
53
|
"bot.session_retry": "🔁 {message}\n\nProvider keeps returning the same error on repeated retries. Use /abort to abort.",
|
|
52
54
|
"bot.external_user_input": "External user input",
|
|
55
|
+
"background.session_fallback": "session {id}",
|
|
56
|
+
"background.assistant_response": "🔔 Assistant replied in background session: {session}",
|
|
57
|
+
"background.question_asked": "❓ Background session needs an answer: {session}",
|
|
58
|
+
"background.permission_asked": "🔐 Background session requested permissions: {session}",
|
|
59
|
+
"background.open_session_button": "Open session",
|
|
53
60
|
"bot.unknown_command": "⚠️ Unknown command: {command}. Use /help to see available commands.",
|
|
54
61
|
"bot.photo_downloading": "⏳ Downloading photo...",
|
|
55
62
|
"bot.photo_too_large": "⚠️ Photo is too large (max {maxSizeMb}MB)",
|
|
@@ -126,6 +133,10 @@ export const en = {
|
|
|
126
133
|
"attach.restored_permissions": "Recovered pending permission requests: {count}.",
|
|
127
134
|
"attach.disconnect_hint": "To disconnect, switch to another session or project.",
|
|
128
135
|
"attach.error": "🔴 Failed to attach to the current session.",
|
|
136
|
+
"detach.project_not_selected": "🏗 Project is not selected.\n\nFirst select a project with /projects.",
|
|
137
|
+
"detach.no_active_session": "ℹ️ Bot is already detached from any session.",
|
|
138
|
+
"detach.success": "✅ Detached from session: {title}\n\nThe OpenCode session was not stopped. If it is still running, it will continue separately. To check it later, select it again via /sessions.",
|
|
139
|
+
"detach.error": "🔴 Failed to detach from the current session.",
|
|
129
140
|
"new.project_not_selected": "🏗 Project is not selected.\n\nFirst select a project with /projects.",
|
|
130
141
|
"new.created": "✅ New session created: {title}",
|
|
131
142
|
"new.create_error": "🔴 OpenCode Server is unavailable or an error occurred while creating session.",
|
|
@@ -350,6 +361,14 @@ export const en = {
|
|
|
350
361
|
"commands.button.next_page": "Next ➡️",
|
|
351
362
|
"commands.page_empty_callback": "No commands on this page",
|
|
352
363
|
"commands.page_load_error_callback": "Cannot load this page. Please try again.",
|
|
364
|
+
"commands.download.no_roots": "No allowed browser roots are configured.",
|
|
365
|
+
"commands.download.downloading": "Downloading file...",
|
|
366
|
+
"commands.download.not_found": "File not found",
|
|
367
|
+
"commands.download.not_file": "Path is not a file",
|
|
368
|
+
"commands.download.file_too_large": "File is too large",
|
|
369
|
+
"commands.download.size": "Size",
|
|
370
|
+
"commands.download.modified": "Modified",
|
|
371
|
+
"commands.download.error": "Failed to download file.",
|
|
353
372
|
"skills.select": "Choose an OpenCode skill:",
|
|
354
373
|
"skills.empty": "📭 No OpenCode skills are available for this project.",
|
|
355
374
|
"skills.fetch_error": "🔴 Failed to load OpenCode skills.",
|
|
@@ -424,4 +443,11 @@ export const en = {
|
|
|
424
443
|
"open.no_subfolders": "📭 No subfolders",
|
|
425
444
|
"open.subfolder_count": "{count} subfolder",
|
|
426
445
|
"open.subfolders_count": "{count} subfolders",
|
|
446
|
+
"ls.access_denied": "⛔ Access denied: path is outside the current project",
|
|
447
|
+
"ls.scan_error": "🔴 Cannot list directory",
|
|
448
|
+
"ls.header": "Directory Listing",
|
|
449
|
+
"ls.total": "Total: {count} items",
|
|
450
|
+
"ls.file.header": "File Details",
|
|
451
|
+
"ls.file.download": "📥 Download",
|
|
452
|
+
"ls.file.back": "⬅️ Back",
|
|
427
453
|
};
|
package/dist/i18n/es.js
CHANGED
|
@@ -2,6 +2,7 @@ export const es = {
|
|
|
2
2
|
"cmd.description.status": "Estado del servidor y de la sesión",
|
|
3
3
|
"cmd.description.new": "Crear una sesión nueva",
|
|
4
4
|
"cmd.description.stop": "Detener la acción actual",
|
|
5
|
+
"cmd.description.detach": "Desconectar de la sesión actual",
|
|
5
6
|
"cmd.description.sessions": "Listar sesiones",
|
|
6
7
|
"cmd.description.tts": "Alternar respuestas de audio",
|
|
7
8
|
"cmd.description.projects": "Listar proyectos",
|
|
@@ -13,6 +14,7 @@ export const es = {
|
|
|
13
14
|
"cmd.description.mcps": "MCP servers",
|
|
14
15
|
"cmd.description.opencode_start": "Iniciar servidor OpenCode",
|
|
15
16
|
"cmd.description.opencode_stop": "Detener servidor OpenCode",
|
|
17
|
+
"cmd.description.ls": "Listar contenidos del directorio",
|
|
16
18
|
"cmd.description.help": "Ayuda",
|
|
17
19
|
"callback.unknown_command": "Comando desconocido",
|
|
18
20
|
"callback.processing_error": "Error de procesamiento",
|
|
@@ -50,6 +52,11 @@ export const es = {
|
|
|
50
52
|
"bot.session_error": "🔴 OpenCode devolvió un error: {message}",
|
|
51
53
|
"bot.session_retry": "🔁 {message}\n\nEl proveedor devuelve el mismo error en intentos repetidos. Usa /abort para detenerlo.",
|
|
52
54
|
"bot.external_user_input": "Entrada externa del usuario",
|
|
55
|
+
"background.session_fallback": "sesión {id}",
|
|
56
|
+
"background.assistant_response": "🔔 El asistente respondió en una sesión en segundo plano: {session}",
|
|
57
|
+
"background.question_asked": "❓ Una sesión en segundo plano necesita una respuesta: {session}",
|
|
58
|
+
"background.permission_asked": "🔐 Una sesión en segundo plano solicitó permisos: {session}",
|
|
59
|
+
"background.open_session_button": "Abrir sesión",
|
|
53
60
|
"bot.unknown_command": "⚠️ Comando desconocido: {command}. Usa /help para ver los comandos disponibles.",
|
|
54
61
|
"bot.photo_downloading": "⏳ Descargando foto...",
|
|
55
62
|
"bot.photo_too_large": "⚠️ La foto es demasiado grande (max {maxSizeMb}MB)",
|
|
@@ -126,6 +133,10 @@ export const es = {
|
|
|
126
133
|
"attach.restored_permissions": "Se restauraron solicitudes de permiso pendientes: {count}.",
|
|
127
134
|
"attach.disconnect_hint": "Para desconectarte, cambia a otra sesión o proyecto.",
|
|
128
135
|
"attach.error": "🔴 No se pudo conectar a la sesión actual.",
|
|
136
|
+
"detach.project_not_selected": "🏗 No hay un proyecto seleccionado.\n\nPrimero selecciona un proyecto con /projects.",
|
|
137
|
+
"detach.no_active_session": "ℹ️ El bot ya no está conectado a ninguna sesión.",
|
|
138
|
+
"detach.success": "✅ Desconectado de la sesión: {title}\n\nLa sesión de OpenCode no se detuvo. Si todavía está en ejecución, continuará por separado. Para revisarla más tarde, selecciónala de nuevo con /sessions.",
|
|
139
|
+
"detach.error": "🔴 No se pudo desconectar de la sesión actual.",
|
|
129
140
|
"new.project_not_selected": "🏗 No hay un proyecto seleccionado.\n\nPrimero selecciona un proyecto con /projects.",
|
|
130
141
|
"new.created": "✅ Sesión nueva creada: {title}",
|
|
131
142
|
"new.create_error": "🔴 OpenCode Server no está disponible u ocurrió un error al crear la sesión.",
|
|
@@ -350,6 +361,14 @@ export const es = {
|
|
|
350
361
|
"commands.button.next_page": "Siguiente ➡️",
|
|
351
362
|
"commands.page_empty_callback": "No hay comandos en esta página",
|
|
352
363
|
"commands.page_load_error_callback": "No se pudo cargar esta página. Por favor, inténtalo de nuevo.",
|
|
364
|
+
"commands.download.no_roots": "No hay raíces de navegación permitidas configuradas.",
|
|
365
|
+
"commands.download.downloading": "Descargando archivo...",
|
|
366
|
+
"commands.download.not_found": "Archivo no encontrado",
|
|
367
|
+
"commands.download.not_file": "La ruta no es un archivo",
|
|
368
|
+
"commands.download.file_too_large": "El archivo es demasiado grande",
|
|
369
|
+
"commands.download.size": "Tamaño",
|
|
370
|
+
"commands.download.modified": "Modificado",
|
|
371
|
+
"commands.download.error": "No se pudo descargar el archivo.",
|
|
353
372
|
"skills.select": "Elige un skill de OpenCode:",
|
|
354
373
|
"skills.empty": "📭 No hay skills de OpenCode disponibles para este proyecto.",
|
|
355
374
|
"skills.fetch_error": "🔴 No se pudieron cargar los skills de OpenCode.",
|
|
@@ -424,4 +443,11 @@ export const es = {
|
|
|
424
443
|
"open.no_subfolders": "📭 Sin subcarpetas",
|
|
425
444
|
"open.subfolder_count": "{count} subcarpeta",
|
|
426
445
|
"open.subfolders_count": "{count} subcarpetas",
|
|
446
|
+
"ls.access_denied": "⛔ Acceso denegado: la ruta está fuera del proyecto actual",
|
|
447
|
+
"ls.scan_error": "🔴 No se puede listar el directorio",
|
|
448
|
+
"ls.header": "Listado del directorio",
|
|
449
|
+
"ls.total": "Total: {count} elementos",
|
|
450
|
+
"ls.file.header": "Detalles del archivo",
|
|
451
|
+
"ls.file.download": "📥 Descargar",
|
|
452
|
+
"ls.file.back": "⬅️ Volver",
|
|
427
453
|
};
|
package/dist/i18n/fr.js
CHANGED
|
@@ -2,6 +2,7 @@ export const fr = {
|
|
|
2
2
|
"cmd.description.status": "Statut du serveur et de la session",
|
|
3
3
|
"cmd.description.new": "Créer une nouvelle session",
|
|
4
4
|
"cmd.description.stop": "Arrêter l'action en cours",
|
|
5
|
+
"cmd.description.detach": "Se détacher de la session actuelle",
|
|
5
6
|
"cmd.description.sessions": "Lister les sessions",
|
|
6
7
|
"cmd.description.tts": "Basculer les réponses audio",
|
|
7
8
|
"cmd.description.projects": "Lister les projets",
|
|
@@ -13,6 +14,7 @@ export const fr = {
|
|
|
13
14
|
"cmd.description.mcps": "MCP servers",
|
|
14
15
|
"cmd.description.opencode_start": "Démarrer le serveur OpenCode",
|
|
15
16
|
"cmd.description.opencode_stop": "Arrêter le serveur OpenCode",
|
|
17
|
+
"cmd.description.ls": "Lister le contenu du répertoire",
|
|
16
18
|
"cmd.description.help": "Aide",
|
|
17
19
|
"callback.unknown_command": "Commande inconnue",
|
|
18
20
|
"callback.processing_error": "Erreur de traitement",
|
|
@@ -50,6 +52,11 @@ export const fr = {
|
|
|
50
52
|
"bot.session_error": "🔴 OpenCode a renvoyé une erreur : {message}",
|
|
51
53
|
"bot.session_retry": "🔁 {message}\n\nLe fournisseur renvoie la même erreur à chaque nouvelle tentative. Utilisez /abort pour arrêter.",
|
|
52
54
|
"bot.external_user_input": "Entrée utilisateur externe",
|
|
55
|
+
"background.session_fallback": "session {id}",
|
|
56
|
+
"background.assistant_response": "🔔 L'assistant a répondu dans une session en arrière-plan : {session}",
|
|
57
|
+
"background.question_asked": "❓ Une session en arrière-plan attend une réponse : {session}",
|
|
58
|
+
"background.permission_asked": "🔐 Une session en arrière-plan a demandé des autorisations : {session}",
|
|
59
|
+
"background.open_session_button": "Ouvrir la session",
|
|
53
60
|
"bot.unknown_command": "⚠️ Commande inconnue : {command}. Utilisez /help pour voir les commandes disponibles.",
|
|
54
61
|
"bot.photo_downloading": "⏳ Téléchargement de la photo...",
|
|
55
62
|
"bot.photo_too_large": "⚠️ La photo est trop volumineuse (max {maxSizeMb}MB)",
|
|
@@ -126,6 +133,10 @@ export const fr = {
|
|
|
126
133
|
"attach.restored_permissions": "Demandes de permission en attente restaurées : {count}.",
|
|
127
134
|
"attach.disconnect_hint": "Pour vous déconnecter, passez simplement à une autre session ou à un autre projet.",
|
|
128
135
|
"attach.error": "🔴 Impossible de se connecter à la session actuelle.",
|
|
136
|
+
"detach.project_not_selected": "🏗 Aucun projet n'est sélectionné.\n\nSélectionnez d'abord un projet avec /projects.",
|
|
137
|
+
"detach.no_active_session": "ℹ️ Le bot n'est déjà attaché à aucune session.",
|
|
138
|
+
"detach.success": "✅ Détaché de la session : {title}\n\nLa session OpenCode n'a pas été arrêtée. Si elle est toujours en cours, elle continuera séparément. Pour la consulter plus tard, sélectionnez-la à nouveau via /sessions.",
|
|
139
|
+
"detach.error": "🔴 Impossible de se détacher de la session actuelle.",
|
|
129
140
|
"new.project_not_selected": "🏗 Aucun projet n'est sélectionné.\n\nSélectionnez d'abord un projet avec /projects.",
|
|
130
141
|
"new.created": "✅ Nouvelle session créée : {title}",
|
|
131
142
|
"new.create_error": "🔴 Le serveur OpenCode est indisponible ou une erreur s'est produite lors de la création de la session.",
|
|
@@ -350,6 +361,14 @@ export const fr = {
|
|
|
350
361
|
"commands.button.next_page": "Suivant ➡️",
|
|
351
362
|
"commands.page_empty_callback": "Aucune commande sur cette page",
|
|
352
363
|
"commands.page_load_error_callback": "Impossible de charger cette page. Veuillez réessayer.",
|
|
364
|
+
"commands.download.no_roots": "Aucune racine de navigation autorisée n'est configurée.",
|
|
365
|
+
"commands.download.downloading": "Téléchargement du fichier...",
|
|
366
|
+
"commands.download.not_found": "Fichier introuvable",
|
|
367
|
+
"commands.download.not_file": "Le chemin n'est pas un fichier",
|
|
368
|
+
"commands.download.file_too_large": "Le fichier est trop volumineux",
|
|
369
|
+
"commands.download.size": "Taille",
|
|
370
|
+
"commands.download.modified": "Modifié",
|
|
371
|
+
"commands.download.error": "Impossible de télécharger le fichier.",
|
|
353
372
|
"skills.select": "Choisissez un skill OpenCode :",
|
|
354
373
|
"skills.empty": "📭 Aucun skill OpenCode n'est disponible pour ce projet.",
|
|
355
374
|
"skills.fetch_error": "🔴 Impossible de charger les skills OpenCode.",
|
|
@@ -424,4 +443,11 @@ export const fr = {
|
|
|
424
443
|
"open.no_subfolders": "📭 Aucun sous-dossier",
|
|
425
444
|
"open.subfolder_count": "{count} sous-dossier",
|
|
426
445
|
"open.subfolders_count": "{count} sous-dossiers",
|
|
446
|
+
"ls.access_denied": "⛔ Accès refusé : le chemin est en dehors du projet actuel",
|
|
447
|
+
"ls.scan_error": "🔴 Impossible de lister le répertoire",
|
|
448
|
+
"ls.header": "Liste du répertoire",
|
|
449
|
+
"ls.total": "Total : {count} éléments",
|
|
450
|
+
"ls.file.header": "Détails du fichier",
|
|
451
|
+
"ls.file.download": "📥 Télécharger",
|
|
452
|
+
"ls.file.back": "⬅️ Retour",
|
|
427
453
|
};
|
package/dist/i18n/ru.js
CHANGED
|
@@ -2,6 +2,7 @@ export const ru = {
|
|
|
2
2
|
"cmd.description.status": "Статус сервера и сессии",
|
|
3
3
|
"cmd.description.new": "Создать новую сессию",
|
|
4
4
|
"cmd.description.stop": "Прервать текущее действие",
|
|
5
|
+
"cmd.description.detach": "Отсоединиться от текущей сессии",
|
|
5
6
|
"cmd.description.sessions": "Список сессий",
|
|
6
7
|
"cmd.description.tts": "Переключить аудиоответы",
|
|
7
8
|
"cmd.description.projects": "Список проектов",
|
|
@@ -13,6 +14,7 @@ export const ru = {
|
|
|
13
14
|
"cmd.description.mcps": "MCP серверы",
|
|
14
15
|
"cmd.description.opencode_start": "Запустить OpenCode сервер",
|
|
15
16
|
"cmd.description.opencode_stop": "Остановить OpenCode сервер",
|
|
17
|
+
"cmd.description.ls": "Список содержимого каталога",
|
|
16
18
|
"cmd.description.help": "Справка",
|
|
17
19
|
"callback.unknown_command": "Неизвестная команда",
|
|
18
20
|
"callback.processing_error": "Ошибка обработки",
|
|
@@ -50,6 +52,11 @@ export const ru = {
|
|
|
50
52
|
"bot.session_error": "🔴 OpenCode вернул ошибку: {message}",
|
|
51
53
|
"bot.session_retry": "🔁 {message}\n\nПровайдер возвращает одну и ту же ошибку при повторных запросах. Используйте /abort для остановки.",
|
|
52
54
|
"bot.external_user_input": "Внешний ввод пользователя",
|
|
55
|
+
"background.session_fallback": "сессия {id}",
|
|
56
|
+
"background.assistant_response": "🔔 В фоновой сессии пришёл ответ ассистента: {session}",
|
|
57
|
+
"background.question_asked": "❓ В фоновой сессии нужен ответ: {session}",
|
|
58
|
+
"background.permission_asked": "🔐 В фоновой сессии запрошены права: {session}",
|
|
59
|
+
"background.open_session_button": "Открыть сессию",
|
|
53
60
|
"bot.unknown_command": "⚠️ Неизвестная команда: {command}. Используйте /help для списка команд.",
|
|
54
61
|
"bot.photo_downloading": "⏳ Скачиваю фото...",
|
|
55
62
|
"bot.photo_too_large": "⚠️ Фото слишком большое (макс. {maxSizeMb}МБ)",
|
|
@@ -126,6 +133,10 @@ export const ru = {
|
|
|
126
133
|
"attach.restored_permissions": "Восстановил ожидающие запросы разрешений: {count}.",
|
|
127
134
|
"attach.disconnect_hint": "Чтобы отключиться, переключитесь на другую сессию или проект.",
|
|
128
135
|
"attach.error": "🔴 Не удалось подключиться к текущей сессии.",
|
|
136
|
+
"detach.project_not_selected": "🏗 Проект не выбран.\n\nСначала выберите проект командой /projects.",
|
|
137
|
+
"detach.no_active_session": "ℹ️ Бот уже не привязан ни к одной сессии.",
|
|
138
|
+
"detach.success": "✅ Отсоединился от сессии: {title}\n\nOpenCode-сессия не остановлена. Если она еще выполняется, выполнение продолжится отдельно. Чтобы проверить ее позже, снова выберите эту сессию через /sessions.",
|
|
139
|
+
"detach.error": "🔴 Не удалось отсоединиться от текущей сессии.",
|
|
129
140
|
"new.project_not_selected": "🏗 Проект не выбран.\n\nСначала выберите проект командой /projects.",
|
|
130
141
|
"new.created": "✅ Создана новая сессия: {title}",
|
|
131
142
|
"new.create_error": "🔴 OpenCode Server недоступен или произошла ошибка при создании сессии.",
|
|
@@ -350,6 +361,14 @@ export const ru = {
|
|
|
350
361
|
"commands.button.next_page": "Вперёд ➡️",
|
|
351
362
|
"commands.page_empty_callback": "На этой странице нет команд",
|
|
352
363
|
"commands.page_load_error_callback": "Не удалось загрузить эту страницу. Пожалуйста, попробуйте снова.",
|
|
364
|
+
"commands.download.no_roots": "Не настроены разрешённые корневые каталоги для просмотра.",
|
|
365
|
+
"commands.download.downloading": "Скачиваю файл...",
|
|
366
|
+
"commands.download.not_found": "Файл не найден",
|
|
367
|
+
"commands.download.not_file": "Путь не является файлом",
|
|
368
|
+
"commands.download.file_too_large": "Файл слишком большой",
|
|
369
|
+
"commands.download.size": "Размер",
|
|
370
|
+
"commands.download.modified": "Изменён",
|
|
371
|
+
"commands.download.error": "Не удалось скачать файл.",
|
|
353
372
|
"skills.select": "Выберите скилл OpenCode:",
|
|
354
373
|
"skills.empty": "📭 Для этого проекта нет доступных скиллов OpenCode.",
|
|
355
374
|
"skills.fetch_error": "🔴 Не удалось загрузить список скиллов OpenCode.",
|
|
@@ -424,4 +443,11 @@ export const ru = {
|
|
|
424
443
|
"open.no_subfolders": "📭 Нет подпапок",
|
|
425
444
|
"open.subfolder_count": "{count} подпапка",
|
|
426
445
|
"open.subfolders_count": "{count} подпапок",
|
|
446
|
+
"ls.access_denied": "⛔ Доступ запрещён: путь находится за пределами текущего проекта",
|
|
447
|
+
"ls.scan_error": "🔴 Не удается перечислить каталог",
|
|
448
|
+
"ls.header": "Список каталога",
|
|
449
|
+
"ls.total": "Всего: {count} элементов",
|
|
450
|
+
"ls.file.header": "Сведения о файле",
|
|
451
|
+
"ls.file.download": "📥 Скачать",
|
|
452
|
+
"ls.file.back": "⬅️ Назад",
|
|
427
453
|
};
|
package/dist/i18n/zh.js
CHANGED
|
@@ -2,6 +2,7 @@ export const zh = {
|
|
|
2
2
|
"cmd.description.status": "服务器和会话状态",
|
|
3
3
|
"cmd.description.new": "创建新会话",
|
|
4
4
|
"cmd.description.stop": "停止当前操作",
|
|
5
|
+
"cmd.description.detach": "从当前会话分离",
|
|
5
6
|
"cmd.description.sessions": "列出会话",
|
|
6
7
|
"cmd.description.tts": "切换语音回复",
|
|
7
8
|
"cmd.description.projects": "列出项目",
|
|
@@ -13,6 +14,7 @@ export const zh = {
|
|
|
13
14
|
"cmd.description.mcps": "MCP servers",
|
|
14
15
|
"cmd.description.opencode_start": "启动 OpenCode 服务器",
|
|
15
16
|
"cmd.description.opencode_stop": "停止 OpenCode 服务器",
|
|
17
|
+
"cmd.description.ls": "列出目录内容",
|
|
16
18
|
"cmd.description.help": "帮助",
|
|
17
19
|
"callback.unknown_command": "未知命令",
|
|
18
20
|
"callback.processing_error": "处理错误",
|
|
@@ -50,6 +52,11 @@ export const zh = {
|
|
|
50
52
|
"bot.session_error": "🔴 OpenCode 返回错误:{message}",
|
|
51
53
|
"bot.session_retry": "🔁 {message}\n\n提供方在重复重试时持续返回同一错误。使用 /abort 可停止。",
|
|
52
54
|
"bot.external_user_input": "外部用户输入",
|
|
55
|
+
"background.session_fallback": "会话 {id}",
|
|
56
|
+
"background.assistant_response": "🔔 助手已在后台会话中回复:{session}",
|
|
57
|
+
"background.question_asked": "❓ 后台会话需要回答:{session}",
|
|
58
|
+
"background.permission_asked": "🔐 后台会话请求了权限:{session}",
|
|
59
|
+
"background.open_session_button": "打开会话",
|
|
53
60
|
"bot.unknown_command": "⚠️ 未知命令:{command}。使用 /help 查看可用命令。",
|
|
54
61
|
"bot.photo_downloading": "⏳ 正在下载照片...",
|
|
55
62
|
"bot.photo_too_large": "⚠️ 照片过大(最大 {maxSizeMb}MB)",
|
|
@@ -126,6 +133,10 @@ export const zh = {
|
|
|
126
133
|
"attach.restored_permissions": "已恢复待处理权限请求:{count}。",
|
|
127
134
|
"attach.disconnect_hint": "如需断开连接,只需切换到其他会话或项目。",
|
|
128
135
|
"attach.error": "🔴 无法连接到当前会话。",
|
|
136
|
+
"detach.project_not_selected": "🏗 未选择项目。\n\n请先使用 /projects 选择一个项目。",
|
|
137
|
+
"detach.no_active_session": "ℹ️ 机器人当前未附加到任何会话。",
|
|
138
|
+
"detach.success": "✅ 已从会话分离:{title}\n\nOpenCode 会话未停止。如果它仍在运行,将继续独立执行。稍后可通过 /sessions 重新选择它来查看。",
|
|
139
|
+
"detach.error": "🔴 无法从当前会话分离。",
|
|
129
140
|
"new.project_not_selected": "🏗 未选择项目。\n\n请先使用 /projects 选择一个项目。",
|
|
130
141
|
"new.created": "✅ 新会话已创建:{title}",
|
|
131
142
|
"new.create_error": "🔴 OpenCode 服务器不可用,或创建会话时发生错误。",
|
|
@@ -350,6 +361,14 @@ export const zh = {
|
|
|
350
361
|
"commands.button.next_page": "下一页 ➡️",
|
|
351
362
|
"commands.page_empty_callback": "这一页没有命令",
|
|
352
363
|
"commands.page_load_error_callback": "无法加载此页面。请重试。",
|
|
364
|
+
"commands.download.no_roots": "未配置允许浏览的根目录。",
|
|
365
|
+
"commands.download.downloading": "正在下载文件...",
|
|
366
|
+
"commands.download.not_found": "未找到文件",
|
|
367
|
+
"commands.download.not_file": "该路径不是文件",
|
|
368
|
+
"commands.download.file_too_large": "文件过大",
|
|
369
|
+
"commands.download.size": "大小",
|
|
370
|
+
"commands.download.modified": "修改时间",
|
|
371
|
+
"commands.download.error": "下载文件失败。",
|
|
353
372
|
"skills.select": "请选择一个 OpenCode 技能:",
|
|
354
373
|
"skills.empty": "📭 当前项目没有可用的 OpenCode 技能。",
|
|
355
374
|
"skills.fetch_error": "🔴 加载 OpenCode 技能失败。",
|
|
@@ -424,4 +443,11 @@ export const zh = {
|
|
|
424
443
|
"open.no_subfolders": "📭 无子文件夹",
|
|
425
444
|
"open.subfolder_count": "{count} 个子文件夹",
|
|
426
445
|
"open.subfolders_count": "{count} 个子文件夹",
|
|
446
|
+
"ls.access_denied": "⛔ 访问被拒绝:路径在当前项目之外",
|
|
447
|
+
"ls.scan_error": "🔴 无法列出目录",
|
|
448
|
+
"ls.header": "目录列表",
|
|
449
|
+
"ls.total": "总计:{count} 个项目",
|
|
450
|
+
"ls.file.header": "文件详情",
|
|
451
|
+
"ls.file.download": "📥 下载",
|
|
452
|
+
"ls.file.back": "⬅️ 返回",
|
|
427
453
|
};
|
package/dist/interaction/busy.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const BUSY_ALLOWED_COMMANDS = ["/abort", "/status", "/help"];
|
|
1
|
+
export const BUSY_ALLOWED_COMMANDS = ["/abort", "/detach", "/status", "/help"];
|
|
2
2
|
const BUSY_ALLOWED_COMMAND_SET = new Set(BUSY_ALLOWED_COMMANDS);
|
|
3
3
|
export function isBusyAllowedCommand(command) {
|
|
4
4
|
return Boolean(command && BUSY_ALLOWED_COMMAND_SET.has(command));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logger } from "../utils/logger.js";
|
|
2
|
-
export const DEFAULT_ALLOWED_INTERACTION_COMMANDS = ["/help", "/status", "/abort"];
|
|
2
|
+
export const DEFAULT_ALLOWED_INTERACTION_COMMANDS = ["/help", "/status", "/abort", "/detach"];
|
|
3
3
|
function normalizeCommand(command) {
|
|
4
4
|
const trimmed = command.trim().toLowerCase();
|
|
5
5
|
if (!trimmed) {
|
|
@@ -2,6 +2,7 @@ import { config } from "../config.js";
|
|
|
2
2
|
import { t } from "../i18n/index.js";
|
|
3
3
|
import { opencodeClient } from "../opencode/client.js";
|
|
4
4
|
import { logger } from "../utils/logger.js";
|
|
5
|
+
import { cleanupScheduledTaskSessionIgnores, registerScheduledTaskSessionIgnore, } from "./session-ignore.js";
|
|
5
6
|
export const SCHEDULED_TASK_AGENT = "build";
|
|
6
7
|
const SCHEDULED_TASK_SESSION_TITLE = "Scheduled task run";
|
|
7
8
|
const EXECUTION_POLL_INTERVAL_MS = 2000;
|
|
@@ -298,6 +299,7 @@ export async function executeScheduledTask(task) {
|
|
|
298
299
|
let sessionId = null;
|
|
299
300
|
let deleteTemporarySession = true;
|
|
300
301
|
try {
|
|
302
|
+
await cleanupScheduledTaskSessionIgnores();
|
|
301
303
|
const { data: session, error: createError } = await opencodeClient.session.create({
|
|
302
304
|
directory: task.projectWorktree,
|
|
303
305
|
title: SCHEDULED_TASK_SESSION_TITLE,
|
|
@@ -306,6 +308,7 @@ export async function executeScheduledTask(task) {
|
|
|
306
308
|
throw createError || new Error("Failed to create temporary scheduled task session");
|
|
307
309
|
}
|
|
308
310
|
sessionId = session.id;
|
|
311
|
+
await registerScheduledTaskSessionIgnore(session.id);
|
|
309
312
|
const promptOptions = {
|
|
310
313
|
sessionID: session.id,
|
|
311
314
|
directory: session.directory,
|
|
@@ -7,6 +7,7 @@ import { sendBotText } from "../bot/utils/telegram-text.js";
|
|
|
7
7
|
import { formatAssistantRunFooter } from "../bot/utils/assistant-run-footer.js";
|
|
8
8
|
import { executeScheduledTask, SCHEDULED_TASK_AGENT } from "./executor.js";
|
|
9
9
|
import { foregroundSessionState } from "./foreground-state.js";
|
|
10
|
+
import { cleanupScheduledTaskSessionIgnores } from "./session-ignore.js";
|
|
10
11
|
import { computeNextRunAt, isTaskDue } from "./next-run.js";
|
|
11
12
|
import { getScheduledTask, listScheduledTasks, removeScheduledTask, replaceScheduledTasks, updateScheduledTask, } from "./store.js";
|
|
12
13
|
const MAX_TIMER_DELAY_MS = 2_147_483_647;
|
|
@@ -96,6 +97,7 @@ export class ScheduledTaskRuntime {
|
|
|
96
97
|
return;
|
|
97
98
|
}
|
|
98
99
|
this.initialized = true;
|
|
100
|
+
await cleanupScheduledTaskSessionIgnores();
|
|
99
101
|
await this.recoverTasksOnStartup();
|
|
100
102
|
await this.flushDeferredDeliveries();
|
|
101
103
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { opencodeClient } from "../opencode/client.js";
|
|
2
2
|
import { logger } from "../utils/logger.js";
|
|
3
|
+
import { cleanupScheduledTaskSessionIgnores, registerScheduledTaskSessionIgnore, } from "./session-ignore.js";
|
|
3
4
|
const SCHEDULE_PARSE_SESSION_TITLE = "Scheduled task schedule parser";
|
|
4
5
|
function getLocalTimezone() {
|
|
5
6
|
return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
|
|
@@ -133,6 +134,8 @@ export async function parseTaskSchedule(scheduleText, directory) {
|
|
|
133
134
|
const timezone = getLocalTimezone();
|
|
134
135
|
let sessionId = null;
|
|
135
136
|
try {
|
|
137
|
+
logger.debug(`[ScheduledTaskScheduleParser] Parsing schedule: directory=${trimmedDirectory}, textLength=${trimmedScheduleText.length}`);
|
|
138
|
+
await cleanupScheduledTaskSessionIgnores();
|
|
136
139
|
const { data: session, error: createError } = await opencodeClient.session.create({
|
|
137
140
|
directory: trimmedDirectory,
|
|
138
141
|
title: SCHEDULE_PARSE_SESSION_TITLE,
|
|
@@ -141,6 +144,8 @@ export async function parseTaskSchedule(scheduleText, directory) {
|
|
|
141
144
|
throw createError || new Error("Failed to create temporary schedule parser session");
|
|
142
145
|
}
|
|
143
146
|
sessionId = session.id;
|
|
147
|
+
await registerScheduledTaskSessionIgnore(session.id);
|
|
148
|
+
logger.debug(`[ScheduledTaskScheduleParser] Created temporary session: sessionId=${session.id}`);
|
|
144
149
|
const { data: response, error: promptError } = await opencodeClient.session.prompt({
|
|
145
150
|
sessionID: session.id,
|
|
146
151
|
directory: session.directory,
|
|
@@ -151,6 +156,7 @@ export async function parseTaskSchedule(scheduleText, directory) {
|
|
|
151
156
|
throw promptError || new Error("Failed to parse schedule");
|
|
152
157
|
}
|
|
153
158
|
const responseText = collectResponseText(response.parts);
|
|
159
|
+
logger.debug(`[ScheduledTaskScheduleParser] Received parser response: sessionId=${session.id}, textLength=${responseText.length}`);
|
|
154
160
|
if (!responseText) {
|
|
155
161
|
throw new Error("Schedule parser returned an empty response");
|
|
156
162
|
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { getScheduledTaskSessionIgnores, setScheduledTaskSessionIgnores, } from "../settings/manager.js";
|
|
2
|
+
const SCHEDULED_TASK_SESSION_IGNORE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
3
|
+
let mutationQueue = Promise.resolve();
|
|
4
|
+
function isFreshIgnore(ignore, nowMs) {
|
|
5
|
+
const createdAtMs = Date.parse(ignore.createdAt);
|
|
6
|
+
return !Number.isNaN(createdAtMs) && nowMs - createdAtMs < SCHEDULED_TASK_SESSION_IGNORE_TTL_MS;
|
|
7
|
+
}
|
|
8
|
+
function pruneExpiredIgnores(ignores, nowMs) {
|
|
9
|
+
return ignores.filter((ignore) => isFreshIgnore(ignore, nowMs));
|
|
10
|
+
}
|
|
11
|
+
async function mutateIgnores(mutator) {
|
|
12
|
+
const runMutation = async () => {
|
|
13
|
+
const currentIgnores = getScheduledTaskSessionIgnores();
|
|
14
|
+
const { ignores, result } = mutator(currentIgnores);
|
|
15
|
+
await setScheduledTaskSessionIgnores(ignores);
|
|
16
|
+
return result;
|
|
17
|
+
};
|
|
18
|
+
const mutationPromise = mutationQueue.then(runMutation, runMutation);
|
|
19
|
+
mutationQueue = mutationPromise.catch(() => undefined);
|
|
20
|
+
return mutationPromise;
|
|
21
|
+
}
|
|
22
|
+
export function isScheduledTaskSessionIgnored(sessionId, now = new Date()) {
|
|
23
|
+
const nowMs = now.getTime();
|
|
24
|
+
return getScheduledTaskSessionIgnores().some((ignore) => ignore.sessionId === sessionId && isFreshIgnore(ignore, nowMs));
|
|
25
|
+
}
|
|
26
|
+
export async function registerScheduledTaskSessionIgnore(sessionId, createdAt = new Date()) {
|
|
27
|
+
await mutateIgnores((ignores) => {
|
|
28
|
+
const nowMs = createdAt.getTime();
|
|
29
|
+
const nextIgnores = pruneExpiredIgnores(ignores, nowMs).filter((ignore) => ignore.sessionId !== sessionId);
|
|
30
|
+
return {
|
|
31
|
+
ignores: [...nextIgnores, { sessionId, createdAt: createdAt.toISOString() }],
|
|
32
|
+
result: undefined,
|
|
33
|
+
};
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export async function removeScheduledTaskSessionIgnore(sessionId) {
|
|
37
|
+
await mutateIgnores((ignores) => ({
|
|
38
|
+
ignores: ignores.filter((ignore) => ignore.sessionId !== sessionId),
|
|
39
|
+
result: undefined,
|
|
40
|
+
}));
|
|
41
|
+
}
|
|
42
|
+
export async function cleanupScheduledTaskSessionIgnores(now = new Date()) {
|
|
43
|
+
return mutateIgnores((ignores) => {
|
|
44
|
+
const nextIgnores = pruneExpiredIgnores(ignores, now.getTime());
|
|
45
|
+
return {
|
|
46
|
+
ignores: nextIgnores,
|
|
47
|
+
result: ignores.length - nextIgnores.length,
|
|
48
|
+
};
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function __resetScheduledTaskSessionIgnoreForTests() {
|
|
52
|
+
mutationQueue = Promise.resolve();
|
|
53
|
+
}
|
package/dist/settings/manager.js
CHANGED
|
@@ -5,6 +5,9 @@ import { logger } from "../utils/logger.js";
|
|
|
5
5
|
function cloneScheduledTasks(tasks) {
|
|
6
6
|
return tasks?.map((task) => cloneScheduledTask(task));
|
|
7
7
|
}
|
|
8
|
+
function cloneScheduledTaskSessionIgnores(ignores) {
|
|
9
|
+
return ignores?.map((ignore) => ({ ...ignore }));
|
|
10
|
+
}
|
|
8
11
|
function getSettingsFilePath() {
|
|
9
12
|
return getRuntimePaths().settingsFilePath;
|
|
10
13
|
}
|
|
@@ -121,6 +124,13 @@ export function setScheduledTasks(tasks) {
|
|
|
121
124
|
currentSettings.scheduledTasks = cloneScheduledTasks(tasks);
|
|
122
125
|
return writeSettingsFile(currentSettings);
|
|
123
126
|
}
|
|
127
|
+
export function getScheduledTaskSessionIgnores() {
|
|
128
|
+
return cloneScheduledTaskSessionIgnores(currentSettings.scheduledTaskSessionIgnores) ?? [];
|
|
129
|
+
}
|
|
130
|
+
export function setScheduledTaskSessionIgnores(ignores) {
|
|
131
|
+
currentSettings.scheduledTaskSessionIgnores = cloneScheduledTaskSessionIgnores(ignores);
|
|
132
|
+
return writeSettingsFile(currentSettings);
|
|
133
|
+
}
|
|
124
134
|
export function __resetSettingsForTests() {
|
|
125
135
|
currentSettings = {};
|
|
126
136
|
settingsWriteQueue = Promise.resolve();
|
|
@@ -138,6 +148,8 @@ export async function loadSettings() {
|
|
|
138
148
|
}
|
|
139
149
|
currentSettings = loadedSettings;
|
|
140
150
|
currentSettings.scheduledTasks = cloneScheduledTasks(loadedSettings.scheduledTasks) ?? [];
|
|
151
|
+
currentSettings.scheduledTaskSessionIgnores =
|
|
152
|
+
cloneScheduledTaskSessionIgnores(loadedSettings.scheduledTaskSessionIgnores) ?? [];
|
|
141
153
|
if (requiresRewrite) {
|
|
142
154
|
void writeSettingsFile(currentSettings);
|
|
143
155
|
}
|