aioli-cli 0.1.45__tar.gz → 0.1.46__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 (118) hide show
  1. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/PKG-INFO +1 -1
  2. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/pyproject.toml +1 -1
  3. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_cli/main.py +4 -2
  4. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/fetch.py +34 -4
  5. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/readiness.py +25 -1
  6. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/models_data.py +3 -3
  7. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/models_data.py +2 -2
  8. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/.gitignore +0 -0
  9. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/LICENSE +0 -0
  10. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/README.md +0 -0
  11. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_cli/_plugins.py +0 -0
  12. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_cli/fetch_ui.py +0 -0
  13. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_cli/readiness.py +0 -0
  14. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_cli/registry.py +0 -0
  15. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_cli/setup.py +0 -0
  16. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_cli/status.py +0 -0
  17. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/__init__.py +0 -0
  18. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/advise.py +0 -0
  19. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/config.py +0 -0
  20. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/doctor.py +0 -0
  21. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/probe.py +0 -0
  22. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_contract/ui.py +0 -0
  23. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/__init__.py +0 -0
  24. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/cli.py +0 -0
  25. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/companions.py +0 -0
  26. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/config.py +0 -0
  27. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/data/models.toml +0 -0
  28. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/doctor.py +0 -0
  29. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/gpu.py +0 -0
  30. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/launcher.py +0 -0
  31. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/models.py +0 -0
  32. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/plugin.py +0 -0
  33. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/presets.py +0 -0
  34. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/progress.py +0 -0
  35. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/stub.py +0 -0
  36. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_image/ui.py +0 -0
  37. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_master/cli.py +0 -0
  38. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_master/doctor.py +0 -0
  39. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_master/ops.py +0 -0
  40. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_master/plugin.py +0 -0
  41. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/__init__.py +0 -0
  42. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/__init__.py +0 -0
  43. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/acestep.py +0 -0
  44. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/base.py +0 -0
  45. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/llm_caption.py +0 -0
  46. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/minimax_comfy.py +0 -0
  47. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/process.py +0 -0
  48. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/stub.py +0 -0
  49. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/backends/yue2.py +0 -0
  50. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/cli_commands/__init__.py +0 -0
  51. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/cli_commands/backend_cmd.py +0 -0
  52. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/cli_commands/generate_cmd.py +0 -0
  53. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/cli_commands/inspire_cmd.py +0 -0
  54. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/cli_commands/models_cmd.py +0 -0
  55. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/client.py +0 -0
  56. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/config.py +0 -0
  57. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/config_data/default.toml +0 -0
  58. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/core/__init__.py +0 -0
  59. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/core/formats.py +0 -0
  60. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/core/generate.py +0 -0
  61. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/core/jobs.py +0 -0
  62. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/core/layout.py +0 -0
  63. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/doctor.py +0 -0
  64. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/generator.py +0 -0
  65. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/history.py +0 -0
  66. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/jsonstore.py +0 -0
  67. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/models.py +0 -0
  68. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/paths.py +0 -0
  69. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/plugin.py +0 -0
  70. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/presets.py +0 -0
  71. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/prompts.py +0 -0
  72. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/server.py +0 -0
  73. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/transport/__init__.py +0 -0
  74. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/transport/client.py +0 -0
  75. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_music/tui.py +0 -0
  76. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_podcast/cast.py +0 -0
  77. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_podcast/cli.py +0 -0
  78. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_podcast/doctor.py +0 -0
  79. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_podcast/episode.py +0 -0
  80. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_podcast/feed.py +0 -0
  81. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_podcast/plugin.py +0 -0
  82. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_podcast/script.py +0 -0
  83. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/__init__.py +0 -0
  84. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/cli.py +0 -0
  85. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/doctor.py +0 -0
  86. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/models_data.py +0 -0
  87. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/paths.py +0 -0
  88. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/plugin.py +0 -0
  89. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/stub.py +0 -0
  90. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/wgp.py +0 -0
  91. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_video/worker.py +0 -0
  92. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/__init__.py +0 -0
  93. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/cli/doctor.py +0 -0
  94. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/cli/engines_cmd.py +0 -0
  95. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/cli/generate.py +0 -0
  96. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/cli/models_cmd.py +0 -0
  97. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/cli/profiles.py +0 -0
  98. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/config/__init__.py +0 -0
  99. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/config/settings.py +0 -0
  100. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/core/__init__.py +0 -0
  101. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/core/generation.py +0 -0
  102. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/core/model_manager.py +0 -0
  103. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/core/profiles.py +0 -0
  104. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/core/registry.py +0 -0
  105. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/core/types.py +0 -0
  106. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/devices.py +0 -0
  107. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/doctor.py +0 -0
  108. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/__init__.py +0 -0
  109. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/chatterbox.py +0 -0
  110. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/kokoro.py +0 -0
  111. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/moss.py +0 -0
  112. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/piper.py +0 -0
  113. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/qwen3.py +0 -0
  114. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/stub.py +0 -0
  115. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/voxcpm.py +0 -0
  116. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/engines/xtts.py +0 -0
  117. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/models_data.py +0 -0
  118. {aioli_cli-0.1.45 → aioli_cli-0.1.46}/src/aioli_voice/plugin.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: aioli-cli
3
- Version: 0.1.45
3
+ Version: 0.1.46
4
4
  Summary: aioli — local-first AI media suite CLI (images, video, music, voice, podcast)
5
5
  Author: aioli contributors
6
6
  License: MIT
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "aioli-cli"
3
- version = "0.1.45"
3
+ version = "0.1.46"
4
4
  description = "aioli — local-first AI media suite CLI (images, video, music, voice, podcast)"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -275,8 +275,10 @@ def models_list(
275
275
  bin_dir = default_bin_dir()
276
276
  wanted = _wanted(plugin, backend)
277
277
  roots = _plugin_roots(wanted, cfg, models_root)
278
- distinct = ", ".join(sorted({str(r) for r in roots.values()}))
279
- table = Table(title=f"aioli models → {distinct}")
278
+ distinct_roots = sorted({str(r) for r in roots.values()})
279
+ for root_str in distinct_roots:
280
+ console.print(f"📁 {root_str}")
281
+ table = Table(title="aioli models")
280
282
  table.add_column("plugin")
281
283
  table.add_column("engine")
282
284
  table.add_column("kind")
@@ -39,6 +39,36 @@ from typing import Any, Protocol
39
39
  from urllib.error import HTTPError
40
40
  from urllib.parse import urlsplit
41
41
 
42
+ # O_NOFOLLOW no existe en Windows (NT): getattr portatil. En NT la proteccion
43
+ # symlink se cubre por la verificacion post-escritura ya existente
44
+ # (_read_lock/is_symlink + chequeo del .part antes de abrir + recibo).
45
+ _O_NOFOLLOW = getattr(os, "O_NOFOLLOW", 0)
46
+
47
+
48
+ def _is_executable(path: Path) -> bool:
49
+ """Ejecutabilidad portatil para recibos.
50
+
51
+ POSIX: bit X como hoy. NT: os.access(X_OK) es True para cualquier
52
+ fichero, asi que ademas se exige cabecera PE ("MZ") o extension
53
+ .exe/.bat. Sin esto, verify venderia "ok" con motor falso en Windows.
54
+ """
55
+ try:
56
+ if not path.is_file():
57
+ return False
58
+ if os.name != "nt":
59
+ return bool(os.access(path, os.X_OK))
60
+ if not os.access(path, os.X_OK):
61
+ return False
62
+ if path.suffix.lower() in (".exe", ".bat"):
63
+ return True
64
+ try:
65
+ with open(path, "rb") as fh:
66
+ return fh.read(2) == b"MZ"
67
+ except OSError:
68
+ return False
69
+ except OSError:
70
+ return False
71
+
42
72
 
43
73
  class Progress(Protocol):
44
74
  def update(self, path: Path, done_bytes: int, total_bytes: int) -> None: ...
@@ -189,7 +219,7 @@ def _dest_lock(lock_path: Path, timeout: float = _LOCK_TIMEOUT) -> Iterator[None
189
219
  deadline = time.monotonic() + timeout
190
220
  while True:
191
221
  try:
192
- fd = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY | os.O_NOFOLLOW)
222
+ fd = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY | _O_NOFOLLOW)
193
223
  with os.fdopen(fd, "wb") as fh:
194
224
  fh.write(me)
195
225
  break
@@ -217,7 +247,7 @@ def _dest_lock(lock_path: Path, timeout: float = _LOCK_TIMEOUT) -> Iterator[None
217
247
  while not stop.wait(interval):
218
248
  fresh = f"{os.getpid()} {time.time()}".encode()
219
249
  try:
220
- fd = os.open(lock_path, os.O_WRONLY | os.O_TRUNC | os.O_NOFOLLOW)
250
+ fd = os.open(lock_path, os.O_WRONLY | os.O_TRUNC | _O_NOFOLLOW)
221
251
  with os.fdopen(fd, "wb") as fh:
222
252
  fh.write(fresh)
223
253
  except OSError:
@@ -446,7 +476,7 @@ def _verify_bundle(entry: ModelFile, dest: Path, quick: bool) -> str:
446
476
  return "corrupt"
447
477
  if entry.executable:
448
478
  try:
449
- if not os.access(exe, os.X_OK):
479
+ if not _is_executable(exe):
450
480
  return "corrupt"
451
481
  except OSError:
452
482
  return "corrupt"
@@ -760,7 +790,7 @@ def _fetch_attempt(
760
790
  total = entry_size or ((expect + start) if expect is not None else 0)
761
791
  # O_NOFOLLOW: si entre el chequeo y la apertura alguien planta
762
792
  # un symlink, la apertura falla (ELOOP) en vez de escribir fuera.
763
- flags = os.O_WRONLY | os.O_CREAT | os.O_NOFOLLOW
793
+ flags = os.O_WRONLY | os.O_CREAT | _O_NOFOLLOW
764
794
  flags |= os.O_APPEND if start > 0 else os.O_TRUNC
765
795
  try:
766
796
  fd = os.open(part, flags, 0o644)
@@ -140,11 +140,35 @@ def check_sdk(spec: BackendSpec) -> ProbeOutcome:
140
140
  return ProbeOutcome(Readiness.DESCONOCIDO, f"{target or spec.backend} no instalado{extra}")
141
141
 
142
142
 
143
+ def _nt_executable(path: Path) -> bool:
144
+ """True si el fichero parece ejecutable Windows (MZ o .exe/.bat)."""
145
+ if path.suffix.lower() in (".exe", ".bat"):
146
+ return True
147
+ try:
148
+ with open(path, "rb") as fh:
149
+ return fh.read(2) == b"MZ"
150
+ except OSError:
151
+ return False
152
+
153
+
143
154
  def check_exec_bit(path: Path) -> ProbeOutcome:
144
- """Bit X vía stat (estable V3: sin bit X es desconocido, nunca éxito)."""
155
+ """Bit X vía stat (estable V3: sin bit X es desconocido, nunca éxito).
156
+
157
+ NT: os.access(X_OK) es True para cualquier fichero, así que además se
158
+ exige cabecera PE ("MZ") o extensión .exe/.bat. POSIX no cambia (bit X).
159
+ """
145
160
  try:
146
161
  if not path.is_file():
147
162
  return ProbeOutcome(Readiness.DESCONOCIDO, f"{path} no es fichero")
163
+ if os.name == "nt":
164
+ if not os.access(path, os.X_OK):
165
+ return ProbeOutcome(Readiness.DESCONOCIDO, f"{path} sin bit X")
166
+ if _nt_executable(path):
167
+ return ProbeOutcome(Readiness.MOTOR, f"{path} ejecutable")
168
+ return ProbeOutcome(
169
+ Readiness.DESCONOCIDO,
170
+ f"{path} no ejecutable en Windows (sin MZ ni .exe/.bat)",
171
+ )
148
172
  if os.access(path, os.X_OK):
149
173
  return ProbeOutcome(Readiness.MOTOR, f"{path} ejecutable")
150
174
  return ProbeOutcome(Readiness.DESCONOCIDO, f"{path} sin bit X")
@@ -133,9 +133,9 @@ MODELS: list[dict] = [
133
133
  {
134
134
  "name": "sd-cli runtime (linux x86_64 CUDA, arch 75-120)",
135
135
  "rel": "sd-cli/sd-cli",
136
- "url": "https://github.com/damenordev/aioli-runtimes/releases/download/runtimes-v4/sd-cli-linux-x86_64-cuda.tar.gz",
137
- "sha256": "1bc6af8031fb5e9aef6756a8eeebf9112978ae1c8928c8e2dd8dd210f3087777",
138
- "size": 583443002,
136
+ "url": "https://github.com/damenordev/aioli-runtimes/releases/download/runtimes-v5/sd-cli-linux-x86_64-cuda.tar.gz",
137
+ "sha256": "4707cc3f064c44666100b3c6133a1a966b62d5a4035aab118bf7bcc0fac705ac",
138
+ "size": 577608971,
139
139
  "backend": "",
140
140
  "role": "runtime",
141
141
  "kind": "runtime",
@@ -14,7 +14,7 @@ MODELS: list[dict] = [
14
14
  {
15
15
  "name": "ace-server runtime (linux x86_64 CUDA)",
16
16
  "rel": "ace-server/ace-server",
17
- "url": "https://github.com/damenordev/aioli-runtimes/releases/download/runtimes-v4/ace-server-linux-x86_64-cuda.tar.gz",
17
+ "url": "https://github.com/damenordev/aioli-runtimes/releases/download/runtimes-v5/ace-server-linux-x86_64-cuda.tar.gz",
18
18
  "sha256": "24db1841a34f927f40efa3281e5a9d7095108ee2f7e79acfa39a33fe5e80be54",
19
19
  "size": 564081922,
20
20
  "backend": "acestep",
@@ -72,7 +72,7 @@ MODELS: list[dict] = [
72
72
  {
73
73
  "name": "audiocpp_cli runtime (linux x86_64 CUDA, motor yue2)",
74
74
  "rel": "audiocpp/audiocpp_cli",
75
- "url": "https://github.com/damenordev/aioli-runtimes/releases/download/runtimes-v4/audiocpp-linux-x86_64-cuda.tar.gz",
75
+ "url": "https://github.com/damenordev/aioli-runtimes/releases/download/runtimes-v5/audiocpp-linux-x86_64-cuda.tar.gz",
76
76
  "sha256": "8400328308f7b3a3174c0306c5be13affd1da47f84a52f18f25d4c137707f2da",
77
77
  "size": 677061646,
78
78
  "backend": "yue2",
File without changes
File without changes
File without changes