mdify-cli 3.0.8__tar.gz → 3.1.0__tar.gz

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.
Files changed (27) hide show
  1. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/PKG-INFO +1 -1
  2. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/__init__.py +1 -1
  3. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/cli.py +63 -12
  4. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify_cli.egg-info/PKG-INFO +1 -1
  5. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/pyproject.toml +1 -1
  6. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/LICENSE +0 -0
  7. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/README.md +0 -0
  8. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/assets/mdify.png +0 -0
  9. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/__main__.py +0 -0
  10. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/container.py +0 -0
  11. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/docling_client.py +0 -0
  12. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/formatting.py +0 -0
  13. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/ssh/__init__.py +0 -0
  14. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/ssh/client.py +0 -0
  15. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/ssh/models.py +0 -0
  16. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/ssh/remote_container.py +0 -0
  17. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify/ssh/transfer.py +0 -0
  18. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify_cli.egg-info/SOURCES.txt +0 -0
  19. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify_cli.egg-info/dependency_links.txt +0 -0
  20. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify_cli.egg-info/entry_points.txt +0 -0
  21. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify_cli.egg-info/requires.txt +0 -0
  22. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/mdify_cli.egg-info/top_level.txt +0 -0
  23. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/setup.cfg +0 -0
  24. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/tests/test_cli.py +0 -0
  25. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/tests/test_container.py +0 -0
  26. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/tests/test_docling_client.py +0 -0
  27. {mdify_cli-3.0.8 → mdify_cli-3.1.0}/tests/test_ssh_client.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdify-cli
3
- Version: 3.0.8
3
+ Version: 3.1.0
4
4
  Summary: Convert PDFs and document images into structured Markdown for LLM workflows
5
5
  Author: tiroq
6
6
  License-Expression: MIT
@@ -1,3 +1,3 @@
1
1
  """mdify - Convert documents to Markdown via Docling container."""
2
2
 
3
- __version__ = "3.0.8"
3
+ __version__ = "3.1.0"
@@ -8,12 +8,14 @@ is lightweight and has no ML dependencies.
8
8
  """
9
9
 
10
10
  import argparse
11
+ import asyncio
11
12
  import json
12
13
  import os
13
14
  import platform
14
15
  import shutil
15
16
  import subprocess
16
17
  import sys
18
+ import tempfile
17
19
  import threading
18
20
  import time
19
21
  from pathlib import Path
@@ -1247,7 +1249,8 @@ def main_async_remote(args) -> int:
1247
1249
  if isinstance(exc, SSHConnectionError):
1248
1250
  return True
1249
1251
  msg = str(exc).lower()
1250
- return "broken pipe" in msg or "connection closed" in msg
1252
+ # Errno 32 = Broken pipe, Errno 54 = Connection reset by peer
1253
+ return any(x in msg for x in ["broken pipe", "connection closed", "connection reset", "errno 32", "errno 54", "ssh connection"])
1251
1254
 
1252
1255
  try:
1253
1256
  for idx, input_file in enumerate(files_to_convert, 1):
@@ -1336,7 +1339,10 @@ def main_async_remote(args) -> int:
1336
1339
  while conversion_attempt < 3 and not conversion_success:
1337
1340
  try:
1338
1341
  if conversion_attempt > 0 and not args.quiet:
1339
- print(f" ↻ Conversion retry {conversion_attempt}...", file=sys.stderr)
1342
+ # Exponential backoff: 2s, 4s, 8s
1343
+ backoff_delay = 2 ** conversion_attempt
1344
+ print(f" ↻ Conversion retry {conversion_attempt} (waiting {backoff_delay}s for server recovery)...", file=sys.stderr)
1345
+ await asyncio.sleep(backoff_delay)
1340
1346
 
1341
1347
  conversion_output, _, conv_code = await ssh_client.run_command(convert_cmd, timeout=remote_conversion_timeout)
1342
1348
 
@@ -1344,19 +1350,39 @@ def main_async_remote(args) -> int:
1344
1350
  conversion_success = True
1345
1351
  break
1346
1352
  else:
1347
- conversion_attempt += 1
1353
+ # Non-zero exit code - fail without retry for non-connection errors
1354
+ break
1348
1355
  except Exception as conv_exc:
1349
- if is_connection_error(conv_exc) and conversion_attempt < 2:
1356
+ is_conn_err = is_connection_error(conv_exc)
1357
+ if is_conn_err and conversion_attempt < 2:
1350
1358
  conversion_attempt += 1
1351
1359
  if not args.quiet:
1352
- print(f" ↻ Connection lost during conversion. Reconnecting (attempt {conversion_attempt})...", file=sys.stderr)
1360
+ # Exponential backoff: 5s, 10s
1361
+ backoff_delay = 5 * conversion_attempt
1362
+ print(f" ↻ Connection reset during conversion. Reconnecting in {backoff_delay}s...", file=sys.stderr)
1363
+
1364
+ await asyncio.sleep(backoff_delay)
1365
+
1353
1366
  try:
1354
1367
  await ssh_client.disconnect()
1355
1368
  except Exception:
1356
1369
  pass
1357
- await ssh_client.connect()
1370
+
1371
+ # Reconnect with retry
1372
+ try:
1373
+ await ssh_client.connect()
1374
+ except Exception:
1375
+ if not args.quiet:
1376
+ print(f" ⚠ Reconnection failed: retrying...", file=sys.stderr)
1377
+ continue
1358
1378
  else:
1359
- conversion_attempt += 1
1379
+ # Either not a connection error, or we've exhausted retries
1380
+ if not args.quiet:
1381
+ print(f" [DEBUG] Breaking loop: not conn_err or exhausted retries", file=sys.stderr)
1382
+ if conversion_attempt >= 2 and is_conn_err:
1383
+ if not args.quiet:
1384
+ print(f" ↻ Connection error on final retry attempt", file=sys.stderr)
1385
+ break
1360
1386
 
1361
1387
  if not conversion_success:
1362
1388
  print(f" ✗ Failed: Conversion failed after {conversion_attempt} attempt(s)", file=sys.stderr)
@@ -1396,12 +1422,37 @@ def main_async_remote(args) -> int:
1396
1422
  # Ultimate fallback
1397
1423
  markdown_content = conversion_output
1398
1424
 
1399
- # Write markdown content to remote file
1400
- write_cmd = f"cat > {remote_output_path} << 'MDIFY_EOF'\n{markdown_content}\nMDIFY_EOF"
1401
- _, _, write_code = await ssh_client.run_command(write_cmd, timeout=30)
1425
+ # Write markdown content to local temp file first, then upload via SFTP
1426
+ # (Piping large content through SSH here-documents can crash the connection)
1427
+ content_size_kb = len(markdown_content) / 1024
1428
+ if not args.quiet:
1429
+ print(f" {color.cyan('Writing')} {content_size_kb:.1f}KB markdown via SFTP...", file=sys.stderr)
1402
1430
 
1403
- if write_code != 0:
1404
- print(f" ✗ Failed to write markdown output", file=sys.stderr)
1431
+ try:
1432
+ # Write to temporary local file
1433
+ with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as temp_file:
1434
+ temp_file.write(markdown_content)
1435
+ temp_path = temp_file.name
1436
+
1437
+ # Upload via SFTP (more reliable for large files)
1438
+ await transfer_manager.upload_file(
1439
+ local_path=temp_path,
1440
+ remote_path=remote_output_path,
1441
+ overwrite=True,
1442
+ compress=False,
1443
+ )
1444
+
1445
+ # Cleanup temp file
1446
+ try:
1447
+ os.unlink(temp_path)
1448
+ except Exception:
1449
+ pass
1450
+
1451
+ if not args.quiet:
1452
+ print(f" {color.green('✓')} Markdown written", file=sys.stderr)
1453
+ except Exception as write_exc:
1454
+ if not args.quiet:
1455
+ print(f" ✗ Failed to write markdown: {write_exc}", file=sys.stderr)
1405
1456
  failed += 1
1406
1457
  break
1407
1458
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdify-cli
3
- Version: 3.0.8
3
+ Version: 3.1.0
4
4
  Summary: Convert PDFs and document images into structured Markdown for LLM workflows
5
5
  Author: tiroq
6
6
  License-Expression: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mdify-cli"
3
- version = "3.0.8"
3
+ version = "3.1.0"
4
4
  description = "Convert PDFs and document images into structured Markdown for LLM workflows"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes