codex-autorunner 0.1.2__py3-none-any.whl → 1.0.0__py3-none-any.whl
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.
- codex_autorunner/__main__.py +4 -0
- codex_autorunner/agents/opencode/client.py +68 -35
- codex_autorunner/agents/opencode/logging.py +21 -5
- codex_autorunner/agents/opencode/run_prompt.py +1 -0
- codex_autorunner/agents/opencode/runtime.py +118 -30
- codex_autorunner/agents/opencode/supervisor.py +36 -48
- codex_autorunner/agents/registry.py +136 -8
- codex_autorunner/api.py +25 -0
- codex_autorunner/bootstrap.py +16 -35
- codex_autorunner/cli.py +157 -139
- codex_autorunner/core/about_car.py +44 -32
- codex_autorunner/core/adapter_utils.py +21 -0
- codex_autorunner/core/app_server_logging.py +7 -3
- codex_autorunner/core/app_server_prompts.py +27 -260
- codex_autorunner/core/app_server_threads.py +15 -26
- codex_autorunner/core/codex_runner.py +6 -0
- codex_autorunner/core/config.py +390 -100
- codex_autorunner/core/docs.py +10 -2
- codex_autorunner/core/drafts.py +82 -0
- codex_autorunner/core/engine.py +278 -262
- codex_autorunner/core/flows/__init__.py +25 -0
- codex_autorunner/core/flows/controller.py +178 -0
- codex_autorunner/core/flows/definition.py +82 -0
- codex_autorunner/core/flows/models.py +75 -0
- codex_autorunner/core/flows/runtime.py +351 -0
- codex_autorunner/core/flows/store.py +485 -0
- codex_autorunner/core/flows/transition.py +133 -0
- codex_autorunner/core/flows/worker_process.py +242 -0
- codex_autorunner/core/hub.py +15 -9
- codex_autorunner/core/locks.py +4 -0
- codex_autorunner/core/prompt.py +15 -7
- codex_autorunner/core/redaction.py +29 -0
- codex_autorunner/core/review_context.py +5 -8
- codex_autorunner/core/run_index.py +6 -0
- codex_autorunner/core/runner_process.py +5 -2
- codex_autorunner/core/state.py +0 -88
- codex_autorunner/core/static_assets.py +55 -0
- codex_autorunner/core/supervisor_utils.py +67 -0
- codex_autorunner/core/update.py +20 -11
- codex_autorunner/core/update_runner.py +2 -0
- codex_autorunner/core/utils.py +29 -2
- codex_autorunner/discovery.py +2 -4
- codex_autorunner/flows/ticket_flow/__init__.py +3 -0
- codex_autorunner/flows/ticket_flow/definition.py +91 -0
- codex_autorunner/integrations/agents/__init__.py +27 -0
- codex_autorunner/integrations/agents/agent_backend.py +142 -0
- codex_autorunner/integrations/agents/codex_backend.py +307 -0
- codex_autorunner/integrations/agents/opencode_backend.py +325 -0
- codex_autorunner/integrations/agents/run_event.py +71 -0
- codex_autorunner/integrations/app_server/client.py +576 -92
- codex_autorunner/integrations/app_server/supervisor.py +59 -33
- codex_autorunner/integrations/telegram/adapter.py +141 -167
- codex_autorunner/integrations/telegram/api_schemas.py +120 -0
- codex_autorunner/integrations/telegram/config.py +175 -0
- codex_autorunner/integrations/telegram/constants.py +16 -1
- codex_autorunner/integrations/telegram/dispatch.py +17 -0
- codex_autorunner/integrations/telegram/doctor.py +47 -0
- codex_autorunner/integrations/telegram/handlers/callbacks.py +0 -4
- codex_autorunner/integrations/telegram/handlers/commands/__init__.py +2 -0
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +53 -57
- codex_autorunner/integrations/telegram/handlers/commands/files.py +2 -6
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +227 -0
- codex_autorunner/integrations/telegram/handlers/commands/formatting.py +1 -1
- codex_autorunner/integrations/telegram/handlers/commands/github.py +41 -582
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +8 -8
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +133 -475
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +11 -4
- codex_autorunner/integrations/telegram/handlers/messages.py +120 -9
- codex_autorunner/integrations/telegram/helpers.py +88 -16
- codex_autorunner/integrations/telegram/outbox.py +208 -37
- codex_autorunner/integrations/telegram/progress_stream.py +3 -10
- codex_autorunner/integrations/telegram/service.py +214 -40
- codex_autorunner/integrations/telegram/state.py +100 -2
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +322 -0
- codex_autorunner/integrations/telegram/transport.py +36 -3
- codex_autorunner/integrations/telegram/trigger_mode.py +53 -0
- codex_autorunner/manifest.py +2 -0
- codex_autorunner/plugin_api.py +22 -0
- codex_autorunner/routes/__init__.py +23 -14
- codex_autorunner/routes/analytics.py +239 -0
- codex_autorunner/routes/base.py +81 -109
- codex_autorunner/routes/file_chat.py +836 -0
- codex_autorunner/routes/flows.py +980 -0
- codex_autorunner/routes/messages.py +459 -0
- codex_autorunner/routes/system.py +6 -1
- codex_autorunner/routes/usage.py +87 -0
- codex_autorunner/routes/workspace.py +271 -0
- codex_autorunner/server.py +2 -1
- codex_autorunner/static/agentControls.js +1 -0
- codex_autorunner/static/agentEvents.js +248 -0
- codex_autorunner/static/app.js +25 -22
- codex_autorunner/static/autoRefresh.js +29 -1
- codex_autorunner/static/bootstrap.js +1 -0
- codex_autorunner/static/bus.js +1 -0
- codex_autorunner/static/cache.js +1 -0
- codex_autorunner/static/constants.js +20 -4
- codex_autorunner/static/dashboard.js +162 -196
- codex_autorunner/static/diffRenderer.js +37 -0
- codex_autorunner/static/docChatCore.js +324 -0
- codex_autorunner/static/docChatStorage.js +65 -0
- codex_autorunner/static/docChatVoice.js +65 -0
- codex_autorunner/static/docEditor.js +133 -0
- codex_autorunner/static/env.js +1 -0
- codex_autorunner/static/eventSummarizer.js +166 -0
- codex_autorunner/static/fileChat.js +182 -0
- codex_autorunner/static/health.js +155 -0
- codex_autorunner/static/hub.js +41 -118
- codex_autorunner/static/index.html +787 -858
- codex_autorunner/static/liveUpdates.js +1 -0
- codex_autorunner/static/loader.js +1 -0
- codex_autorunner/static/messages.js +470 -0
- codex_autorunner/static/mobileCompact.js +2 -1
- codex_autorunner/static/settings.js +24 -211
- codex_autorunner/static/styles.css +7567 -3865
- codex_autorunner/static/tabs.js +28 -5
- codex_autorunner/static/terminal.js +14 -0
- codex_autorunner/static/terminalManager.js +34 -59
- codex_autorunner/static/ticketChatActions.js +333 -0
- codex_autorunner/static/ticketChatEvents.js +16 -0
- codex_autorunner/static/ticketChatStorage.js +16 -0
- codex_autorunner/static/ticketChatStream.js +264 -0
- codex_autorunner/static/ticketEditor.js +750 -0
- codex_autorunner/static/ticketVoice.js +9 -0
- codex_autorunner/static/tickets.js +1315 -0
- codex_autorunner/static/utils.js +32 -3
- codex_autorunner/static/voice.js +1 -0
- codex_autorunner/static/workspace.js +672 -0
- codex_autorunner/static/workspaceApi.js +53 -0
- codex_autorunner/static/workspaceFileBrowser.js +504 -0
- codex_autorunner/tickets/__init__.py +20 -0
- codex_autorunner/tickets/agent_pool.py +377 -0
- codex_autorunner/tickets/files.py +85 -0
- codex_autorunner/tickets/frontmatter.py +55 -0
- codex_autorunner/tickets/lint.py +102 -0
- codex_autorunner/tickets/models.py +95 -0
- codex_autorunner/tickets/outbox.py +232 -0
- codex_autorunner/tickets/replies.py +179 -0
- codex_autorunner/tickets/runner.py +823 -0
- codex_autorunner/tickets/spec_ingest.py +77 -0
- codex_autorunner/web/app.py +269 -91
- codex_autorunner/web/middleware.py +3 -4
- codex_autorunner/web/schemas.py +89 -109
- codex_autorunner/web/static_assets.py +1 -44
- codex_autorunner/workspace/__init__.py +40 -0
- codex_autorunner/workspace/paths.py +319 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/METADATA +18 -21
- codex_autorunner-1.0.0.dist-info/RECORD +251 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/WHEEL +1 -1
- codex_autorunner/agents/execution/policy.py +0 -292
- codex_autorunner/agents/factory.py +0 -52
- codex_autorunner/agents/orchestrator.py +0 -358
- codex_autorunner/core/doc_chat.py +0 -1446
- codex_autorunner/core/snapshot.py +0 -580
- codex_autorunner/integrations/github/chatops.py +0 -268
- codex_autorunner/integrations/github/pr_flow.py +0 -1314
- codex_autorunner/routes/docs.py +0 -381
- codex_autorunner/routes/github.py +0 -327
- codex_autorunner/routes/runs.py +0 -250
- codex_autorunner/spec_ingest.py +0 -812
- codex_autorunner/static/docChatActions.js +0 -287
- codex_autorunner/static/docChatEvents.js +0 -300
- codex_autorunner/static/docChatRender.js +0 -205
- codex_autorunner/static/docChatStream.js +0 -361
- codex_autorunner/static/docs.js +0 -20
- codex_autorunner/static/docsClipboard.js +0 -69
- codex_autorunner/static/docsCrud.js +0 -257
- codex_autorunner/static/docsDocUpdates.js +0 -62
- codex_autorunner/static/docsDrafts.js +0 -16
- codex_autorunner/static/docsElements.js +0 -69
- codex_autorunner/static/docsInit.js +0 -285
- codex_autorunner/static/docsParse.js +0 -160
- codex_autorunner/static/docsSnapshot.js +0 -87
- codex_autorunner/static/docsSpecIngest.js +0 -263
- codex_autorunner/static/docsState.js +0 -127
- codex_autorunner/static/docsThreadRegistry.js +0 -44
- codex_autorunner/static/docsUi.js +0 -153
- codex_autorunner/static/docsVoice.js +0 -56
- codex_autorunner/static/github.js +0 -504
- codex_autorunner/static/logs.js +0 -678
- codex_autorunner/static/review.js +0 -157
- codex_autorunner/static/runs.js +0 -418
- codex_autorunner/static/snapshot.js +0 -124
- codex_autorunner/static/state.js +0 -94
- codex_autorunner/static/todoPreview.js +0 -27
- codex_autorunner/workspace.py +0 -16
- codex_autorunner-0.1.2.dist-info/RECORD +0 -222
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-0.1.2.dist-info → codex_autorunner-1.0.0.dist-info}/top_level.txt +0 -0
|
@@ -21,12 +21,10 @@ from ....core.logging_utils import log_event
|
|
|
21
21
|
from ....core.state import now_iso
|
|
22
22
|
from ....core.update import _normalize_update_target, _spawn_update_process
|
|
23
23
|
from ....core.utils import canonicalize_path
|
|
24
|
-
from ....integrations.github.service import GitHubError, GitHubService
|
|
25
24
|
from ...app_server.client import _normalize_sandbox_policy
|
|
26
25
|
from ..adapter import (
|
|
27
26
|
CompactCallback,
|
|
28
27
|
InlineButton,
|
|
29
|
-
PrFlowStartCallback,
|
|
30
28
|
TelegramCallbackQuery,
|
|
31
29
|
TelegramCommand,
|
|
32
30
|
TelegramMessage,
|
|
@@ -56,6 +54,7 @@ from ..constants import (
|
|
|
56
54
|
TurnKey,
|
|
57
55
|
)
|
|
58
56
|
from ..helpers import (
|
|
57
|
+
CodexFeatureRow,
|
|
59
58
|
_coerce_model_options,
|
|
60
59
|
_compact_preview,
|
|
61
60
|
_extract_command_result,
|
|
@@ -79,6 +78,9 @@ from ..helpers import (
|
|
|
79
78
|
_set_rollout_path,
|
|
80
79
|
_thread_summary_preview,
|
|
81
80
|
_with_conversation_id,
|
|
81
|
+
derive_codex_features_command,
|
|
82
|
+
format_codex_features,
|
|
83
|
+
parse_codex_features_list,
|
|
82
84
|
)
|
|
83
85
|
from ..state import (
|
|
84
86
|
parse_topic_key,
|
|
@@ -98,6 +100,7 @@ from .commands import (
|
|
|
98
100
|
ApprovalsCommands,
|
|
99
101
|
ExecutionCommands,
|
|
100
102
|
FilesCommands,
|
|
103
|
+
FlowCommands,
|
|
101
104
|
FormattingHelpers,
|
|
102
105
|
GitHubCommands,
|
|
103
106
|
VoiceCommands,
|
|
@@ -113,20 +116,7 @@ OUTBOX_CONTEXT_RE = re.compile(
|
|
|
113
116
|
"(?:\\b(?:pdf|png|jpg|jpeg|gif|webp|svg|csv|tsv|json|yaml|yml|zip|tar|gz|tgz|xlsx|xls|docx|pptx|md|txt|log|html|xml)\\b|\\.(?:pdf|png|jpg|jpeg|gif|webp|svg|csv|tsv|json|yaml|yml|zip|tar|gz|tgz|xlsx|xls|docx|pptx|md|txt|log|html|xml)\\b|\\b(?:outbox)\\b)",
|
|
114
117
|
re.IGNORECASE,
|
|
115
118
|
)
|
|
116
|
-
|
|
117
|
-
"car",
|
|
118
|
-
"codex",
|
|
119
|
-
"todo",
|
|
120
|
-
"progress",
|
|
121
|
-
"opinions",
|
|
122
|
-
"spec",
|
|
123
|
-
"summary",
|
|
124
|
-
"autorunner",
|
|
125
|
-
"work docs",
|
|
126
|
-
)
|
|
127
|
-
CAR_CONTEXT_HINT = (
|
|
128
|
-
"Context: read .codex-autorunner/ABOUT_CAR.md for repo-specific rules."
|
|
129
|
-
)
|
|
119
|
+
|
|
130
120
|
FILES_HINT_TEMPLATE = """Inbox: {inbox}
|
|
131
121
|
Outbox (pending): {outbox}
|
|
132
122
|
Topic key: {topic_key}
|
|
@@ -297,7 +287,7 @@ def _iter_exception_chain(exc: BaseException) -> list[BaseException]:
|
|
|
297
287
|
def _sanitize_error_detail(detail: str, *, limit: int = 200) -> str:
|
|
298
288
|
cleaned = " ".join(detail.split())
|
|
299
289
|
if len(cleaned) > limit:
|
|
300
|
-
return f"{cleaned[:limit - 3]}..."
|
|
290
|
+
return f"{cleaned[: limit - 3]}..."
|
|
301
291
|
return cleaned
|
|
302
292
|
|
|
303
293
|
|
|
@@ -376,13 +366,13 @@ def _format_media_batch_failure(
|
|
|
376
366
|
class TelegramCommandHandlers(
|
|
377
367
|
WorkspaceCommands,
|
|
378
368
|
GitHubCommands,
|
|
369
|
+
FlowCommands,
|
|
379
370
|
FilesCommands,
|
|
380
371
|
VoiceCommands,
|
|
381
372
|
ExecutionCommands,
|
|
382
373
|
ApprovalsCommands,
|
|
383
374
|
FormattingHelpers,
|
|
384
375
|
):
|
|
385
|
-
|
|
386
376
|
async def _handle_help(
|
|
387
377
|
self, message: TelegramMessage, _args: str, _runtime: Any
|
|
388
378
|
) -> None:
|
|
@@ -1182,451 +1172,6 @@ class TelegramCommandHandlers(
|
|
|
1182
1172
|
reply_to=message.message_id,
|
|
1183
1173
|
)
|
|
1184
1174
|
|
|
1185
|
-
async def _pr_flow_request(
|
|
1186
|
-
self,
|
|
1187
|
-
record: "TelegramTopicRecord",
|
|
1188
|
-
*,
|
|
1189
|
-
method: str,
|
|
1190
|
-
path: str,
|
|
1191
|
-
payload: Optional[dict[str, Any]] = None,
|
|
1192
|
-
) -> dict[str, Any]:
|
|
1193
|
-
base, headers = self._pr_flow_api_base(record)
|
|
1194
|
-
if not base:
|
|
1195
|
-
raise RuntimeError(
|
|
1196
|
-
"PR flow cannot start: repo server base URL could not be resolved for this chat/topic."
|
|
1197
|
-
)
|
|
1198
|
-
url = f"{base}{path}"
|
|
1199
|
-
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
1200
|
-
res = await client.request(method, url, json=payload, headers=headers)
|
|
1201
|
-
res.raise_for_status()
|
|
1202
|
-
data = res.json()
|
|
1203
|
-
if isinstance(data, dict):
|
|
1204
|
-
return data
|
|
1205
|
-
return {"status": "ok", "flow": data}
|
|
1206
|
-
|
|
1207
|
-
def _parse_pr_flags(self, argv: list[str]) -> tuple[Optional[str], dict[str, Any]]:
|
|
1208
|
-
ref: Optional[str] = None
|
|
1209
|
-
flags: dict[str, Any] = {}
|
|
1210
|
-
idx = 0
|
|
1211
|
-
while idx < len(argv):
|
|
1212
|
-
token = argv[idx]
|
|
1213
|
-
if token.startswith("--"):
|
|
1214
|
-
if token == "--draft":
|
|
1215
|
-
flags["draft"] = True
|
|
1216
|
-
idx += 1
|
|
1217
|
-
continue
|
|
1218
|
-
if token == "--ready":
|
|
1219
|
-
flags["draft"] = False
|
|
1220
|
-
idx += 1
|
|
1221
|
-
continue
|
|
1222
|
-
if token == "--base" and idx + 1 < len(argv):
|
|
1223
|
-
flags["base_branch"] = argv[idx + 1]
|
|
1224
|
-
idx += 2
|
|
1225
|
-
continue
|
|
1226
|
-
if token == "--until" and idx + 1 < len(argv):
|
|
1227
|
-
until = argv[idx + 1].strip().lower()
|
|
1228
|
-
if until in ("minor", "minor_only"):
|
|
1229
|
-
flags["stop_condition"] = "minor_only"
|
|
1230
|
-
elif until in ("clean", "no_issues"):
|
|
1231
|
-
flags["stop_condition"] = "no_issues"
|
|
1232
|
-
idx += 2
|
|
1233
|
-
continue
|
|
1234
|
-
if token in ("--max-cycles", "--max_cycles") and idx + 1 < len(argv):
|
|
1235
|
-
try:
|
|
1236
|
-
flags["max_cycles"] = int(argv[idx + 1])
|
|
1237
|
-
except ValueError:
|
|
1238
|
-
pass
|
|
1239
|
-
idx += 2
|
|
1240
|
-
continue
|
|
1241
|
-
if token in ("--max-runs", "--max_runs") and idx + 1 < len(argv):
|
|
1242
|
-
try:
|
|
1243
|
-
flags["max_implementation_runs"] = int(argv[idx + 1])
|
|
1244
|
-
except ValueError:
|
|
1245
|
-
pass
|
|
1246
|
-
idx += 2
|
|
1247
|
-
continue
|
|
1248
|
-
if token in ("--timeout", "--timeout-seconds") and idx + 1 < len(argv):
|
|
1249
|
-
try:
|
|
1250
|
-
flags["max_wallclock_seconds"] = int(argv[idx + 1])
|
|
1251
|
-
except ValueError:
|
|
1252
|
-
pass
|
|
1253
|
-
idx += 2
|
|
1254
|
-
continue
|
|
1255
|
-
idx += 1
|
|
1256
|
-
continue
|
|
1257
|
-
if ref is None:
|
|
1258
|
-
ref = token
|
|
1259
|
-
idx += 1
|
|
1260
|
-
return ref, flags
|
|
1261
|
-
|
|
1262
|
-
def _format_pr_flow_status(self, flow: dict[str, Any]) -> str:
|
|
1263
|
-
status = flow.get("status") or "unknown"
|
|
1264
|
-
step = flow.get("step") or "unknown"
|
|
1265
|
-
cycle = flow.get("cycle") or 0
|
|
1266
|
-
pr_url = flow.get("pr_url") or ""
|
|
1267
|
-
lines = [f"PR flow: {status} (step: {step}, cycle: {cycle})"]
|
|
1268
|
-
if pr_url:
|
|
1269
|
-
lines.append(f"PR: {pr_url}")
|
|
1270
|
-
return "\n".join(lines)
|
|
1271
|
-
|
|
1272
|
-
async def _handle_github_issue_url(
|
|
1273
|
-
self, message: TelegramMessage, key: str, slug: str, number: int
|
|
1274
|
-
) -> None:
|
|
1275
|
-
if key is None:
|
|
1276
|
-
return
|
|
1277
|
-
|
|
1278
|
-
record = await self._router.get_topic(key)
|
|
1279
|
-
if record is None or not record.workspace_path:
|
|
1280
|
-
await self._send_message(
|
|
1281
|
-
message.chat_id,
|
|
1282
|
-
self._with_conversation_id(
|
|
1283
|
-
"Topic not bound. Use /bind <repo_id> or /bind <path>.",
|
|
1284
|
-
chat_id=message.chat_id,
|
|
1285
|
-
thread_id=message.thread_id,
|
|
1286
|
-
),
|
|
1287
|
-
thread_id=message.thread_id,
|
|
1288
|
-
reply_to=message.message_id,
|
|
1289
|
-
)
|
|
1290
|
-
return
|
|
1291
|
-
|
|
1292
|
-
try:
|
|
1293
|
-
from pathlib import Path
|
|
1294
|
-
|
|
1295
|
-
service = GitHubService(Path(record.workspace_path), self._raw_config)
|
|
1296
|
-
issue_ref = f"{slug}#{number}"
|
|
1297
|
-
service.validate_issue_same_repo(issue_ref)
|
|
1298
|
-
except GitHubError as exc:
|
|
1299
|
-
await self._send_message(
|
|
1300
|
-
message.chat_id,
|
|
1301
|
-
str(exc),
|
|
1302
|
-
thread_id=message.thread_id,
|
|
1303
|
-
reply_to=message.message_id,
|
|
1304
|
-
)
|
|
1305
|
-
return
|
|
1306
|
-
|
|
1307
|
-
await self._offer_pr_flow_start(message, record, slug, number)
|
|
1308
|
-
|
|
1309
|
-
async def _offer_pr_flow_start(
|
|
1310
|
-
self,
|
|
1311
|
-
message: TelegramMessage,
|
|
1312
|
-
record: "TelegramTopicRecord",
|
|
1313
|
-
slug: str,
|
|
1314
|
-
number: int,
|
|
1315
|
-
) -> None:
|
|
1316
|
-
from ..adapter import (
|
|
1317
|
-
InlineButton,
|
|
1318
|
-
build_inline_keyboard,
|
|
1319
|
-
encode_cancel_callback,
|
|
1320
|
-
encode_pr_flow_start_callback,
|
|
1321
|
-
)
|
|
1322
|
-
|
|
1323
|
-
keyboard = build_inline_keyboard(
|
|
1324
|
-
[
|
|
1325
|
-
[
|
|
1326
|
-
InlineButton(
|
|
1327
|
-
f"Create PR for #{number}",
|
|
1328
|
-
encode_pr_flow_start_callback(slug, number),
|
|
1329
|
-
),
|
|
1330
|
-
InlineButton(
|
|
1331
|
-
"Cancel",
|
|
1332
|
-
encode_cancel_callback("pr_flow_offer"),
|
|
1333
|
-
),
|
|
1334
|
-
]
|
|
1335
|
-
]
|
|
1336
|
-
)
|
|
1337
|
-
await self._send_message(
|
|
1338
|
-
message.chat_id,
|
|
1339
|
-
f"Detected GitHub issue: {slug}#{number}\nStart PR flow to create a PR?",
|
|
1340
|
-
thread_id=message.thread_id,
|
|
1341
|
-
reply_to=message.message_id,
|
|
1342
|
-
reply_markup=keyboard,
|
|
1343
|
-
)
|
|
1344
|
-
|
|
1345
|
-
async def _handle_pr_flow_start_callback(
|
|
1346
|
-
self,
|
|
1347
|
-
key: str,
|
|
1348
|
-
callback: TelegramCallbackQuery,
|
|
1349
|
-
parsed: PrFlowStartCallback,
|
|
1350
|
-
) -> None:
|
|
1351
|
-
from ..adapter import TelegramMessage
|
|
1352
|
-
|
|
1353
|
-
await self._answer_callback(callback)
|
|
1354
|
-
record = await self._router.get_topic(key)
|
|
1355
|
-
if record is None or not record.workspace_path:
|
|
1356
|
-
return
|
|
1357
|
-
|
|
1358
|
-
issue_ref = f"{parsed.slug}#{parsed.number}"
|
|
1359
|
-
payload = {"mode": "issue", "issue": issue_ref}
|
|
1360
|
-
payload["source"] = "telegram"
|
|
1361
|
-
source_meta: dict[str, Any] = {}
|
|
1362
|
-
if callback.chat_id is not None:
|
|
1363
|
-
source_meta["chat_id"] = callback.chat_id
|
|
1364
|
-
if callback.thread_id is not None:
|
|
1365
|
-
source_meta["thread_id"] = callback.thread_id
|
|
1366
|
-
if source_meta:
|
|
1367
|
-
payload["source_meta"] = source_meta
|
|
1368
|
-
|
|
1369
|
-
message = TelegramMessage(
|
|
1370
|
-
update_id=callback.update_id,
|
|
1371
|
-
message_id=callback.message_id or 0,
|
|
1372
|
-
chat_id=callback.chat_id or 0,
|
|
1373
|
-
thread_id=callback.thread_id,
|
|
1374
|
-
from_user_id=callback.from_user_id,
|
|
1375
|
-
text="",
|
|
1376
|
-
date=None,
|
|
1377
|
-
is_topic_message=False,
|
|
1378
|
-
)
|
|
1379
|
-
|
|
1380
|
-
try:
|
|
1381
|
-
data = await self._pr_flow_request(
|
|
1382
|
-
record,
|
|
1383
|
-
method="POST",
|
|
1384
|
-
path="/api/github/pr_flow/start",
|
|
1385
|
-
payload=payload,
|
|
1386
|
-
)
|
|
1387
|
-
flow = data.get("flow") if isinstance(data, dict) else data
|
|
1388
|
-
except Exception as exc:
|
|
1389
|
-
detail = _format_httpx_exception(exc) or str(exc)
|
|
1390
|
-
await self._send_message(
|
|
1391
|
-
message.chat_id,
|
|
1392
|
-
f"PR flow error: {detail}",
|
|
1393
|
-
thread_id=message.thread_id,
|
|
1394
|
-
reply_to=callback.message_id,
|
|
1395
|
-
)
|
|
1396
|
-
return
|
|
1397
|
-
await self._send_message(
|
|
1398
|
-
message.chat_id,
|
|
1399
|
-
self._format_pr_flow_status(flow),
|
|
1400
|
-
thread_id=message.thread_id,
|
|
1401
|
-
reply_to=callback.message_id,
|
|
1402
|
-
)
|
|
1403
|
-
|
|
1404
|
-
async def _handle_pr(
|
|
1405
|
-
self, message: TelegramMessage, args: str, runtime: Any
|
|
1406
|
-
) -> None:
|
|
1407
|
-
record = await self._require_bound_record(message)
|
|
1408
|
-
if not record:
|
|
1409
|
-
return
|
|
1410
|
-
argv = self._parse_command_args(args)
|
|
1411
|
-
if not argv:
|
|
1412
|
-
await self._send_message(
|
|
1413
|
-
message.chat_id,
|
|
1414
|
-
"Usage: /pr start <issueRef> | /pr fix <prRef> | /pr status | /pr stop | /pr resume | /pr collect",
|
|
1415
|
-
thread_id=message.thread_id,
|
|
1416
|
-
reply_to=message.message_id,
|
|
1417
|
-
)
|
|
1418
|
-
return
|
|
1419
|
-
command = argv[0].lower()
|
|
1420
|
-
if command == "status":
|
|
1421
|
-
try:
|
|
1422
|
-
data = await self._pr_flow_request(
|
|
1423
|
-
record, method="GET", path="/api/github/pr_flow/status"
|
|
1424
|
-
)
|
|
1425
|
-
flow = data.get("flow") if isinstance(data, dict) else data
|
|
1426
|
-
except Exception as exc:
|
|
1427
|
-
detail = _format_httpx_exception(exc) or str(exc)
|
|
1428
|
-
await self._send_message(
|
|
1429
|
-
message.chat_id,
|
|
1430
|
-
f"PR flow error: {detail}",
|
|
1431
|
-
thread_id=message.thread_id,
|
|
1432
|
-
reply_to=message.message_id,
|
|
1433
|
-
)
|
|
1434
|
-
return
|
|
1435
|
-
await self._send_message(
|
|
1436
|
-
message.chat_id,
|
|
1437
|
-
self._format_pr_flow_status(flow),
|
|
1438
|
-
thread_id=message.thread_id,
|
|
1439
|
-
reply_to=message.message_id,
|
|
1440
|
-
)
|
|
1441
|
-
return
|
|
1442
|
-
if command == "stop":
|
|
1443
|
-
try:
|
|
1444
|
-
data = await self._pr_flow_request(
|
|
1445
|
-
record, method="POST", path="/api/github/pr_flow/stop", payload={}
|
|
1446
|
-
)
|
|
1447
|
-
flow = data.get("flow") if isinstance(data, dict) else data
|
|
1448
|
-
except Exception as exc:
|
|
1449
|
-
detail = _format_httpx_exception(exc) or str(exc)
|
|
1450
|
-
await self._send_message(
|
|
1451
|
-
message.chat_id,
|
|
1452
|
-
f"PR flow error: {detail}",
|
|
1453
|
-
thread_id=message.thread_id,
|
|
1454
|
-
reply_to=message.message_id,
|
|
1455
|
-
)
|
|
1456
|
-
return
|
|
1457
|
-
await self._send_message(
|
|
1458
|
-
message.chat_id,
|
|
1459
|
-
self._format_pr_flow_status(flow),
|
|
1460
|
-
thread_id=message.thread_id,
|
|
1461
|
-
reply_to=message.message_id,
|
|
1462
|
-
)
|
|
1463
|
-
return
|
|
1464
|
-
if command == "resume":
|
|
1465
|
-
try:
|
|
1466
|
-
data = await self._pr_flow_request(
|
|
1467
|
-
record, method="POST", path="/api/github/pr_flow/resume", payload={}
|
|
1468
|
-
)
|
|
1469
|
-
flow = data.get("flow") if isinstance(data, dict) else data
|
|
1470
|
-
except Exception as exc:
|
|
1471
|
-
detail = _format_httpx_exception(exc) or str(exc)
|
|
1472
|
-
await self._send_message(
|
|
1473
|
-
message.chat_id,
|
|
1474
|
-
f"PR flow error: {detail}",
|
|
1475
|
-
thread_id=message.thread_id,
|
|
1476
|
-
reply_to=message.message_id,
|
|
1477
|
-
)
|
|
1478
|
-
return
|
|
1479
|
-
await self._send_message(
|
|
1480
|
-
message.chat_id,
|
|
1481
|
-
self._format_pr_flow_status(flow),
|
|
1482
|
-
thread_id=message.thread_id,
|
|
1483
|
-
reply_to=message.message_id,
|
|
1484
|
-
)
|
|
1485
|
-
return
|
|
1486
|
-
if command == "collect":
|
|
1487
|
-
try:
|
|
1488
|
-
data = await self._pr_flow_request(
|
|
1489
|
-
record,
|
|
1490
|
-
method="POST",
|
|
1491
|
-
path="/api/github/pr_flow/collect",
|
|
1492
|
-
payload={},
|
|
1493
|
-
)
|
|
1494
|
-
flow = data.get("flow") if isinstance(data, dict) else data
|
|
1495
|
-
except Exception as exc:
|
|
1496
|
-
detail = _format_httpx_exception(exc) or str(exc)
|
|
1497
|
-
await self._send_message(
|
|
1498
|
-
message.chat_id,
|
|
1499
|
-
f"PR flow error: {detail}",
|
|
1500
|
-
thread_id=message.thread_id,
|
|
1501
|
-
reply_to=message.message_id,
|
|
1502
|
-
)
|
|
1503
|
-
return
|
|
1504
|
-
await self._send_message(
|
|
1505
|
-
message.chat_id,
|
|
1506
|
-
self._format_pr_flow_status(flow),
|
|
1507
|
-
thread_id=message.thread_id,
|
|
1508
|
-
reply_to=message.message_id,
|
|
1509
|
-
)
|
|
1510
|
-
return
|
|
1511
|
-
if command in ("start", "implement"):
|
|
1512
|
-
ref, flags = self._parse_pr_flags(argv[1:])
|
|
1513
|
-
if not ref:
|
|
1514
|
-
gh = GitHubService(Path(record.workspace_path))
|
|
1515
|
-
issues = await asyncio.to_thread(gh.list_open_issues, limit=5)
|
|
1516
|
-
if issues:
|
|
1517
|
-
lines = ["Open issues:"]
|
|
1518
|
-
for issue in issues:
|
|
1519
|
-
num = issue.get("number")
|
|
1520
|
-
title = issue.get("title") or ""
|
|
1521
|
-
lines.append(f"- #{num} {title}".strip())
|
|
1522
|
-
lines.append("Use /pr start <issueRef> to begin.")
|
|
1523
|
-
await self._send_message(
|
|
1524
|
-
message.chat_id,
|
|
1525
|
-
"\n".join(lines),
|
|
1526
|
-
thread_id=message.thread_id,
|
|
1527
|
-
reply_to=message.message_id,
|
|
1528
|
-
)
|
|
1529
|
-
return
|
|
1530
|
-
await self._send_message(
|
|
1531
|
-
message.chat_id,
|
|
1532
|
-
"Usage: /pr start <issueRef>",
|
|
1533
|
-
thread_id=message.thread_id,
|
|
1534
|
-
reply_to=message.message_id,
|
|
1535
|
-
)
|
|
1536
|
-
return
|
|
1537
|
-
payload = {"mode": "issue", "issue": ref, **flags}
|
|
1538
|
-
payload["source"] = "telegram"
|
|
1539
|
-
payload["source_meta"] = {
|
|
1540
|
-
"chat_id": message.chat_id,
|
|
1541
|
-
"thread_id": message.thread_id,
|
|
1542
|
-
}
|
|
1543
|
-
try:
|
|
1544
|
-
data = await self._pr_flow_request(
|
|
1545
|
-
record,
|
|
1546
|
-
method="POST",
|
|
1547
|
-
path="/api/github/pr_flow/start",
|
|
1548
|
-
payload=payload,
|
|
1549
|
-
)
|
|
1550
|
-
flow = data.get("flow") if isinstance(data, dict) else data
|
|
1551
|
-
except Exception as exc:
|
|
1552
|
-
detail = _format_httpx_exception(exc) or str(exc)
|
|
1553
|
-
await self._send_message(
|
|
1554
|
-
message.chat_id,
|
|
1555
|
-
f"PR flow error: {detail}",
|
|
1556
|
-
thread_id=message.thread_id,
|
|
1557
|
-
reply_to=message.message_id,
|
|
1558
|
-
)
|
|
1559
|
-
return
|
|
1560
|
-
await self._send_message(
|
|
1561
|
-
message.chat_id,
|
|
1562
|
-
self._format_pr_flow_status(flow),
|
|
1563
|
-
thread_id=message.thread_id,
|
|
1564
|
-
reply_to=message.message_id,
|
|
1565
|
-
)
|
|
1566
|
-
return
|
|
1567
|
-
if command in ("fix", "pr"):
|
|
1568
|
-
ref, flags = self._parse_pr_flags(argv[1:])
|
|
1569
|
-
if not ref:
|
|
1570
|
-
gh = GitHubService(Path(record.workspace_path))
|
|
1571
|
-
prs = await asyncio.to_thread(gh.list_open_prs, limit=5)
|
|
1572
|
-
if prs:
|
|
1573
|
-
lines = ["Open PRs:"]
|
|
1574
|
-
for pr in prs:
|
|
1575
|
-
num = pr.get("number")
|
|
1576
|
-
title = pr.get("title") or ""
|
|
1577
|
-
lines.append(f"- #{num} {title}".strip())
|
|
1578
|
-
lines.append("Use /pr fix <prRef> to begin.")
|
|
1579
|
-
await self._send_message(
|
|
1580
|
-
message.chat_id,
|
|
1581
|
-
"\n".join(lines),
|
|
1582
|
-
thread_id=message.thread_id,
|
|
1583
|
-
reply_to=message.message_id,
|
|
1584
|
-
)
|
|
1585
|
-
return
|
|
1586
|
-
await self._send_message(
|
|
1587
|
-
message.chat_id,
|
|
1588
|
-
"Usage: /pr fix <prRef>",
|
|
1589
|
-
thread_id=message.thread_id,
|
|
1590
|
-
reply_to=message.message_id,
|
|
1591
|
-
)
|
|
1592
|
-
return
|
|
1593
|
-
payload = {"mode": "pr", "pr": ref, **flags}
|
|
1594
|
-
payload["source"] = "telegram"
|
|
1595
|
-
payload["source_meta"] = {
|
|
1596
|
-
"chat_id": message.chat_id,
|
|
1597
|
-
"thread_id": message.thread_id,
|
|
1598
|
-
}
|
|
1599
|
-
try:
|
|
1600
|
-
data = await self._pr_flow_request(
|
|
1601
|
-
record,
|
|
1602
|
-
method="POST",
|
|
1603
|
-
path="/api/github/pr_flow/start",
|
|
1604
|
-
payload=payload,
|
|
1605
|
-
)
|
|
1606
|
-
flow = data.get("flow") if isinstance(data, dict) else data
|
|
1607
|
-
except Exception as exc:
|
|
1608
|
-
detail = _format_httpx_exception(exc) or str(exc)
|
|
1609
|
-
await self._send_message(
|
|
1610
|
-
message.chat_id,
|
|
1611
|
-
f"PR flow error: {detail}",
|
|
1612
|
-
thread_id=message.thread_id,
|
|
1613
|
-
reply_to=message.message_id,
|
|
1614
|
-
)
|
|
1615
|
-
return
|
|
1616
|
-
await self._send_message(
|
|
1617
|
-
message.chat_id,
|
|
1618
|
-
self._format_pr_flow_status(flow),
|
|
1619
|
-
thread_id=message.thread_id,
|
|
1620
|
-
reply_to=message.message_id,
|
|
1621
|
-
)
|
|
1622
|
-
return
|
|
1623
|
-
await self._send_message(
|
|
1624
|
-
message.chat_id,
|
|
1625
|
-
"Unknown /pr command. Use /pr start|fix|status|stop|resume|collect.",
|
|
1626
|
-
thread_id=message.thread_id,
|
|
1627
|
-
reply_to=message.message_id,
|
|
1628
|
-
)
|
|
1629
|
-
|
|
1630
1175
|
async def _list_recent_commits(
|
|
1631
1176
|
self, record: TelegramTopicRecord
|
|
1632
1177
|
) -> list[tuple[str, str]]:
|
|
@@ -2068,36 +1613,84 @@ class TelegramCommandHandlers(
|
|
|
2068
1613
|
)
|
|
2069
1614
|
return
|
|
2070
1615
|
argv = self._parse_command_args(args)
|
|
2071
|
-
|
|
1616
|
+
|
|
1617
|
+
async def _read_explicit_config_features() -> Optional[str]:
|
|
2072
1618
|
try:
|
|
2073
1619
|
result = await client.request("config/read", {"includeLayers": False})
|
|
1620
|
+
except Exception:
|
|
1621
|
+
return None
|
|
1622
|
+
return _format_feature_flags(result)
|
|
1623
|
+
|
|
1624
|
+
async def _fetch_codex_features() -> (
|
|
1625
|
+
tuple[list[CodexFeatureRow], Optional[str]]
|
|
1626
|
+
):
|
|
1627
|
+
features_command = derive_codex_features_command(
|
|
1628
|
+
self._config.app_server_command
|
|
1629
|
+
)
|
|
1630
|
+
try:
|
|
1631
|
+
result = await client.request(
|
|
1632
|
+
"command/exec",
|
|
1633
|
+
{
|
|
1634
|
+
"cwd": record.workspace_path,
|
|
1635
|
+
"command": features_command,
|
|
1636
|
+
"timeoutMs": 10000,
|
|
1637
|
+
},
|
|
1638
|
+
)
|
|
2074
1639
|
except Exception as exc:
|
|
2075
1640
|
log_event(
|
|
2076
1641
|
self._logger,
|
|
2077
1642
|
logging.WARNING,
|
|
2078
|
-
"telegram.experimental.
|
|
1643
|
+
"telegram.experimental.exec_failed",
|
|
2079
1644
|
chat_id=message.chat_id,
|
|
2080
1645
|
thread_id=message.thread_id,
|
|
2081
1646
|
exc=exc,
|
|
2082
1647
|
)
|
|
1648
|
+
return (
|
|
1649
|
+
[],
|
|
1650
|
+
"Failed to run `codex features list`; check Codex install/PATH.",
|
|
1651
|
+
)
|
|
1652
|
+
stdout, stderr, exit_code = _extract_command_result(result)
|
|
1653
|
+
if exit_code not in (None, 0):
|
|
1654
|
+
detail = stderr.strip() if isinstance(stderr, str) else ""
|
|
1655
|
+
msg = f"`{' '.join(features_command)}` failed (exit {exit_code})."
|
|
1656
|
+
if detail:
|
|
1657
|
+
msg = f"{msg} stderr: {detail}"
|
|
1658
|
+
return [], msg
|
|
1659
|
+
rows = parse_codex_features_list(stdout)
|
|
1660
|
+
if not rows:
|
|
1661
|
+
return (
|
|
1662
|
+
[],
|
|
1663
|
+
f"No feature rows returned by `{' '.join(features_command)}`.",
|
|
1664
|
+
)
|
|
1665
|
+
return rows, None
|
|
1666
|
+
|
|
1667
|
+
list_all = bool(argv and argv[0].lower() == "all")
|
|
1668
|
+
is_list_request = not argv or list_all or argv[0].lower() in ("list", "ls")
|
|
1669
|
+
if is_list_request:
|
|
1670
|
+
stage_filter = None if list_all else "beta"
|
|
1671
|
+
rows, error = await _fetch_codex_features()
|
|
1672
|
+
if error:
|
|
1673
|
+
fallback = await _read_explicit_config_features()
|
|
1674
|
+
message_lines = [error]
|
|
1675
|
+
if fallback and fallback.strip() != "No feature flags found.":
|
|
1676
|
+
message_lines.append("")
|
|
1677
|
+
message_lines.append("Explicit config entries (may be incomplete):")
|
|
1678
|
+
message_lines.append(fallback)
|
|
2083
1679
|
await self._send_message(
|
|
2084
1680
|
message.chat_id,
|
|
2085
|
-
|
|
2086
|
-
"Failed to read config; check logs for details.",
|
|
2087
|
-
chat_id=message.chat_id,
|
|
2088
|
-
thread_id=message.thread_id,
|
|
2089
|
-
),
|
|
1681
|
+
"\n".join(message_lines),
|
|
2090
1682
|
thread_id=message.thread_id,
|
|
2091
1683
|
reply_to=message.message_id,
|
|
2092
1684
|
)
|
|
2093
1685
|
return
|
|
2094
1686
|
await self._send_message(
|
|
2095
1687
|
message.chat_id,
|
|
2096
|
-
|
|
1688
|
+
format_codex_features(rows, stage_filter=stage_filter),
|
|
2097
1689
|
thread_id=message.thread_id,
|
|
2098
1690
|
reply_to=message.message_id,
|
|
2099
1691
|
)
|
|
2100
1692
|
return
|
|
1693
|
+
|
|
2101
1694
|
if len(argv) < 2:
|
|
2102
1695
|
await self._send_message(
|
|
2103
1696
|
message.chat_id,
|
|
@@ -2128,9 +1721,35 @@ class TelegramCommandHandlers(
|
|
|
2128
1721
|
reply_to=message.message_id,
|
|
2129
1722
|
)
|
|
2130
1723
|
return
|
|
1724
|
+
|
|
1725
|
+
rows, error = await _fetch_codex_features()
|
|
1726
|
+
if error:
|
|
1727
|
+
await self._send_message(
|
|
1728
|
+
message.chat_id,
|
|
1729
|
+
error,
|
|
1730
|
+
thread_id=message.thread_id,
|
|
1731
|
+
reply_to=message.message_id,
|
|
1732
|
+
)
|
|
1733
|
+
return
|
|
1734
|
+
|
|
1735
|
+
normalized_feature = feature
|
|
1736
|
+
if feature.startswith("features."):
|
|
1737
|
+
normalized_feature = feature[len("features.") :]
|
|
1738
|
+
target_row = next((row for row in rows if row.key == normalized_feature), None)
|
|
1739
|
+
if target_row is None:
|
|
1740
|
+
available = ", ".join(sorted(row.key for row in rows))
|
|
1741
|
+
await self._send_message(
|
|
1742
|
+
message.chat_id,
|
|
1743
|
+
f"Unknown feature '{feature}'. Known features: {available}\n"
|
|
1744
|
+
"Use /experimental all to list all stages.",
|
|
1745
|
+
thread_id=message.thread_id,
|
|
1746
|
+
reply_to=message.message_id,
|
|
1747
|
+
)
|
|
1748
|
+
return
|
|
1749
|
+
|
|
2131
1750
|
key_path = feature if feature.startswith("features.") else f"features.{feature}"
|
|
2132
1751
|
try:
|
|
2133
|
-
await client.request(
|
|
1752
|
+
write_result = await client.request(
|
|
2134
1753
|
"config/value/write",
|
|
2135
1754
|
{"keyPath": key_path, "value": value, "mergeStrategy": "replace"},
|
|
2136
1755
|
)
|
|
@@ -2154,9 +1773,49 @@ class TelegramCommandHandlers(
|
|
|
2154
1773
|
reply_to=message.message_id,
|
|
2155
1774
|
)
|
|
2156
1775
|
return
|
|
1776
|
+
|
|
1777
|
+
post_rows, post_error = await _fetch_codex_features()
|
|
1778
|
+
effective_row = None
|
|
1779
|
+
if not post_error:
|
|
1780
|
+
effective_row = next(
|
|
1781
|
+
(row for row in post_rows if row.key == normalized_feature), None
|
|
1782
|
+
)
|
|
1783
|
+
|
|
1784
|
+
lines = [f"Feature {key_path} set to {value}."]
|
|
1785
|
+
if effective_row:
|
|
1786
|
+
lines.append(
|
|
1787
|
+
f"Stage: {effective_row.stage}; effective state: {effective_row.enabled}."
|
|
1788
|
+
)
|
|
1789
|
+
elif post_error:
|
|
1790
|
+
lines.append(f"(Could not verify effective state: {post_error})")
|
|
1791
|
+
|
|
1792
|
+
if isinstance(write_result, dict):
|
|
1793
|
+
status = write_result.get("status")
|
|
1794
|
+
overridden = write_result.get("overriddenMetadata")
|
|
1795
|
+
if status == "okOverridden" and isinstance(overridden, dict):
|
|
1796
|
+
message_txt = overridden.get("message")
|
|
1797
|
+
effective_value = overridden.get("effectiveValue")
|
|
1798
|
+
layer = overridden.get("overridingLayer") or {}
|
|
1799
|
+
layer_name = layer.get("name") if isinstance(layer, dict) else None
|
|
1800
|
+
layer_version = (
|
|
1801
|
+
layer.get("version") if isinstance(layer, dict) else None
|
|
1802
|
+
)
|
|
1803
|
+
lines.append("Write was overridden by another config layer.")
|
|
1804
|
+
if layer_name:
|
|
1805
|
+
layer_desc = (
|
|
1806
|
+
f"{layer_name} (version {layer_version})"
|
|
1807
|
+
if layer_version
|
|
1808
|
+
else layer_name
|
|
1809
|
+
)
|
|
1810
|
+
lines.append(f"- Overriding layer: {layer_desc}")
|
|
1811
|
+
if effective_value is not None:
|
|
1812
|
+
lines.append(f"- Effective value: {effective_value}")
|
|
1813
|
+
if isinstance(message_txt, str) and message_txt:
|
|
1814
|
+
lines.append(f"- Note: {message_txt}")
|
|
1815
|
+
|
|
2157
1816
|
await self._send_message(
|
|
2158
1817
|
message.chat_id,
|
|
2159
|
-
|
|
1818
|
+
"\n".join(lines),
|
|
2160
1819
|
thread_id=message.thread_id,
|
|
2161
1820
|
reply_to=message.message_id,
|
|
2162
1821
|
)
|
|
@@ -2352,7 +2011,6 @@ class TelegramCommandHandlers(
|
|
|
2352
2011
|
async def _handle_compact_callback(
|
|
2353
2012
|
self, key: str, callback: TelegramCallbackQuery, parsed: CompactCallback
|
|
2354
2013
|
) -> None:
|
|
2355
|
-
|
|
2356
2014
|
async def _send_compact_status(text: str) -> bool:
|
|
2357
2015
|
try:
|
|
2358
2016
|
await self._send_message(
|
|
@@ -2630,6 +2288,7 @@ Summary applied.""",
|
|
|
2630
2288
|
update_dir=update_dir,
|
|
2631
2289
|
logger=self._logger,
|
|
2632
2290
|
update_target=update_target,
|
|
2291
|
+
skip_checks=bool(getattr(self, "_update_skip_checks", False)),
|
|
2633
2292
|
notify_chat_id=chat_id,
|
|
2634
2293
|
notify_thread_id=thread_id,
|
|
2635
2294
|
notify_reply_to=notify_reply_to,
|
|
@@ -2773,7 +2432,6 @@ Summary applied.""",
|
|
|
2773
2432
|
timeout_seconds: float = 300.0,
|
|
2774
2433
|
interval_seconds: float = 2.0,
|
|
2775
2434
|
) -> None:
|
|
2776
|
-
|
|
2777
2435
|
async def _watch() -> None:
|
|
2778
2436
|
deadline = time.monotonic() + timeout_seconds
|
|
2779
2437
|
while time.monotonic() < deadline:
|