tano.page 0.1.0a1__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.
@@ -0,0 +1,15 @@
1
+ node_modules/
2
+ dist/
3
+ target/
4
+ .DS_Store
5
+ *.log
6
+ .env
7
+ .env.*
8
+ !.env.example
9
+ test-results/
10
+ artifacts/
11
+ apps/client/src-tauri/gen/android/app/src/main/jniLibs/
12
+ apps/client/src-tauri/gen/android/app/src/main/assets/
13
+ apps/client/src-tauri/gen/android/app/tauri.build.gradle.kts
14
+ apps/client/src-tauri/gen/android/app/tauri.properties
15
+ data/
@@ -0,0 +1,5 @@
1
+ Copyright (c) 2026 dramwig. All rights reserved.
2
+
3
+ This package is not released under an open source license. Publication does
4
+ not grant permission to copy, modify, redistribute, or sublicense this work
5
+ except as otherwise permitted by applicable law or a separate agreement.
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.5
2
+ Name: tano.page
3
+ Version: 0.1.0a1
4
+ Summary: Tano Markdown workspace protocol helpers: content hashes, short IDs and portable path classification.
5
+ Author: dramwig
6
+ License-Expression: LicenseRef-Proprietary
7
+ License-File: LICENSE
8
+ Keywords: markdown,protocol,tano,workspace
9
+ Classifier: Development Status :: 3 - Alpha
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+
15
+ # Tano Workspace Protocol Helpers
16
+
17
+ Tano - Humans and agents, on the same page.
18
+
19
+ This preview Python library provides working helpers for Tano's local Markdown
20
+ workspace protocol. It is not the desktop application or a sync client.
21
+ PyPI normalizes the distribution name `tano.page` to `tano-page`; import it as
22
+ `tano_page`.
23
+
24
+ ```python
25
+ from tano_page import content_hash, is_short_id, is_workspace_data_path
26
+
27
+ revision = content_hash(b"# Notes\n")
28
+ assert len(revision) == 64
29
+ assert is_short_id("a1b2c3")
30
+ assert is_workspace_data_path(".tano/objects/a1b2c3.tano.json")
31
+ assert not is_workspace_data_path(".tano/config.json")
32
+ ```
33
+
34
+ - `content_hash` returns a lowercase SHA-256 of the exact bytes, without newline
35
+ or Unicode normalization.
36
+ - `is_short_id` recognizes six lowercase hexadecimal characters. These IDs are
37
+ separate from workspace UUIDs.
38
+ - `is_workspace_internal_path` identifies `.tano` paths.
39
+ - `is_workspace_data_path` classifies portable object, capability, relation,
40
+ and conflict paths. Workspace identity configuration is excluded.
41
+
42
+ Path classification is not path validation or filesystem authorization. Callers
43
+ must validate relative paths, enforce workspace boundaries, reject unsafe
44
+ symlinks, and compare the base hash before writing. The library performs no
45
+ filesystem or network operations, has no runtime dependencies, and performs
46
+ no actions at installation time.
47
+
48
+ Copyright dramwig. All rights reserved. See LICENSE; no open source license
49
+ has been granted.
@@ -0,0 +1,35 @@
1
+ # Tano Workspace Protocol Helpers
2
+
3
+ Tano - Humans and agents, on the same page.
4
+
5
+ This preview Python library provides working helpers for Tano's local Markdown
6
+ workspace protocol. It is not the desktop application or a sync client.
7
+ PyPI normalizes the distribution name `tano.page` to `tano-page`; import it as
8
+ `tano_page`.
9
+
10
+ ```python
11
+ from tano_page import content_hash, is_short_id, is_workspace_data_path
12
+
13
+ revision = content_hash(b"# Notes\n")
14
+ assert len(revision) == 64
15
+ assert is_short_id("a1b2c3")
16
+ assert is_workspace_data_path(".tano/objects/a1b2c3.tano.json")
17
+ assert not is_workspace_data_path(".tano/config.json")
18
+ ```
19
+
20
+ - `content_hash` returns a lowercase SHA-256 of the exact bytes, without newline
21
+ or Unicode normalization.
22
+ - `is_short_id` recognizes six lowercase hexadecimal characters. These IDs are
23
+ separate from workspace UUIDs.
24
+ - `is_workspace_internal_path` identifies `.tano` paths.
25
+ - `is_workspace_data_path` classifies portable object, capability, relation,
26
+ and conflict paths. Workspace identity configuration is excluded.
27
+
28
+ Path classification is not path validation or filesystem authorization. Callers
29
+ must validate relative paths, enforce workspace boundaries, reject unsafe
30
+ symlinks, and compare the base hash before writing. The library performs no
31
+ filesystem or network operations, has no runtime dependencies, and performs
32
+ no actions at installation time.
33
+
34
+ Copyright dramwig. All rights reserved. See LICENSE; no open source license
35
+ has been granted.
@@ -0,0 +1,25 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27,<2"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "tano.page"
7
+ version = "0.1.0a1"
8
+ description = "Tano Markdown workspace protocol helpers: content hashes, short IDs and portable path classification."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "LicenseRef-Proprietary"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "dramwig" }]
14
+ keywords = ["tano", "markdown", "workspace", "protocol"]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Programming Language :: Python :: 3",
18
+ "Operating System :: OS Independent",
19
+ ]
20
+
21
+ [tool.hatch.build.targets.wheel]
22
+ packages = ["src/tano_page"]
23
+
24
+ [tool.hatch.build.targets.sdist]
25
+ include = ["src/", "tests/", "README.md", "LICENSE", "pyproject.toml"]
@@ -0,0 +1,24 @@
1
+ """Byte-level helpers for Tano's Markdown workspace protocol."""
2
+
3
+ from .protocol import (
4
+ OBJECT_DIRECTORY,
5
+ WORKSPACE_CAPABILITY_PATH,
6
+ WORKSPACE_DATA_DIRECTORY,
7
+ WORKSPACE_RELATION_PATH,
8
+ content_hash,
9
+ is_short_id,
10
+ is_workspace_data_path,
11
+ is_workspace_internal_path,
12
+ )
13
+
14
+ __version__ = "0.1.0a1"
15
+ __all__ = [
16
+ "OBJECT_DIRECTORY",
17
+ "WORKSPACE_CAPABILITY_PATH",
18
+ "WORKSPACE_DATA_DIRECTORY",
19
+ "WORKSPACE_RELATION_PATH",
20
+ "content_hash",
21
+ "is_short_id",
22
+ "is_workspace_data_path",
23
+ "is_workspace_internal_path",
24
+ ]
@@ -0,0 +1,33 @@
1
+ """Path classification does not validate filesystem access or authorize writes."""
2
+
3
+ import hashlib
4
+ import re
5
+
6
+ WORKSPACE_DATA_DIRECTORY = ".tano"
7
+ OBJECT_DIRECTORY = ".tano/objects"
8
+ WORKSPACE_CAPABILITY_PATH = ".tano/capabilities.json"
9
+ WORKSPACE_RELATION_PATH = ".tano/relations.tano-relations.json"
10
+
11
+
12
+ def content_hash(content: bytes) -> str:
13
+ """Return the SHA-256 of exact bytes for Tano's expectedHash field."""
14
+ return hashlib.sha256(content).hexdigest()
15
+
16
+
17
+ def is_short_id(value: object) -> bool:
18
+ """Recognize six lowercase hexadecimal characters, separate from UUIDs."""
19
+ return isinstance(value, str) and re.fullmatch(r"[0-9a-f]{6}", value) is not None
20
+
21
+
22
+ def is_workspace_internal_path(path: str) -> bool:
23
+ """Classify paths inside Tano's portable data directory."""
24
+ return path == WORKSPACE_DATA_DIRECTORY or path.startswith(".tano/")
25
+
26
+
27
+ def is_workspace_data_path(path: str) -> bool:
28
+ """Classify portable data paths, excluding workspace identity and app caches."""
29
+ return (
30
+ path in (WORKSPACE_CAPABILITY_PATH, WORKSPACE_RELATION_PATH)
31
+ or (path.startswith(".tano/conflicts/") and path.endswith(".json"))
32
+ or (path.startswith(".tano/objects/") and path.endswith(".tano.json"))
33
+ )
File without changes
@@ -0,0 +1,49 @@
1
+ import unittest
2
+
3
+ from tano_page import (
4
+ WORKSPACE_CAPABILITY_PATH,
5
+ WORKSPACE_RELATION_PATH,
6
+ content_hash,
7
+ is_short_id,
8
+ is_workspace_data_path,
9
+ is_workspace_internal_path,
10
+ )
11
+
12
+
13
+ class ProtocolTests(unittest.TestCase):
14
+ def test_hashes_exact_content_bytes(self):
15
+ self.assertEqual(
16
+ content_hash(b"abc"),
17
+ "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad",
18
+ )
19
+ self.assertNotEqual(content_hash(b"# Notes\n"), content_hash(b"# Notes\r\n"))
20
+
21
+ def test_reference_ids_do_not_accept_other_identity_formats(self):
22
+ for valid in ("a1b2c3", "000000"):
23
+ self.assertTrue(is_short_id(valid))
24
+ for invalid in (
25
+ "A1B2C3", "abcdef0", "12345", "a1b2cg", "\uff1123456", 123456,
26
+ "550e8400-e29b-41d4-a716-446655440000",
27
+ ):
28
+ with self.subTest(value=invalid):
29
+ self.assertFalse(is_short_id(invalid))
30
+
31
+ def test_identity_and_caches_are_excluded_from_portable_data(self):
32
+ for path in (
33
+ WORKSPACE_CAPABILITY_PATH,
34
+ WORKSPACE_RELATION_PATH,
35
+ ".tano/objects/a1b2c3.tano.json",
36
+ ".tano/conflicts/conflict.json",
37
+ ):
38
+ self.assertTrue(is_workspace_internal_path(path))
39
+ self.assertTrue(is_workspace_data_path(path))
40
+ for path in (".tano/config.json", ".tano/cache.sqlite", ".tano/objects/a.json"):
41
+ self.assertTrue(is_workspace_internal_path(path))
42
+ self.assertFalse(is_workspace_data_path(path))
43
+ self.assertTrue(is_workspace_internal_path(".tano"))
44
+ self.assertFalse(is_workspace_internal_path(".tano-backup/config.json"))
45
+ self.assertFalse(is_workspace_internal_path("notes/.tano/config.json"))
46
+
47
+
48
+ if __name__ == "__main__":
49
+ unittest.main()