witbitz-code 1.6.7__tar.gz → 1.6.8__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 (100) hide show
  1. {witbitz_code-1.6.7/src/witbitz_code.egg-info → witbitz_code-1.6.8}/PKG-INFO +2 -2
  2. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/README.md +1 -1
  3. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/pyproject.toml +1 -1
  4. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/__init__.py +1 -1
  5. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/mkdir.py +13 -3
  6. {witbitz_code-1.6.7 → witbitz_code-1.6.8/src/witbitz_code.egg-info}/PKG-INFO +2 -2
  7. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_dirs.py +11 -1
  8. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/LICENSE +0 -0
  9. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/setup.cfg +0 -0
  10. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/__main__.py +0 -0
  11. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/_js.py +0 -0
  12. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/account.py +0 -0
  13. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/asks.py +0 -0
  14. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/attachments.py +0 -0
  15. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/auto.py +0 -0
  16. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/auto_runner.py +0 -0
  17. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/cli.py +0 -0
  18. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/codekeys.py +0 -0
  19. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/codetools.py +0 -0
  20. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/compress.py +0 -0
  21. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/computers.py +0 -0
  22. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/confidential.py +0 -0
  23. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/confidential_models.json +0 -0
  24. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/connector.py +0 -0
  25. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/devicelink.py +0 -0
  26. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/dirs.py +0 -0
  27. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/folders.py +0 -0
  28. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/gateway.py +0 -0
  29. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/icons/witbitz-code-wsl.ico +0 -0
  30. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/icons/witbitz-code.ico +0 -0
  31. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/keys.py +0 -0
  32. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/link.py +0 -0
  33. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/models.py +0 -0
  34. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/net.py +0 -0
  35. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/offered_models.json +0 -0
  36. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/opencode_plugins/notes-init.md +0 -0
  37. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/opencode_plugins/witbitz-media-server.mjs +0 -0
  38. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/opencode_plugins/witbitz-notes.js +0 -0
  39. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/opencode_plugins/witbitz-progress.js +0 -0
  40. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/outputs.py +0 -0
  41. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/pair.py +0 -0
  42. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/pairings.py +0 -0
  43. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/payers.py +0 -0
  44. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/plugins.py +0 -0
  45. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/policy.py +0 -0
  46. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/ports.py +0 -0
  47. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/qr.py +0 -0
  48. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/receipt.py +0 -0
  49. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/relay.py +0 -0
  50. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/seen.py +0 -0
  51. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/tinfoil_read.py +0 -0
  52. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/tools_catalog.json +0 -0
  53. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/tools_probe.py +0 -0
  54. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/toolsetup.py +0 -0
  55. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/ui.py +0 -0
  56. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/ui_static/app.css +0 -0
  57. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/ui_static/app.js +0 -0
  58. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/ui_static/index.html +0 -0
  59. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/uitools.py +0 -0
  60. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/uninstall.py +0 -0
  61. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/winagent.py +0 -0
  62. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/winbg.py +0 -0
  63. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/winconsole.py +0 -0
  64. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/wizard.py +0 -0
  65. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code/wslstart.py +0 -0
  66. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code.egg-info/SOURCES.txt +0 -0
  67. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code.egg-info/dependency_links.txt +0 -0
  68. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code.egg-info/entry_points.txt +0 -0
  69. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code.egg-info/requires.txt +0 -0
  70. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/src/witbitz_code.egg-info/top_level.txt +0 -0
  71. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_asks.py +0 -0
  72. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_attachments.py +0 -0
  73. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_auto.py +0 -0
  74. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_cli.py +0 -0
  75. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_codekeys.py +0 -0
  76. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_codetools.py +0 -0
  77. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_confidential.py +0 -0
  78. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_connector_e2e.py +0 -0
  79. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_devicelink.py +0 -0
  80. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_files_and_compress.py +0 -0
  81. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_folders.py +0 -0
  82. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_outputs.py +0 -0
  83. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_pair_flows.py +0 -0
  84. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_plugins.py +0 -0
  85. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_policy.py +0 -0
  86. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_ports.py +0 -0
  87. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_ports_win.py +0 -0
  88. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_registry.py +0 -0
  89. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_relay_codec.py +0 -0
  90. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_setup.py +0 -0
  91. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_tools_probe.py +0 -0
  92. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_toolsetup.py +0 -0
  93. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_ui.py +0 -0
  94. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_uitools.py +0 -0
  95. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_uninstall.py +0 -0
  96. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_winagent.py +0 -0
  97. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_winbg.py +0 -0
  98. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_winconsole.py +0 -0
  99. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_windows_files.py +0 -0
  100. {witbitz_code-1.6.7 → witbitz_code-1.6.8}/tests/test_wslstart.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: witbitz-code
3
- Version: 1.6.7
3
+ Version: 1.6.8
4
4
  Summary: Reach OpenCode on this computer from the Witbitz Spaces Code section, through an end-to-end sealed relay.
5
5
  Author: Witbitz
6
6
  License-Expression: MIT
@@ -50,7 +50,7 @@ witbitz-code setup
50
50
  ```
51
51
 
52
52
  `setup` walks five steps and skips whatever is already done: it installs OpenCode if it is missing (it asks first), pairs
53
- this computer (scan the QR code in Spaces → Settings → Privacy & security → Back up & recovery → Add a device — scan its code), which gives the computer **its
53
+ this computer (scan the QR code in Spaces → Settings → Privacy & security → Add a device), which gives the computer **its
54
54
  own keys from your Spaces account**, offers to install the tools the agent uses for audio, video, PDFs and scans (ffmpeg,
55
55
  Poppler, Tesseract, … — with winget, Homebrew or one `sudo apt-get` line; it shows what it will run and asks once), then
56
56
  starts `serve` in the window. Then open Code in Spaces. Leave it running (Ctrl-C
@@ -24,7 +24,7 @@ witbitz-code setup
24
24
  ```
25
25
 
26
26
  `setup` walks five steps and skips whatever is already done: it installs OpenCode if it is missing (it asks first), pairs
27
- this computer (scan the QR code in Spaces → Settings → Privacy & security → Back up & recovery → Add a device — scan its code), which gives the computer **its
27
+ this computer (scan the QR code in Spaces → Settings → Privacy & security → Add a device), which gives the computer **its
28
28
  own keys from your Spaces account**, offers to install the tools the agent uses for audio, video, PDFs and scans (ffmpeg,
29
29
  Poppler, Tesseract, … — with winget, Homebrew or one `sudo apt-get` line; it shows what it will run and asks once), then
30
30
  starts `serve` in the window. Then open Code in Spaces. Leave it running (Ctrl-C
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "witbitz-code"
7
- version = "1.6.7"
7
+ version = "1.6.8"
8
8
  description = "Reach OpenCode on this computer from the Witbitz Spaces Code section, through an end-to-end sealed relay."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -4,4 +4,4 @@ The Python implementation of tools/witbitz-code.mjs (docs/opencode-relay.md phas
4
4
  vectors and cross-implementation tests.
5
5
  """
6
6
 
7
- __version__ = "1.6.7"
7
+ __version__ = "1.6.8"
@@ -12,9 +12,10 @@ import ntpath
12
12
  import os
13
13
  import posixpath
14
14
  import re
15
+ import unicodedata
15
16
 
16
17
  MKDIR_ROUTE = "/witbitz/mkdir"
17
- _CLEAN = re.compile(r"^[A-Za-z0-9_ ()-][A-Za-z0-9._ ()-]*$")
18
+ _PUNCT = set("._ ()-")
18
19
  _WIN_ABS = re.compile(r"^[A-Za-z]:[\\/]")
19
20
 
20
21
 
@@ -22,12 +23,21 @@ def _error(st: int, message: str) -> tuple[int, str]:
22
23
  return st, json.dumps({"error": message}, ensure_ascii=False, separators=(",", ":"))
23
24
 
24
25
 
26
+ def _ok_char(ch: str) -> bool:
27
+ # letters, digits and combining marks of ANY script (Hebrew, Arabic, …) plus . _ space ( ) - — ⇄ code-mkdir.mjs. A control
28
+ # or an invisible direction mark (category C*) is not a letter, so it stays out.
29
+ return ch in _PUNCT or unicodedata.category(ch)[0] in ("L", "N", "M")
30
+
31
+
25
32
  def _clean(n: object) -> bool:
26
- s = str(n or "") if isinstance(n, str) else ""
27
- return bool(s) and len(s) <= 120 and bool(_CLEAN.match(s)) and ".." not in s and not re.search(r"[/\\\0]", s) and not re.search(r"[^ -~]", s)
33
+ s = unicodedata.normalize("NFC", n) if isinstance(n, str) else ""
34
+ return (bool(s) and len(s) <= 120 and s[0] != "." and all(_ok_char(c) for c in s)
35
+ and ".." not in s and not re.search(r"[/\\\0]", s))
28
36
 
29
37
 
30
38
  def mkdir_in(*, root: object, rel: object, name: object) -> tuple[int, str]:
39
+ if isinstance(name, str):
40
+ name = unicodedata.normalize("NFC", name) # one spelling of a name, whatever keyboard typed it
31
41
  if not _clean(name):
32
42
  return _error(400, "a folder name: no /, . or .., and not hidden")
33
43
  win = isinstance(root, str) and bool(_WIN_ABS.match(root))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: witbitz-code
3
- Version: 1.6.7
3
+ Version: 1.6.8
4
4
  Summary: Reach OpenCode on this computer from the Witbitz Spaces Code section, through an end-to-end sealed relay.
5
5
  Author: Witbitz
6
6
  License-Expression: MIT
@@ -50,7 +50,7 @@ witbitz-code setup
50
50
  ```
51
51
 
52
52
  `setup` walks five steps and skips whatever is already done: it installs OpenCode if it is missing (it asks first), pairs
53
- this computer (scan the QR code in Spaces → Settings → Privacy & security → Back up & recovery → Add a device — scan its code), which gives the computer **its
53
+ this computer (scan the QR code in Spaces → Settings → Privacy & security → Add a device), which gives the computer **its
54
54
  own keys from your Spaces account**, offers to install the tools the agent uses for audio, video, PDFs and scans (ffmpeg,
55
55
  Poppler, Tesseract, … — with winget, Homebrew or one `sudo apt-get` line; it shows what it will run and asks once), then
56
56
  starts `serve` in the window. Then open Code in Spaces. Leave it running (Ctrl-C
@@ -52,9 +52,19 @@ def test_the_javascript_lists_the_same():
52
52
 
53
53
 
54
54
  def test_new_folder_names_are_one_clean_leaf_on_both_kinds_of_root(tmp_path):
55
- for bad in ["..", ".x", "a/b", "a\\b", "\\x", "", "a\x00b", "é"]:
55
+ for bad in ["..", ".x", "a/b", "a\\b", "\\x", "", "a\x00b", "a\u202eb"]: # "é" is a letter now (any script, 2026-10-11)
56
56
  assert mkdir.mkdir_in(root=str(tmp_path), rel=".", name=bad)[0] == 400, bad
57
57
  assert mkdir.mkdir_in(root="relative", rel=".", name="x")[0] == 400
58
58
  assert mkdir.mkdir_in(root="C:\\Users\\nobody-here", rel=".", name="x")[0] == 404, "a Windows root is a root (not on this computer)"
59
59
  assert mkdir.mkdir_in(root=str(tmp_path), rel="..", name="x")[0] == 403, "never above the root"
60
60
  assert mkdir.mkdir_in(root=str(tmp_path), rel=".", name="ok one")[0] == 200 and (tmp_path / "ok one").is_dir()
61
+
62
+
63
+ def test_mkdir_any_script_name_is_made(tmp_path):
64
+ # the owner, 2026-10-11: "making a directory in hebrew does not work" (it was ASCII only) — ⇄ tools/code-mkdir.test.mjs
65
+ for name in ["ערך 3", "עבודה (2)", "Ünïcode", "مشروع", "项目-1"]:
66
+ assert mkdir.mkdir_in(root=str(tmp_path), rel=".", name=name)[0] == 200, name
67
+ for bad in ["x\u202eexe", "a\u200fb", "a\u2066b"]:
68
+ assert mkdir.mkdir_in(root=str(tmp_path), rel=".", name=bad)[0] == 400, repr(bad)
69
+ assert mkdir.mkdir_in(root=str(tmp_path), rel=".", name="U\u0308ber")[0] == 200
70
+ assert "\u00dcber" in [p.name for p in tmp_path.iterdir()], "stored composed (NFC)"
File without changes
File without changes