codex-autorunner 1.1.0__py3-none-any.whl → 1.2.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/agents/opencode/client.py +113 -4
- codex_autorunner/agents/opencode/supervisor.py +4 -0
- codex_autorunner/agents/registry.py +17 -7
- codex_autorunner/bootstrap.py +219 -1
- codex_autorunner/core/__init__.py +17 -1
- codex_autorunner/core/about_car.py +114 -1
- codex_autorunner/core/app_server_threads.py +6 -0
- codex_autorunner/core/config.py +236 -1
- codex_autorunner/core/context_awareness.py +38 -0
- codex_autorunner/core/docs.py +0 -122
- codex_autorunner/core/filebox.py +265 -0
- codex_autorunner/core/flows/controller.py +71 -1
- codex_autorunner/core/flows/reconciler.py +4 -1
- codex_autorunner/core/flows/runtime.py +22 -0
- codex_autorunner/core/flows/store.py +61 -9
- codex_autorunner/core/flows/transition.py +23 -16
- codex_autorunner/core/flows/ux_helpers.py +18 -3
- codex_autorunner/core/flows/worker_process.py +32 -6
- codex_autorunner/core/hub.py +198 -41
- codex_autorunner/core/lifecycle_events.py +253 -0
- codex_autorunner/core/path_utils.py +2 -1
- codex_autorunner/core/pma_audit.py +224 -0
- codex_autorunner/core/pma_context.py +496 -0
- codex_autorunner/core/pma_dispatch_interceptor.py +284 -0
- codex_autorunner/core/pma_lifecycle.py +527 -0
- codex_autorunner/core/pma_queue.py +367 -0
- codex_autorunner/core/pma_safety.py +221 -0
- codex_autorunner/core/pma_state.py +115 -0
- codex_autorunner/core/ports/agent_backend.py +2 -5
- codex_autorunner/core/ports/run_event.py +1 -4
- codex_autorunner/core/prompt.py +0 -80
- codex_autorunner/core/prompts.py +56 -172
- codex_autorunner/core/redaction.py +0 -4
- codex_autorunner/core/review_context.py +11 -9
- codex_autorunner/core/runner_controller.py +35 -33
- codex_autorunner/core/runner_state.py +147 -0
- codex_autorunner/core/runtime.py +829 -0
- codex_autorunner/core/sqlite_utils.py +13 -4
- codex_autorunner/core/state.py +7 -10
- codex_autorunner/core/state_roots.py +5 -0
- codex_autorunner/core/templates/__init__.py +39 -0
- codex_autorunner/core/templates/git_mirror.py +234 -0
- codex_autorunner/core/templates/provenance.py +56 -0
- codex_autorunner/core/templates/scan_cache.py +120 -0
- codex_autorunner/core/ticket_linter_cli.py +17 -0
- codex_autorunner/core/ticket_manager_cli.py +154 -92
- codex_autorunner/core/time_utils.py +11 -0
- codex_autorunner/core/types.py +18 -0
- codex_autorunner/core/utils.py +34 -6
- codex_autorunner/flows/review/service.py +23 -25
- codex_autorunner/flows/ticket_flow/definition.py +43 -1
- codex_autorunner/integrations/agents/__init__.py +2 -0
- codex_autorunner/integrations/agents/backend_orchestrator.py +18 -0
- codex_autorunner/integrations/agents/codex_backend.py +19 -8
- codex_autorunner/integrations/agents/runner.py +3 -8
- codex_autorunner/integrations/agents/wiring.py +8 -0
- codex_autorunner/integrations/telegram/doctor.py +228 -6
- codex_autorunner/integrations/telegram/handlers/commands/execution.py +236 -74
- codex_autorunner/integrations/telegram/handlers/commands/files.py +314 -75
- codex_autorunner/integrations/telegram/handlers/commands/flows.py +346 -58
- codex_autorunner/integrations/telegram/handlers/commands/workspace.py +498 -37
- codex_autorunner/integrations/telegram/handlers/commands_runtime.py +202 -45
- codex_autorunner/integrations/telegram/handlers/commands_spec.py +18 -7
- codex_autorunner/integrations/telegram/handlers/messages.py +26 -1
- codex_autorunner/integrations/telegram/helpers.py +1 -3
- codex_autorunner/integrations/telegram/runtime.py +9 -4
- codex_autorunner/integrations/telegram/service.py +30 -0
- codex_autorunner/integrations/telegram/state.py +38 -0
- codex_autorunner/integrations/telegram/ticket_flow_bridge.py +10 -4
- codex_autorunner/integrations/telegram/transport.py +10 -3
- codex_autorunner/integrations/templates/__init__.py +27 -0
- codex_autorunner/integrations/templates/scan_agent.py +312 -0
- codex_autorunner/server.py +2 -2
- codex_autorunner/static/agentControls.js +21 -5
- codex_autorunner/static/app.js +115 -11
- codex_autorunner/static/chatUploads.js +137 -0
- codex_autorunner/static/docChatCore.js +185 -13
- codex_autorunner/static/fileChat.js +68 -40
- codex_autorunner/static/fileboxUi.js +159 -0
- codex_autorunner/static/hub.js +46 -81
- codex_autorunner/static/index.html +303 -24
- codex_autorunner/static/messages.js +82 -4
- codex_autorunner/static/notifications.js +255 -0
- codex_autorunner/static/pma.js +1167 -0
- codex_autorunner/static/settings.js +3 -0
- codex_autorunner/static/streamUtils.js +57 -0
- codex_autorunner/static/styles.css +9125 -6742
- codex_autorunner/static/templateReposSettings.js +225 -0
- codex_autorunner/static/ticketChatActions.js +165 -3
- codex_autorunner/static/ticketChatStream.js +17 -119
- codex_autorunner/static/ticketEditor.js +41 -13
- codex_autorunner/static/ticketTemplates.js +798 -0
- codex_autorunner/static/tickets.js +69 -19
- codex_autorunner/static/turnEvents.js +27 -0
- codex_autorunner/static/turnResume.js +33 -0
- codex_autorunner/static/utils.js +28 -0
- codex_autorunner/static/workspace.js +258 -44
- codex_autorunner/static/workspaceFileBrowser.js +6 -4
- codex_autorunner/surfaces/cli/cli.py +1465 -155
- codex_autorunner/surfaces/cli/pma_cli.py +817 -0
- codex_autorunner/surfaces/web/app.py +253 -49
- codex_autorunner/surfaces/web/routes/__init__.py +4 -0
- codex_autorunner/surfaces/web/routes/analytics.py +29 -22
- codex_autorunner/surfaces/web/routes/file_chat.py +317 -36
- codex_autorunner/surfaces/web/routes/filebox.py +227 -0
- codex_autorunner/surfaces/web/routes/flows.py +219 -29
- codex_autorunner/surfaces/web/routes/messages.py +70 -39
- codex_autorunner/surfaces/web/routes/pma.py +1652 -0
- codex_autorunner/surfaces/web/routes/repos.py +1 -1
- codex_autorunner/surfaces/web/routes/shared.py +0 -3
- codex_autorunner/surfaces/web/routes/templates.py +634 -0
- codex_autorunner/surfaces/web/runner_manager.py +2 -2
- codex_autorunner/surfaces/web/schemas.py +70 -18
- codex_autorunner/tickets/agent_pool.py +27 -0
- codex_autorunner/tickets/files.py +33 -16
- codex_autorunner/tickets/lint.py +50 -0
- codex_autorunner/tickets/models.py +3 -0
- codex_autorunner/tickets/outbox.py +41 -5
- codex_autorunner/tickets/runner.py +350 -69
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.0.dist-info}/METADATA +15 -19
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.0.dist-info}/RECORD +125 -94
- codex_autorunner/core/adapter_utils.py +0 -21
- codex_autorunner/core/engine.py +0 -3302
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.0.dist-info}/WHEEL +0 -0
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.0.dist-info}/entry_points.txt +0 -0
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.0.dist-info}/licenses/LICENSE +0 -0
- {codex_autorunner-1.1.0.dist-info → codex_autorunner-1.2.0.dist-info}/top_level.txt +0 -0
|
@@ -29,6 +29,13 @@ FILES_HINT_TEMPLATE = (
|
|
|
29
29
|
"Check delivery with /files outbox.\n"
|
|
30
30
|
"Max file size: {max_bytes} bytes."
|
|
31
31
|
)
|
|
32
|
+
PMA_FILES_HINT_TEMPLATE = (
|
|
33
|
+
"PMA inbox: {inbox}\n"
|
|
34
|
+
"PMA outbox (pending): {outbox}\n"
|
|
35
|
+
"Place files in outbox pending to send after this turn finishes.\n"
|
|
36
|
+
"Check delivery with /files outbox.\n"
|
|
37
|
+
"Max file size: {max_bytes} bytes."
|
|
38
|
+
)
|
|
32
39
|
|
|
33
40
|
|
|
34
41
|
_GENERIC_TELEGRAM_ERRORS = {
|
|
@@ -97,6 +104,63 @@ class MediaBatchResult:
|
|
|
97
104
|
|
|
98
105
|
|
|
99
106
|
class FilesCommands(SharedHelpers):
|
|
107
|
+
def _files_usage(self, *, pma: bool) -> str:
|
|
108
|
+
header = "Usage:"
|
|
109
|
+
lines = [
|
|
110
|
+
header,
|
|
111
|
+
"/files",
|
|
112
|
+
"/files inbox",
|
|
113
|
+
"/files outbox",
|
|
114
|
+
"/files all",
|
|
115
|
+
"/files send <filename>",
|
|
116
|
+
"/files clear inbox|outbox|all",
|
|
117
|
+
]
|
|
118
|
+
if pma:
|
|
119
|
+
lines.append("Note: PMA files live in .codex-autorunner/pma/inbox|outbox.")
|
|
120
|
+
return "\n".join(lines)
|
|
121
|
+
|
|
122
|
+
async def _send_pma_outbox_file(
|
|
123
|
+
self,
|
|
124
|
+
path: Path,
|
|
125
|
+
*,
|
|
126
|
+
chat_id: int,
|
|
127
|
+
thread_id: Optional[int],
|
|
128
|
+
reply_to: Optional[int],
|
|
129
|
+
) -> bool:
|
|
130
|
+
try:
|
|
131
|
+
data = path.read_bytes()
|
|
132
|
+
except Exception as exc:
|
|
133
|
+
log_event(
|
|
134
|
+
self._logger,
|
|
135
|
+
logging.WARNING,
|
|
136
|
+
"telegram.files.pma_outbox.read_failed",
|
|
137
|
+
chat_id=chat_id,
|
|
138
|
+
thread_id=thread_id,
|
|
139
|
+
path=str(path),
|
|
140
|
+
exc=exc,
|
|
141
|
+
)
|
|
142
|
+
return False
|
|
143
|
+
try:
|
|
144
|
+
await self._bot.send_document(
|
|
145
|
+
chat_id,
|
|
146
|
+
data,
|
|
147
|
+
filename=path.name,
|
|
148
|
+
message_thread_id=thread_id,
|
|
149
|
+
reply_to_message_id=reply_to,
|
|
150
|
+
)
|
|
151
|
+
except Exception as exc:
|
|
152
|
+
log_event(
|
|
153
|
+
self._logger,
|
|
154
|
+
logging.WARNING,
|
|
155
|
+
"telegram.files.pma_outbox.send_failed",
|
|
156
|
+
chat_id=chat_id,
|
|
157
|
+
thread_id=thread_id,
|
|
158
|
+
path=str(path),
|
|
159
|
+
exc=exc,
|
|
160
|
+
)
|
|
161
|
+
return False
|
|
162
|
+
return True
|
|
163
|
+
|
|
100
164
|
def _format_telegram_download_error(self, exc: Exception) -> Optional[str]:
|
|
101
165
|
for current in _iter_exception_chain(exc):
|
|
102
166
|
if isinstance(current, Exception):
|
|
@@ -245,7 +309,11 @@ class FilesCommands(SharedHelpers):
|
|
|
245
309
|
return
|
|
246
310
|
try:
|
|
247
311
|
image_path = self._save_image_file(
|
|
248
|
-
record.workspace_path,
|
|
312
|
+
record.workspace_path,
|
|
313
|
+
data,
|
|
314
|
+
file_path,
|
|
315
|
+
candidate,
|
|
316
|
+
pma_enabled=bool(getattr(record, "pma_enabled", False)),
|
|
249
317
|
)
|
|
250
318
|
except asyncio.CancelledError:
|
|
251
319
|
raise
|
|
@@ -442,6 +510,7 @@ class FilesCommands(SharedHelpers):
|
|
|
442
510
|
data,
|
|
443
511
|
candidate=candidate,
|
|
444
512
|
file_path=file_path,
|
|
513
|
+
pma_enabled=bool(getattr(record, "pma_enabled", False)),
|
|
445
514
|
)
|
|
446
515
|
except asyncio.CancelledError:
|
|
447
516
|
raise
|
|
@@ -470,6 +539,7 @@ class FilesCommands(SharedHelpers):
|
|
|
470
539
|
file_size=file_size or len(data),
|
|
471
540
|
topic_key=key,
|
|
472
541
|
workspace_path=record.workspace_path,
|
|
542
|
+
pma_enabled=bool(getattr(record, "pma_enabled", False)),
|
|
473
543
|
)
|
|
474
544
|
log_event(
|
|
475
545
|
self._logger,
|
|
@@ -543,6 +613,15 @@ class FilesCommands(SharedHelpers):
|
|
|
543
613
|
first_msg.chat_id, first_msg.thread_id
|
|
544
614
|
)
|
|
545
615
|
record = await self._router.get_topic(topic_key)
|
|
616
|
+
record, pma_error = message_handlers._record_with_media_workspace(self, record)
|
|
617
|
+
if pma_error:
|
|
618
|
+
await self._send_message(
|
|
619
|
+
first_msg.chat_id,
|
|
620
|
+
pma_error,
|
|
621
|
+
thread_id=first_msg.thread_id,
|
|
622
|
+
reply_to=first_msg.message_id,
|
|
623
|
+
)
|
|
624
|
+
return None
|
|
546
625
|
if record is None or not record.workspace_path:
|
|
547
626
|
await self._send_message(
|
|
548
627
|
first_msg.chat_id,
|
|
@@ -669,7 +748,11 @@ class FilesCommands(SharedHelpers):
|
|
|
669
748
|
return True
|
|
670
749
|
try:
|
|
671
750
|
image_path = self._save_image_file(
|
|
672
|
-
context.record.workspace_path,
|
|
751
|
+
context.record.workspace_path,
|
|
752
|
+
data,
|
|
753
|
+
file_path,
|
|
754
|
+
candidate,
|
|
755
|
+
pma_enabled=bool(getattr(context.record, "pma_enabled", False)),
|
|
673
756
|
)
|
|
674
757
|
saved_image_paths.append(image_path)
|
|
675
758
|
except asyncio.CancelledError:
|
|
@@ -759,6 +842,7 @@ class FilesCommands(SharedHelpers):
|
|
|
759
842
|
data,
|
|
760
843
|
candidate=candidate,
|
|
761
844
|
file_path=file_path,
|
|
845
|
+
pma_enabled=bool(getattr(context.record, "pma_enabled", False)),
|
|
762
846
|
)
|
|
763
847
|
original_name = (
|
|
764
848
|
candidate.file_name
|
|
@@ -815,23 +899,10 @@ class FilesCommands(SharedHelpers):
|
|
|
815
899
|
prompt_parts.append(
|
|
816
900
|
f"\nFailed to process {result.stats.failed_count} item(s)."
|
|
817
901
|
)
|
|
818
|
-
|
|
819
|
-
context.record.workspace_path,
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
context.record.workspace_path, context.topic_key
|
|
823
|
-
)
|
|
824
|
-
topic_dir = self._files_topic_dir(
|
|
825
|
-
context.record.workspace_path, context.topic_key
|
|
826
|
-
)
|
|
827
|
-
hint = wrap_injected_context(
|
|
828
|
-
FILES_HINT_TEMPLATE.format(
|
|
829
|
-
inbox=str(inbox_dir),
|
|
830
|
-
outbox=str(outbox_dir),
|
|
831
|
-
topic_key=context.topic_key,
|
|
832
|
-
topic_dir=str(topic_dir),
|
|
833
|
-
max_bytes=self._config.media.max_file_bytes,
|
|
834
|
-
)
|
|
902
|
+
hint = self._build_files_hint(
|
|
903
|
+
workspace_path=context.record.workspace_path,
|
|
904
|
+
topic_key=context.topic_key,
|
|
905
|
+
pma_enabled=bool(getattr(context.record, "pma_enabled", False)),
|
|
835
906
|
)
|
|
836
907
|
prompt_parts.append(hint)
|
|
837
908
|
combined_prompt = "\n\n".join(prompt_parts)
|
|
@@ -905,7 +976,11 @@ class FilesCommands(SharedHelpers):
|
|
|
905
976
|
data = await self._bot.download_file(file_path)
|
|
906
977
|
return data, file_path, file_size
|
|
907
978
|
|
|
908
|
-
def _image_storage_dir(self, workspace_path: str) -> Path:
|
|
979
|
+
def _image_storage_dir(self, workspace_path: str, *, pma_enabled: bool) -> Path:
|
|
980
|
+
if pma_enabled:
|
|
981
|
+
pma_inbox = self._pma_inbox_dir()
|
|
982
|
+
if pma_inbox is not None:
|
|
983
|
+
return pma_inbox
|
|
909
984
|
return (
|
|
910
985
|
Path(workspace_path) / ".codex-autorunner" / "uploads" / "telegram-images"
|
|
911
986
|
)
|
|
@@ -935,8 +1010,10 @@ class FilesCommands(SharedHelpers):
|
|
|
935
1010
|
data: bytes,
|
|
936
1011
|
file_path: Optional[str],
|
|
937
1012
|
candidate: TelegramMediaCandidate,
|
|
1013
|
+
*,
|
|
1014
|
+
pma_enabled: bool,
|
|
938
1015
|
) -> Path:
|
|
939
|
-
images_dir = self._image_storage_dir(workspace_path)
|
|
1016
|
+
images_dir = self._image_storage_dir(workspace_path, pma_enabled=pma_enabled)
|
|
940
1017
|
images_dir.mkdir(parents=True, exist_ok=True)
|
|
941
1018
|
ext = self._choose_image_extension(
|
|
942
1019
|
file_path=file_path,
|
|
@@ -952,6 +1029,24 @@ class FilesCommands(SharedHelpers):
|
|
|
952
1029
|
def _files_root_dir(self, workspace_path: str) -> Path:
|
|
953
1030
|
return Path(workspace_path) / ".codex-autorunner" / "uploads" / "telegram-files"
|
|
954
1031
|
|
|
1032
|
+
def _pma_root_dir(self) -> Optional[Path]:
|
|
1033
|
+
hub_root = getattr(self, "_hub_root", None)
|
|
1034
|
+
if hub_root is None:
|
|
1035
|
+
return None
|
|
1036
|
+
return Path(hub_root) / ".codex-autorunner" / "pma"
|
|
1037
|
+
|
|
1038
|
+
def _pma_inbox_dir(self) -> Optional[Path]:
|
|
1039
|
+
pma_root = self._pma_root_dir()
|
|
1040
|
+
if pma_root is None:
|
|
1041
|
+
return None
|
|
1042
|
+
return pma_root / "inbox"
|
|
1043
|
+
|
|
1044
|
+
def _pma_outbox_dir(self) -> Optional[Path]:
|
|
1045
|
+
pma_root = self._pma_root_dir()
|
|
1046
|
+
if pma_root is None:
|
|
1047
|
+
return None
|
|
1048
|
+
return pma_root / "outbox"
|
|
1049
|
+
|
|
955
1050
|
def _sanitize_topic_dir_name(self, key: str) -> str:
|
|
956
1051
|
cleaned = re.sub(r"[^A-Za-z0-9._-]+", "_", key).strip("._-")
|
|
957
1052
|
if not cleaned:
|
|
@@ -1013,8 +1108,13 @@ class FilesCommands(SharedHelpers):
|
|
|
1013
1108
|
*,
|
|
1014
1109
|
candidate: TelegramMediaCandidate,
|
|
1015
1110
|
file_path: Optional[str],
|
|
1111
|
+
pma_enabled: bool,
|
|
1016
1112
|
) -> Path:
|
|
1017
1113
|
inbox_dir = self._files_inbox_dir(workspace_path, topic_key)
|
|
1114
|
+
if pma_enabled:
|
|
1115
|
+
pma_inbox = self._pma_inbox_dir()
|
|
1116
|
+
if pma_inbox is not None:
|
|
1117
|
+
inbox_dir = pma_inbox
|
|
1018
1118
|
inbox_dir.mkdir(parents=True, exist_ok=True)
|
|
1019
1119
|
stem = self._sanitize_filename_component(
|
|
1020
1120
|
self._choose_file_stem(candidate.file_name, file_path),
|
|
@@ -1041,6 +1141,7 @@ class FilesCommands(SharedHelpers):
|
|
|
1041
1141
|
file_size: int,
|
|
1042
1142
|
topic_key: str,
|
|
1043
1143
|
workspace_path: str,
|
|
1144
|
+
pma_enabled: bool,
|
|
1044
1145
|
) -> str:
|
|
1045
1146
|
header = caption_text.strip() or "File received."
|
|
1046
1147
|
original_name = (
|
|
@@ -1048,17 +1149,10 @@ class FilesCommands(SharedHelpers):
|
|
|
1048
1149
|
or (Path(source_path).name if source_path else None)
|
|
1049
1150
|
or "unknown"
|
|
1050
1151
|
)
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
FILES_HINT_TEMPLATE.format(
|
|
1056
|
-
inbox=str(inbox_dir),
|
|
1057
|
-
outbox=str(outbox_dir),
|
|
1058
|
-
topic_key=topic_key,
|
|
1059
|
-
topic_dir=str(topic_dir),
|
|
1060
|
-
max_bytes=self._config.media.max_file_bytes,
|
|
1061
|
-
)
|
|
1152
|
+
hint = self._build_files_hint(
|
|
1153
|
+
workspace_path=workspace_path,
|
|
1154
|
+
topic_key=topic_key,
|
|
1155
|
+
pma_enabled=pma_enabled,
|
|
1062
1156
|
)
|
|
1063
1157
|
parts = [
|
|
1064
1158
|
header,
|
|
@@ -1074,6 +1168,37 @@ class FilesCommands(SharedHelpers):
|
|
|
1074
1168
|
parts.append(hint)
|
|
1075
1169
|
return "\n".join(parts)
|
|
1076
1170
|
|
|
1171
|
+
def _build_files_hint(
|
|
1172
|
+
self,
|
|
1173
|
+
*,
|
|
1174
|
+
workspace_path: str,
|
|
1175
|
+
topic_key: str,
|
|
1176
|
+
pma_enabled: bool,
|
|
1177
|
+
) -> str:
|
|
1178
|
+
if pma_enabled:
|
|
1179
|
+
pma_inbox = self._pma_inbox_dir()
|
|
1180
|
+
pma_outbox = self._pma_outbox_dir()
|
|
1181
|
+
if pma_inbox is not None and pma_outbox is not None:
|
|
1182
|
+
return wrap_injected_context(
|
|
1183
|
+
PMA_FILES_HINT_TEMPLATE.format(
|
|
1184
|
+
inbox=str(pma_inbox),
|
|
1185
|
+
outbox=str(pma_outbox),
|
|
1186
|
+
max_bytes=self._config.media.max_file_bytes,
|
|
1187
|
+
)
|
|
1188
|
+
)
|
|
1189
|
+
inbox_dir = self._files_inbox_dir(workspace_path, topic_key)
|
|
1190
|
+
outbox_dir = self._files_outbox_pending_dir(workspace_path, topic_key)
|
|
1191
|
+
topic_dir = self._files_topic_dir(workspace_path, topic_key)
|
|
1192
|
+
return wrap_injected_context(
|
|
1193
|
+
FILES_HINT_TEMPLATE.format(
|
|
1194
|
+
inbox=str(inbox_dir),
|
|
1195
|
+
outbox=str(outbox_dir),
|
|
1196
|
+
topic_key=topic_key,
|
|
1197
|
+
topic_dir=str(topic_dir),
|
|
1198
|
+
max_bytes=self._config.media.max_file_bytes,
|
|
1199
|
+
)
|
|
1200
|
+
)
|
|
1201
|
+
|
|
1077
1202
|
def _format_bytes(self, size: int) -> str:
|
|
1078
1203
|
if size < 1024:
|
|
1079
1204
|
return f"{size} B"
|
|
@@ -1192,7 +1317,12 @@ class FilesCommands(SharedHelpers):
|
|
|
1192
1317
|
key = topic_key
|
|
1193
1318
|
else:
|
|
1194
1319
|
key = await self._resolve_topic_key(chat_id, thread_id)
|
|
1320
|
+
pma_enabled = bool(getattr(record, "pma_enabled", False))
|
|
1195
1321
|
pending_dir = self._files_outbox_pending_dir(record.workspace_path, key)
|
|
1322
|
+
if pma_enabled:
|
|
1323
|
+
pma_outbox = self._pma_outbox_dir()
|
|
1324
|
+
if pma_outbox is not None:
|
|
1325
|
+
pending_dir = pma_outbox
|
|
1196
1326
|
if not pending_dir.exists():
|
|
1197
1327
|
return
|
|
1198
1328
|
files = self._list_files(pending_dir)
|
|
@@ -1215,13 +1345,34 @@ class FilesCommands(SharedHelpers):
|
|
|
1215
1345
|
reply_to=reply_to,
|
|
1216
1346
|
)
|
|
1217
1347
|
continue
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1348
|
+
if pma_enabled:
|
|
1349
|
+
success = await self._send_pma_outbox_file(
|
|
1350
|
+
path,
|
|
1351
|
+
chat_id=chat_id,
|
|
1352
|
+
thread_id=thread_id,
|
|
1353
|
+
reply_to=reply_to,
|
|
1354
|
+
)
|
|
1355
|
+
if success:
|
|
1356
|
+
try:
|
|
1357
|
+
path.unlink()
|
|
1358
|
+
except OSError as exc:
|
|
1359
|
+
log_event(
|
|
1360
|
+
self._logger,
|
|
1361
|
+
logging.WARNING,
|
|
1362
|
+
"telegram.files.pma_outbox.delete_failed",
|
|
1363
|
+
chat_id=chat_id,
|
|
1364
|
+
thread_id=thread_id,
|
|
1365
|
+
path=str(path),
|
|
1366
|
+
exc=exc,
|
|
1367
|
+
)
|
|
1368
|
+
else:
|
|
1369
|
+
await self._send_outbox_file(
|
|
1370
|
+
path,
|
|
1371
|
+
sent_dir=sent_dir,
|
|
1372
|
+
chat_id=chat_id,
|
|
1373
|
+
thread_id=thread_id,
|
|
1374
|
+
reply_to=reply_to,
|
|
1375
|
+
)
|
|
1225
1376
|
|
|
1226
1377
|
def _format_file_listing(self, title: str, files: list[Path]) -> str:
|
|
1227
1378
|
if not files:
|
|
@@ -1266,26 +1417,72 @@ class FilesCommands(SharedHelpers):
|
|
|
1266
1417
|
reply_to=message.message_id,
|
|
1267
1418
|
)
|
|
1268
1419
|
return
|
|
1269
|
-
record = await self._require_bound_record(message)
|
|
1270
|
-
if not record:
|
|
1271
|
-
return
|
|
1272
1420
|
key = await self._resolve_topic_key(message.chat_id, message.thread_id)
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1421
|
+
record = await self._router.ensure_topic(message.chat_id, message.thread_id)
|
|
1422
|
+
record, pma_error = message_handlers._record_with_media_workspace(self, record)
|
|
1423
|
+
if pma_error:
|
|
1424
|
+
await self._send_message(
|
|
1425
|
+
message.chat_id,
|
|
1426
|
+
pma_error,
|
|
1427
|
+
thread_id=message.thread_id,
|
|
1428
|
+
reply_to=message.message_id,
|
|
1429
|
+
)
|
|
1430
|
+
return
|
|
1431
|
+
if record is None or not record.workspace_path:
|
|
1432
|
+
await self._send_message(
|
|
1433
|
+
message.chat_id,
|
|
1434
|
+
self._with_conversation_id(
|
|
1435
|
+
"Topic not bound. Use /bind <repo_id> or /bind <path>.",
|
|
1436
|
+
chat_id=message.chat_id,
|
|
1437
|
+
thread_id=message.thread_id,
|
|
1438
|
+
),
|
|
1439
|
+
thread_id=message.thread_id,
|
|
1440
|
+
reply_to=message.message_id,
|
|
1441
|
+
)
|
|
1442
|
+
return
|
|
1443
|
+
pma_enabled = bool(getattr(record, "pma_enabled", False))
|
|
1444
|
+
if pma_enabled:
|
|
1445
|
+
hub_root = getattr(self, "_hub_root", None)
|
|
1446
|
+
if hub_root is None:
|
|
1447
|
+
await self._send_message(
|
|
1448
|
+
message.chat_id,
|
|
1449
|
+
"PMA unavailable; hub root not configured.",
|
|
1450
|
+
thread_id=message.thread_id,
|
|
1451
|
+
reply_to=message.message_id,
|
|
1452
|
+
)
|
|
1453
|
+
return
|
|
1454
|
+
pma_root = Path(hub_root) / ".codex-autorunner" / "pma"
|
|
1455
|
+
inbox_dir = pma_root / "inbox"
|
|
1456
|
+
outbox_dir = pma_root / "outbox"
|
|
1457
|
+
pending_dir = outbox_dir
|
|
1458
|
+
sent_dir = outbox_dir
|
|
1459
|
+
else:
|
|
1460
|
+
inbox_dir = self._files_inbox_dir(record.workspace_path, key)
|
|
1461
|
+
pending_dir = self._files_outbox_pending_dir(record.workspace_path, key)
|
|
1462
|
+
sent_dir = self._files_outbox_sent_dir(record.workspace_path, key)
|
|
1276
1463
|
argv = self._parse_command_args(args)
|
|
1277
1464
|
if not argv:
|
|
1278
1465
|
inbox_items = self._list_files(inbox_dir)
|
|
1279
1466
|
pending_items = self._list_files(pending_dir)
|
|
1280
|
-
sent_items = self._list_files(sent_dir)
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1467
|
+
sent_items = [] if pma_enabled else self._list_files(sent_dir)
|
|
1468
|
+
usage = self._files_usage(pma=pma_enabled)
|
|
1469
|
+
if pma_enabled:
|
|
1470
|
+
text = "\n".join(
|
|
1471
|
+
[
|
|
1472
|
+
f"Inbox: {len(inbox_items)} file(s)",
|
|
1473
|
+
f"Outbox: {len(pending_items)} file(s)",
|
|
1474
|
+
usage,
|
|
1475
|
+
]
|
|
1476
|
+
)
|
|
1477
|
+
else:
|
|
1478
|
+
text = "\n".join(
|
|
1479
|
+
[
|
|
1480
|
+
f"Inbox: {len(inbox_items)} item(s)",
|
|
1481
|
+
f"Outbox pending: {len(pending_items)} item(s)",
|
|
1482
|
+
f"Outbox sent: {len(sent_items)} item(s)",
|
|
1483
|
+
usage,
|
|
1484
|
+
]
|
|
1485
|
+
)
|
|
1289
1486
|
await self._send_message(
|
|
1290
1487
|
message.chat_id,
|
|
1291
1488
|
text,
|
|
@@ -1306,14 +1503,46 @@ class FilesCommands(SharedHelpers):
|
|
|
1306
1503
|
return
|
|
1307
1504
|
if subcommand == "outbox":
|
|
1308
1505
|
pending_items = self._list_files(pending_dir)
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1506
|
+
if pma_enabled:
|
|
1507
|
+
text = self._format_file_listing("Outbox", pending_items)
|
|
1508
|
+
else:
|
|
1509
|
+
sent_items = self._list_files(sent_dir)
|
|
1510
|
+
text = "\n".join(
|
|
1511
|
+
[
|
|
1512
|
+
self._format_file_listing("Outbox pending", pending_items),
|
|
1513
|
+
"",
|
|
1514
|
+
self._format_file_listing("Outbox sent", sent_items),
|
|
1515
|
+
]
|
|
1516
|
+
)
|
|
1517
|
+
await self._send_message(
|
|
1518
|
+
message.chat_id,
|
|
1519
|
+
text,
|
|
1520
|
+
thread_id=message.thread_id,
|
|
1521
|
+
reply_to=message.message_id,
|
|
1316
1522
|
)
|
|
1523
|
+
return
|
|
1524
|
+
if subcommand == "all":
|
|
1525
|
+
inbox_items = self._list_files(inbox_dir)
|
|
1526
|
+
pending_items = self._list_files(pending_dir)
|
|
1527
|
+
if pma_enabled:
|
|
1528
|
+
text = "\n".join(
|
|
1529
|
+
[
|
|
1530
|
+
self._format_file_listing("Inbox", inbox_items),
|
|
1531
|
+
"",
|
|
1532
|
+
self._format_file_listing("Outbox", pending_items),
|
|
1533
|
+
]
|
|
1534
|
+
)
|
|
1535
|
+
else:
|
|
1536
|
+
sent_items = self._list_files(sent_dir)
|
|
1537
|
+
text = "\n".join(
|
|
1538
|
+
[
|
|
1539
|
+
self._format_file_listing("Inbox", inbox_items),
|
|
1540
|
+
"",
|
|
1541
|
+
self._format_file_listing("Outbox pending", pending_items),
|
|
1542
|
+
"",
|
|
1543
|
+
self._format_file_listing("Outbox sent", sent_items),
|
|
1544
|
+
]
|
|
1545
|
+
)
|
|
1317
1546
|
await self._send_message(
|
|
1318
1547
|
message.chat_id,
|
|
1319
1548
|
text,
|
|
@@ -1325,7 +1554,7 @@ class FilesCommands(SharedHelpers):
|
|
|
1325
1554
|
if len(argv) < 2:
|
|
1326
1555
|
await self._send_message(
|
|
1327
1556
|
message.chat_id,
|
|
1328
|
-
|
|
1557
|
+
self._files_usage(pma=pma_enabled),
|
|
1329
1558
|
thread_id=message.thread_id,
|
|
1330
1559
|
reply_to=message.message_id,
|
|
1331
1560
|
)
|
|
@@ -1336,15 +1565,17 @@ class FilesCommands(SharedHelpers):
|
|
|
1336
1565
|
deleted = self._delete_files_in_dir(inbox_dir)
|
|
1337
1566
|
elif target == "outbox":
|
|
1338
1567
|
deleted = self._delete_files_in_dir(pending_dir)
|
|
1339
|
-
|
|
1568
|
+
if not pma_enabled:
|
|
1569
|
+
deleted += self._delete_files_in_dir(sent_dir)
|
|
1340
1570
|
elif target == "all":
|
|
1341
1571
|
deleted = self._delete_files_in_dir(inbox_dir)
|
|
1342
1572
|
deleted += self._delete_files_in_dir(pending_dir)
|
|
1343
|
-
|
|
1573
|
+
if not pma_enabled:
|
|
1574
|
+
deleted += self._delete_files_in_dir(sent_dir)
|
|
1344
1575
|
else:
|
|
1345
1576
|
await self._send_message(
|
|
1346
1577
|
message.chat_id,
|
|
1347
|
-
|
|
1578
|
+
self._files_usage(pma=pma_enabled),
|
|
1348
1579
|
thread_id=message.thread_id,
|
|
1349
1580
|
reply_to=message.message_id,
|
|
1350
1581
|
)
|
|
@@ -1360,7 +1591,7 @@ class FilesCommands(SharedHelpers):
|
|
|
1360
1591
|
if len(argv) < 2:
|
|
1361
1592
|
await self._send_message(
|
|
1362
1593
|
message.chat_id,
|
|
1363
|
-
|
|
1594
|
+
self._files_usage(pma=pma_enabled),
|
|
1364
1595
|
thread_id=message.thread_id,
|
|
1365
1596
|
reply_to=message.message_id,
|
|
1366
1597
|
)
|
|
@@ -1370,7 +1601,7 @@ class FilesCommands(SharedHelpers):
|
|
|
1370
1601
|
if not _path_within(pending_dir, candidate) or not candidate.is_file():
|
|
1371
1602
|
await self._send_message(
|
|
1372
1603
|
message.chat_id,
|
|
1373
|
-
f"Outbox
|
|
1604
|
+
f"Outbox file not found: {name}",
|
|
1374
1605
|
thread_id=message.thread_id,
|
|
1375
1606
|
reply_to=message.message_id,
|
|
1376
1607
|
)
|
|
@@ -1385,13 +1616,21 @@ class FilesCommands(SharedHelpers):
|
|
|
1385
1616
|
reply_to=message.message_id,
|
|
1386
1617
|
)
|
|
1387
1618
|
return
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1619
|
+
if pma_enabled:
|
|
1620
|
+
success = await self._send_pma_outbox_file(
|
|
1621
|
+
candidate,
|
|
1622
|
+
chat_id=message.chat_id,
|
|
1623
|
+
thread_id=message.thread_id,
|
|
1624
|
+
reply_to=message.message_id,
|
|
1625
|
+
)
|
|
1626
|
+
else:
|
|
1627
|
+
success = await self._send_outbox_file(
|
|
1628
|
+
candidate,
|
|
1629
|
+
sent_dir=sent_dir,
|
|
1630
|
+
chat_id=message.chat_id,
|
|
1631
|
+
thread_id=message.thread_id,
|
|
1632
|
+
reply_to=message.message_id,
|
|
1633
|
+
)
|
|
1395
1634
|
result = "Sent." if success else "Failed to send."
|
|
1396
1635
|
await self._send_message(
|
|
1397
1636
|
message.chat_id,
|
|
@@ -1402,7 +1641,7 @@ class FilesCommands(SharedHelpers):
|
|
|
1402
1641
|
return
|
|
1403
1642
|
await self._send_message(
|
|
1404
1643
|
message.chat_id,
|
|
1405
|
-
|
|
1644
|
+
self._files_usage(pma=pma_enabled),
|
|
1406
1645
|
thread_id=message.thread_id,
|
|
1407
1646
|
reply_to=message.message_id,
|
|
1408
1647
|
)
|