webbee 0.5.0__tar.gz → 0.5.2__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 (139) hide show
  1. {webbee-0.5.0 → webbee-0.5.2}/CHANGELOG.md +14 -0
  2. {webbee-0.5.0 → webbee-0.5.2}/PKG-INFO +4 -3
  3. {webbee-0.5.0 → webbee-0.5.2}/pyproject.toml +3 -3
  4. webbee-0.5.2/src/webbee/__init__.py +1 -0
  5. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/service.py +7 -1
  6. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tools.py +11 -2
  7. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_service.py +17 -0
  8. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tools.py +45 -0
  9. webbee-0.5.0/src/webbee/__init__.py +0 -1
  10. {webbee-0.5.0 → webbee-0.5.2}/.github/workflows/publish.yml +0 -0
  11. {webbee-0.5.0 → webbee-0.5.2}/.gitignore +0 -0
  12. {webbee-0.5.0 → webbee-0.5.2}/LICENSE +0 -0
  13. {webbee-0.5.0 → webbee-0.5.2}/README.md +0 -0
  14. {webbee-0.5.0 → webbee-0.5.2}/install.sh +0 -0
  15. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/__main__.py +0 -0
  16. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/account.py +0 -0
  17. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/active_sessions.py +0 -0
  18. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/banner_art.py +0 -0
  19. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/boot.py +0 -0
  20. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/browser.py +0 -0
  21. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/checkpoints.py +0 -0
  22. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/cli.py +0 -0
  23. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/clipboard.py +0 -0
  24. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/clipboard_read.py +0 -0
  25. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/clipboard_session.py +0 -0
  26. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/coding_context.py +0 -0
  27. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/commands.py +0 -0
  28. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/config.py +0 -0
  29. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/consent.py +0 -0
  30. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/details.py +0 -0
  31. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/events.py +0 -0
  32. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/frames.py +0 -0
  33. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/home.py +0 -0
  34. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/home_view.py +0 -0
  35. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/http.py +0 -0
  36. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/instance_lock.py +0 -0
  37. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/__init__.py +0 -0
  38. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/graph.py +0 -0
  39. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/outline.py +0 -0
  40. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/query.py +0 -0
  41. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/store.py +0 -0
  42. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/vectors.py +0 -0
  43. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/intel/watch.py +0 -0
  44. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/media.py +0 -0
  45. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/modals.py +0 -0
  46. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/mode_store.py +0 -0
  47. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/native_files.py +0 -0
  48. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/native_read.py +0 -0
  49. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/newtab_mode.py +0 -0
  50. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/output_pane.py +0 -0
  51. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/pricing_constants.py +0 -0
  52. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/queue_panel.py +0 -0
  53. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/reflow.py +0 -0
  54. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/remote.py +0 -0
  55. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/render.py +0 -0
  56. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/repl.py +0 -0
  57. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/repo.py +0 -0
  58. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/selection.py +0 -0
  59. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/session.py +0 -0
  60. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/sessions.py +0 -0
  61. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/sizing.py +0 -0
  62. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/slots.py +0 -0
  63. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/steer.py +0 -0
  64. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/stream.py +0 -0
  65. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tab_store.py +0 -0
  66. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tabs.py +0 -0
  67. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/thread.py +0 -0
  68. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tier_store.py +0 -0
  69. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/todo_panel.py +0 -0
  70. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tokens.py +0 -0
  71. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/tui.py +0 -0
  72. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/update.py +0 -0
  73. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/urlopen.py +0 -0
  74. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/wallet.py +0 -0
  75. {webbee-0.5.0 → webbee-0.5.2}/src/webbee/worktrees.py +0 -0
  76. {webbee-0.5.0 → webbee-0.5.2}/tests/__init__.py +0 -0
  77. {webbee-0.5.0 → webbee-0.5.2}/tests/conftest.py +0 -0
  78. {webbee-0.5.0 → webbee-0.5.2}/tests/test_account.py +0 -0
  79. {webbee-0.5.0 → webbee-0.5.2}/tests/test_active_sessions.py +0 -0
  80. {webbee-0.5.0 → webbee-0.5.2}/tests/test_audit_2026_07_25_findings.py +0 -0
  81. {webbee-0.5.0 → webbee-0.5.2}/tests/test_checkpoints.py +0 -0
  82. {webbee-0.5.0 → webbee-0.5.2}/tests/test_cli.py +0 -0
  83. {webbee-0.5.0 → webbee-0.5.2}/tests/test_clipboard.py +0 -0
  84. {webbee-0.5.0 → webbee-0.5.2}/tests/test_clipboard_read.py +0 -0
  85. {webbee-0.5.0 → webbee-0.5.2}/tests/test_coding_context.py +0 -0
  86. {webbee-0.5.0 → webbee-0.5.2}/tests/test_commands.py +0 -0
  87. {webbee-0.5.0 → webbee-0.5.2}/tests/test_config.py +0 -0
  88. {webbee-0.5.0 → webbee-0.5.2}/tests/test_cpc_contract_stable.py +0 -0
  89. {webbee-0.5.0 → webbee-0.5.2}/tests/test_cpc_outline.py +0 -0
  90. {webbee-0.5.0 → webbee-0.5.2}/tests/test_cross_loop_token.py +0 -0
  91. {webbee-0.5.0 → webbee-0.5.2}/tests/test_details.py +0 -0
  92. {webbee-0.5.0 → webbee-0.5.2}/tests/test_events.py +0 -0
  93. {webbee-0.5.0 → webbee-0.5.2}/tests/test_freeze_fix.py +0 -0
  94. {webbee-0.5.0 → webbee-0.5.2}/tests/test_home.py +0 -0
  95. {webbee-0.5.0 → webbee-0.5.2}/tests/test_home_view.py +0 -0
  96. {webbee-0.5.0 → webbee-0.5.2}/tests/test_inspect_page.py +0 -0
  97. {webbee-0.5.0 → webbee-0.5.2}/tests/test_instance_lock.py +0 -0
  98. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_chunker.py +0 -0
  99. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_embed.py +0 -0
  100. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_graph.py +0 -0
  101. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_no_numpy.py +0 -0
  102. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_query.py +0 -0
  103. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_store.py +0 -0
  104. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_vectors.py +0 -0
  105. {webbee-0.5.0 → webbee-0.5.2}/tests/test_intel_watch.py +0 -0
  106. {webbee-0.5.0 → webbee-0.5.2}/tests/test_marathon.py +0 -0
  107. {webbee-0.5.0 → webbee-0.5.2}/tests/test_modals_e2e.py +0 -0
  108. {webbee-0.5.0 → webbee-0.5.2}/tests/test_mode_store.py +0 -0
  109. {webbee-0.5.0 → webbee-0.5.2}/tests/test_native_files.py +0 -0
  110. {webbee-0.5.0 → webbee-0.5.2}/tests/test_newtab_mode.py +0 -0
  111. {webbee-0.5.0 → webbee-0.5.2}/tests/test_packaging.py +0 -0
  112. {webbee-0.5.0 → webbee-0.5.2}/tests/test_queue_manage_0337.py +0 -0
  113. {webbee-0.5.0 → webbee-0.5.2}/tests/test_reflow.py +0 -0
  114. {webbee-0.5.0 → webbee-0.5.2}/tests/test_render.py +0 -0
  115. {webbee-0.5.0 → webbee-0.5.2}/tests/test_repl.py +0 -0
  116. {webbee-0.5.0 → webbee-0.5.2}/tests/test_repo.py +0 -0
  117. {webbee-0.5.0 → webbee-0.5.2}/tests/test_session.py +0 -0
  118. {webbee-0.5.0 → webbee-0.5.2}/tests/test_sessions.py +0 -0
  119. {webbee-0.5.0 → webbee-0.5.2}/tests/test_sizing.py +0 -0
  120. {webbee-0.5.0 → webbee-0.5.2}/tests/test_slots.py +0 -0
  121. {webbee-0.5.0 → webbee-0.5.2}/tests/test_steer.py +0 -0
  122. {webbee-0.5.0 → webbee-0.5.2}/tests/test_stream.py +0 -0
  123. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tab_restore_0337.py +0 -0
  124. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tabs.py +0 -0
  125. {webbee-0.5.0 → webbee-0.5.2}/tests/test_terminal_ux_0336.py +0 -0
  126. {webbee-0.5.0 → webbee-0.5.2}/tests/test_thread.py +0 -0
  127. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tier_store.py +0 -0
  128. {webbee-0.5.0 → webbee-0.5.2}/tests/test_todo_panel.py +0 -0
  129. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tokens.py +0 -0
  130. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tools_vision_websearch.py +0 -0
  131. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tui.py +0 -0
  132. {webbee-0.5.0 → webbee-0.5.2}/tests/test_tui_hardening.py +0 -0
  133. {webbee-0.5.0 → webbee-0.5.2}/tests/test_update.py +0 -0
  134. {webbee-0.5.0 → webbee-0.5.2}/tests/test_update_badge_0340.py +0 -0
  135. {webbee-0.5.0 → webbee-0.5.2}/tests/test_urlopen.py +0 -0
  136. {webbee-0.5.0 → webbee-0.5.2}/tests/test_version.py +0 -0
  137. {webbee-0.5.0 → webbee-0.5.2}/tests/test_version_badge_corner.py +0 -0
  138. {webbee-0.5.0 → webbee-0.5.2}/tests/test_wallet.py +0 -0
  139. {webbee-0.5.0 → webbee-0.5.2}/tests/test_worktrees.py +0 -0
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.2
4
+
5
+ * **One server, one machine**: a repo synced over your own `ssh` reports every host key of that server your
6
+ `known_hosts` trusts (RSA, ECDSA, ed25519), so the panel and your terminal see one machine, not two.
7
+ * Requires `imperal-sdk >= 6.3.4` (10 MB a file, batched syncs).
8
+
9
+ ## 0.5.1
10
+
11
+ **Security fixes for the remote and local code sync.**
12
+
13
+ * The system-`sftp` fallback refuses a remote path with a control character: a file name from a server can
14
+ no longer start a second batch command (sftp's `!` runs a local shell).
15
+ * The local sync never reads a symlink or a path that resolves outside the workspace.
16
+
3
17
  ## 0.5.0
4
18
 
5
19
  **Webbee v0.5.0: one code graph for every source.**
@@ -1,13 +1,14 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: webbee
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
5
5
  Project-URL: Homepage, https://imperal.io
6
6
  Project-URL: Documentation, https://docs.imperal.io
7
7
  Project-URL: Repository, https://github.com/imperalcloud/webbee-code
8
8
  Project-URL: Issues, https://github.com/imperalcloud/webbee-code/issues
9
9
  Project-URL: Protocol, https://icnli.org
10
- Author-email: "Imperal, Inc." <hello@imperal.io>
10
+ Author: Imperal, Inc.
11
+ Author-email: Valentin Scerbacov <valentin@imperal.io>
11
12
  License-Expression: GPL-3.0-or-later
12
13
  License-File: LICENSE
13
14
  Keywords: ai-agent,cli,coding-agent,icnli,imperal,llm,terminal,webbee
@@ -29,7 +30,7 @@ Requires-Dist: asyncssh>=2.14
29
30
  Requires-Dist: httpx-sse>=0.4
30
31
  Requires-Dist: httpx>=0.27
31
32
  Requires-Dist: imperal-mcp>=0.5.0
32
- Requires-Dist: imperal-sdk>=6.3.2
33
+ Requires-Dist: imperal-sdk>=6.3.4
33
34
  Requires-Dist: model2vec>=0.8.2; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
34
35
  Requires-Dist: numpy>=2; sys_platform != 'darwin' or platform_machine != 'x86_64' or python_version < '3.13'
35
36
  Requires-Dist: prompt-toolkit>=3
@@ -1,15 +1,15 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
5
5
  readme = "README.md"
6
6
  license = "GPL-3.0-or-later"
7
7
  license-files = ["LICENSE"]
8
8
  requires-python = ">=3.9"
9
- authors = [{ name = "Imperal, Inc.", email = "hello@imperal.io" }]
9
+ authors = [{ name = "Valentin Scerbacov", email = "valentin@imperal.io" }, { name = "Imperal, Inc." }]
10
10
  dependencies = [
11
11
  "imperal-mcp>=0.5.0",
12
- "imperal-sdk>=6.3.2",
12
+ "imperal-sdk>=6.3.4",
13
13
  "asyncssh>=2.14",
14
14
  "httpx>=0.27",
15
15
  "httpx-sse>=0.4",
@@ -0,0 +1 @@
1
+ __version__ = "0.5.2"
@@ -291,9 +291,15 @@ class IntelService:
291
291
 
292
292
  async def read(paths):
293
293
  got = {}
294
+ base = os.path.realpath(self.root)
294
295
  for p in paths:
296
+ full = os.path.join(self.root, p)
297
+ # never a link, never a path that resolves outside the workspace
298
+ real = os.path.realpath(full)
299
+ if os.path.islink(full) or not (real == base or real.startswith(base + os.sep)):
300
+ continue
295
301
  try:
296
- with open(os.path.join(self.root, p), "rb") as f:
302
+ with open(full, "rb") as f:
297
303
  got[p] = f.read(indexer.MAX_FILE_BYTES + 1)
298
304
  except OSError:
299
305
  continue
@@ -1012,7 +1012,12 @@ class SystemSftp:
1012
1012
 
1013
1013
  @staticmethod
1014
1014
  def _quote(path: str) -> str:
1015
- return '"' + str(path).replace("\\", "\\\\").replace('"', '\\"') + '"'
1015
+ # one batch line per command: a control character in a path (a newline names a second command,
1016
+ # and sftp's "!" runs a LOCAL shell) is refused -- names come from the server's listing
1017
+ p = str(path)
1018
+ if any(ord(c) < 32 or ord(c) == 127 for c in p):
1019
+ raise OSError("refused: a control character in a remote path")
1020
+ return '"' + p.replace("\\", "\\\\").replace('"', '\\"') + '"'
1016
1021
 
1017
1022
  def _mtime(self, mon: str, day: str, when: str) -> float:
1018
1023
  import datetime
@@ -1106,7 +1111,11 @@ async def _open_sftp(host: str):
1106
1111
  except Exception as e: # unreachable, refused, a host key that changed: said, never guessed past
1107
1112
  return None, None, [], f"{type(e).__name__}: {e}"
1108
1113
  key = conn.get_server_host_key()
1109
- return await conn.start_sftp_client(), conn, ([key.get_fingerprint("sha256")] if key else []), ""
1114
+ # the negotiated key and the host's other keys the user's known_hosts trusts: a server has several (RSA,
1115
+ # ECDSA, ed25519) and another source may have met it by a different one -- one machine, never two
1116
+ fps = sorted({key.get_fingerprint("sha256")} if key else set())
1117
+ fps = sorted(set(fps) | set(known_fingerprints(host)))
1118
+ return await conn.start_sftp_client(), conn, fps, ""
1110
1119
 
1111
1120
 
1112
1121
  async def remote_sync(client, host: str, root: str, paths: list) -> dict:
@@ -270,3 +270,20 @@ def test_the_build_sync_carries_the_history_links(tmp_path, monkeypatch):
270
270
  svc.build()
271
271
  svc.attach_vault(lambda: cloud)
272
272
  assert cloud.calls[-1][1]["measured"][0]["dst_path"] == "b.py"
273
+
274
+
275
+ def test_a_link_leading_out_of_the_workspace_is_never_read(tmp_path, monkeypatch):
276
+ """Security review of 0.5.0: the local sync reads no symlink and nothing that resolves outside the root."""
277
+ import os
278
+ outside = tmp_path.parent / f"{tmp_path.name}-outside.py"
279
+ outside.write_text("SECRET = 'outside the workspace'\n")
280
+ (tmp_path / "a.py").write_text("def alpha():\n return 1\n")
281
+ os.symlink(outside, tmp_path / "leak.py")
282
+ svc = IntelService(str(tmp_path), "rk", cache_dir=str(tmp_path / "c"))
283
+ _synced(svc, monkeypatch)
284
+ cloud = _Cloud()
285
+ svc.build()
286
+ svc.attach_vault(lambda: cloud)
287
+ sent = [c[1] for c in cloud.calls if "files" in c[1]]
288
+ assert sent and all(f["path"] != "leak.py" for c in sent for f in c["files"])
289
+ assert "outside the workspace" not in repr(cloud.calls)
@@ -869,3 +869,48 @@ def test_system_sftp_answers_lstat_and_realpath_from_the_server():
869
869
  asyncio.run(s.realpath("/r/a.py"))
870
870
  asyncio.run(s.lstat("/r/a.py"))
871
871
  assert len(calls) == n # cached: no new sftp process
872
+
873
+
874
+ def test_system_sftp_refuses_a_control_character_in_a_remote_path():
875
+ """Security review of 0.5.0: a newline would start a second sftp batch command ("!" runs a local shell)."""
876
+ import asyncio
877
+ import pytest
878
+ from webbee.tools import SystemSftp
879
+ ran = []
880
+
881
+ async def batch(cmds):
882
+ ran.append(cmds)
883
+ return ""
884
+ s = SystemSftp("h")
885
+ s._batch = batch
886
+ for bad in ("/r/a.py\n!touch /tmp/pwned", "/r/a\rb", "/r/\x00x"):
887
+ with pytest.raises(OSError):
888
+ asyncio.run(s.readdir(bad))
889
+ assert ran == []
890
+
891
+
892
+ def test_an_asyncssh_connect_reports_every_key_of_the_host_the_user_trusts(monkeypatch):
893
+ """Live 2026-10-20: the panel met whm-jump by RSA, the user's ssh by ed25519 -- two machine nodes. The
894
+ client sends the negotiated key and the host's other known_hosts keys: one machine."""
895
+ import asyncio
896
+ import asyncssh
897
+ import webbee.tools as t
898
+ monkeypatch.setattr(t, "master_open", lambda host: False)
899
+ monkeypatch.setattr(t, "known_fingerprints", lambda host: ["SHA256:rsa", "SHA256:ecdsa", "SHA256:ed"])
900
+
901
+ class Key:
902
+ def get_fingerprint(self, alg):
903
+ return "SHA256:ed"
904
+
905
+ class Conn:
906
+ def get_server_host_key(self):
907
+ return Key()
908
+
909
+ async def start_sftp_client(self):
910
+ return "sftp"
911
+
912
+ async def connect(host, **kw):
913
+ return Conn()
914
+ monkeypatch.setattr(asyncssh, "connect", connect)
915
+ sftp, conn, fps, why = asyncio.run(t._open_sftp("whm-jump"))
916
+ assert sftp == "sftp" and why == "" and fps == ["SHA256:ecdsa", "SHA256:ed", "SHA256:rsa"]
@@ -1 +0,0 @@
1
- __version__ = "0.5.0"
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
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
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
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
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
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
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
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
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
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