fix(email): use RFC to save inbound attachments into execution runtime

linuztx committed Mar 23, 2026 at 13:03 UTC 42487055f3907720acfbb79e93608fba589f0f9e
2 files changed +50 -2
plugins/_email_integration/helpers/attachment_writer.py new
+36
@@ -0,0 +1,36 @@
1 +"""
2 +Write attachment files into execution runtime.
3 +
4 +No agent/tool dependencies.
5 +"""
6 +
7 +import base64
8 +import os
9 +from typing import TypedDict
10 +
11 +
12 +# ------------------------------------------------------------------
13 +# Data models
14 +# ------------------------------------------------------------------
15 +
16 +class WriteResult(TypedDict):
17 + path: str
18 + error: str
19 +
20 +
21 +# ------------------------------------------------------------------
22 +# File writer
23 +# ------------------------------------------------------------------
24 +
25 +def write_attachment(rel_path: str, content_b64: str) -> WriteResult:
26 + try:
27 + from helpers import files
28 + abs_path = files.get_abs_path(rel_path)
29 + files.make_dirs(abs_path)
30 +
31 + content = base64.b64decode(content_b64)
32 + files.write_file_bin(abs_path, content)
33 +
34 + return WriteResult(path=abs_path, error="")
35 + except Exception as e:
36 + return WriteResult(path="", error=str(e))
\ No newline at end of file
plugins/_email_integration/helpers/imap_client.py
+14 -2
@@ -412,8 +412,20 @@ async def _save_attachment(filename: str, content: bytes, download_folder: str)
412 name, ext = os.path.splitext(filename)
413 unique = f"{name}_{uuid.uuid4().hex[:8]}{ext}"
414 rel_path = os.path.join(download_folder, unique)
415 - files.write_file_bin(rel_path, content)
416 - return files.get_abs_path(rel_path)
415 + from helpers import runtime
416 + from plugins._email_integration.helpers.attachment_writer import write_attachment
417 +
418 + import base64
419 + content_b64 = base64.b64encode(content).decode()
420 +
421 + result = await runtime.call_development_function(
422 + write_attachment, rel_path, content_b64
423 + )
424 + if result.get("error"):
425 + from helpers.print_style import PrintStyle
426 + PrintStyle.error(f"Failed to save attachment {filename}: {result['error']}")
427 +
428 + return result.get("path", files.get_abs_path(rel_path))
429
430
431 def _decode_header(header: str) -> str: