shelfdb 3.0.2__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. {shelfdb-3.0.2 → shelfdb-3.1.0}/PKG-INFO +32 -8
  2. {shelfdb-3.0.2 → shelfdb-3.1.0}/README.md +28 -6
  3. {shelfdb-3.0.2 → shelfdb-3.1.0}/pyproject.toml +8 -6
  4. {shelfdb-3.0.2 → shelfdb-3.1.0}/pyproject.toml.orig +8 -4
  5. shelfdb-3.1.0/src/shelfdb/cli.py +53 -0
  6. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/client/client.py +1 -1
  7. shelfdb-3.1.0/src/shelfdb/protocol/__init__.py +59 -0
  8. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/protocol.py +4 -5
  9. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/shelf/__init__.py +2 -1
  10. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/shelf/db.py +2 -2
  11. {shelfdb-3.0.2/src/shelfdb/shelf → shelfdb-3.1.0/src/shelfdb}/shelf/query.py +1 -1
  12. shelfdb-3.0.2/ai-skill/shelfdb-usage/SKILL.md +0 -56
  13. shelfdb-3.0.2/src/shelfdb/cli.py +0 -109
  14. shelfdb-3.0.2/src/shelfdb/protocol/__init__.py +0 -35
  15. shelfdb-3.0.2/src/shelfdb/shelf/shelf/__init__.py +0 -4
  16. {shelfdb-3.0.2 → shelfdb-3.1.0}/LICENSE +0 -0
  17. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/__init__.py +0 -0
  18. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/__main__.py +0 -0
  19. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/client/__init__.py +0 -0
  20. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/demo_poc.py +0 -0
  21. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/query_result.py +0 -0
  22. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/server.py +0 -0
  23. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/session.py +0 -0
  24. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/protocol/write_admission.py +0 -0
  25. {shelfdb-3.0.2/src/shelfdb/shelf → shelfdb-3.1.0/src/shelfdb}/shelf/schema.py +0 -0
  26. /shelfdb-3.0.2/src/shelfdb/shelf/shelf/shelf.py → /shelfdb-3.1.0/src/shelfdb/shelf/storage.py +0 -0
  27. {shelfdb-3.0.2 → shelfdb-3.1.0}/src/shelfdb/target.py +0 -0
@@ -1,18 +1,20 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: shelfdb
3
- Version: 3.0.2
3
+ Version: 3.1.0
4
4
  Summary: A tiny database for Python
5
5
  Author: Nitipit Nontasuwan
6
6
  Author-email: Nitipit Nontasuwan <nitipit@gmail.com>
7
7
  License-Expression: MIT
8
8
  License-File: LICENSE
9
9
  Classifier: Programming Language :: Python :: 3.12
10
+ Classifier: Programming Language :: Python :: 3.13
11
+ Classifier: Programming Language :: Python :: 3.14
10
12
  Classifier: Operating System :: OS Independent
11
13
  Requires-Dist: cyclopts>=4.10.2,<5
12
14
  Requires-Dist: dill>=0.4.0,<0.5
13
15
  Requires-Dist: lmdb>=1.7.3,<2
14
16
  Requires-Dist: msgpack>=1.2.1,<2
15
- Requires-Python: >=3.12
17
+ Requires-Python: >=3.12, <3.15
16
18
  Project-URL: Documentation, https://keenlycode.github.io/shelfdb/
17
19
  Project-URL: Repository, https://github.com/keenlycode/shelfdb
18
20
  Description-Content-Type: text/markdown
@@ -34,16 +36,29 @@ pip install shelfdb
34
36
 
35
37
  ## Development
36
38
 
39
+ Python 3.12–3.14 is supported. Development uses Python 3.14, selected by
40
+ `.python-version`; compatibility checks cover Python 3.12 and 3.14. Python 3.15
41
+ is temporarily excluded because the released `dill` cannot serialize query
42
+ callbacks on it; support will be reassessed after an upstream fix is released.
43
+
37
44
  Install development dependencies:
38
45
 
39
46
  ```bash
40
47
  uv sync --dev
41
48
  ```
42
49
 
50
+ Development tooling lives in `src/dev/`, outside the published `shelfdb` package.
51
+ Use `--directory src` to run its module; workflow commands resolve paths from the
52
+ repository root. Documentation macros live in `src/macros.py`.
53
+
54
+ Unit tests mirror `src/` under `tests/unit/`: `shelfdb/`, `dev/`, and
55
+ `macros_test.py`. Integration tests cover cross-layer behavior; `tests/usage/`
56
+ contains executable usage examples.
57
+
43
58
  Run the complete non-publishing release gate:
44
59
 
45
60
  ```bash
46
- uv run python -m dev release-check
61
+ uv run --directory src python -m dev release-check
47
62
  ```
48
63
 
49
64
  The gate audits locked dependencies, checks formatting, linting, types, supported
@@ -53,13 +68,13 @@ and version tags. Before tagging, authenticated release maintainers also check
53
68
  GitHub's repository alerts:
54
69
 
55
70
  ```bash
56
- uv run python -m dev release-check --github
71
+ uv run --directory src python -m dev release-check --github
57
72
  ```
58
73
 
59
74
  Serve the docs locally (Zensical dev server; live reload is built in):
60
75
 
61
76
  ```bash
62
- uv run python -m dev docs serve --port 9001 --livereload
77
+ uv run --directory src python -m dev docs serve --port 9001 --livereload
63
78
  ```
64
79
 
65
80
  `--livereload` is a legacy compatibility flag and is intentionally ignored by Zensical
@@ -68,19 +83,19 @@ uv run python -m dev docs serve --port 9001 --livereload
68
83
  Build docs for verification (Zensical build):
69
84
 
70
85
  ```bash
71
- uv run python -m dev docs build
86
+ uv run --directory src python -m dev docs build
72
87
  ```
73
88
 
74
89
  Publish the docs with mike to the `docs` branch:
75
90
 
76
91
  ```bash
77
- uv run python -m dev docs publish
92
+ uv run --directory src python -m dev docs publish
78
93
  ```
79
94
 
80
95
  Override the publish target when needed:
81
96
 
82
97
  ```bash
83
- uv run python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
98
+ uv run --directory src python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
84
99
  ```
85
100
 
86
101
  ## Server
@@ -130,6 +145,15 @@ client = await Client.connect("unix:///tmp/shelfdb.sock")
130
145
  See [remote transaction usage](docs-src/usage/remote.md#concurrent-transactions)
131
146
  for coordination limits and details on partial updates and error handling.
132
147
 
148
+ ## Large results
149
+
150
+ Remote messages are buffered, not streamed. The former 1 MiB cap is removed;
151
+ only the unsigned 32-bit payload length remains (4 GiB minus 1 byte). Large
152
+ queries can still exhaust memory, so paginate bulk reads with `.slice(...)`.
153
+ Store large assets externally and keep references in ShelfDB.
154
+
155
+ See [pagination and its limits](docs-src/usage/remote.md#large-results-and-pagination).
156
+
133
157
  ## Example
134
158
 
135
159
  ```python
@@ -15,16 +15,29 @@ pip install shelfdb
15
15
 
16
16
  ## Development
17
17
 
18
+ Python 3.12–3.14 is supported. Development uses Python 3.14, selected by
19
+ `.python-version`; compatibility checks cover Python 3.12 and 3.14. Python 3.15
20
+ is temporarily excluded because the released `dill` cannot serialize query
21
+ callbacks on it; support will be reassessed after an upstream fix is released.
22
+
18
23
  Install development dependencies:
19
24
 
20
25
  ```bash
21
26
  uv sync --dev
22
27
  ```
23
28
 
29
+ Development tooling lives in `src/dev/`, outside the published `shelfdb` package.
30
+ Use `--directory src` to run its module; workflow commands resolve paths from the
31
+ repository root. Documentation macros live in `src/macros.py`.
32
+
33
+ Unit tests mirror `src/` under `tests/unit/`: `shelfdb/`, `dev/`, and
34
+ `macros_test.py`. Integration tests cover cross-layer behavior; `tests/usage/`
35
+ contains executable usage examples.
36
+
24
37
  Run the complete non-publishing release gate:
25
38
 
26
39
  ```bash
27
- uv run python -m dev release-check
40
+ uv run --directory src python -m dev release-check
28
41
  ```
29
42
 
30
43
  The gate audits locked dependencies, checks formatting, linting, types, supported
@@ -34,13 +47,13 @@ and version tags. Before tagging, authenticated release maintainers also check
34
47
  GitHub's repository alerts:
35
48
 
36
49
  ```bash
37
- uv run python -m dev release-check --github
50
+ uv run --directory src python -m dev release-check --github
38
51
  ```
39
52
 
40
53
  Serve the docs locally (Zensical dev server; live reload is built in):
41
54
 
42
55
  ```bash
43
- uv run python -m dev docs serve --port 9001 --livereload
56
+ uv run --directory src python -m dev docs serve --port 9001 --livereload
44
57
  ```
45
58
 
46
59
  `--livereload` is a legacy compatibility flag and is intentionally ignored by Zensical
@@ -49,19 +62,19 @@ uv run python -m dev docs serve --port 9001 --livereload
49
62
  Build docs for verification (Zensical build):
50
63
 
51
64
  ```bash
52
- uv run python -m dev docs build
65
+ uv run --directory src python -m dev docs build
53
66
  ```
54
67
 
55
68
  Publish the docs with mike to the `docs` branch:
56
69
 
57
70
  ```bash
58
- uv run python -m dev docs publish
71
+ uv run --directory src python -m dev docs publish
59
72
  ```
60
73
 
61
74
  Override the publish target when needed:
62
75
 
63
76
  ```bash
64
- uv run python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
77
+ uv run --directory src python -m dev docs publish --publish-version 3.0.1 --alias latest --branch docs --remote origin
65
78
  ```
66
79
 
67
80
  ## Server
@@ -111,6 +124,15 @@ client = await Client.connect("unix:///tmp/shelfdb.sock")
111
124
  See [remote transaction usage](docs-src/usage/remote.md#concurrent-transactions)
112
125
  for coordination limits and details on partial updates and error handling.
113
126
 
127
+ ## Large results
128
+
129
+ Remote messages are buffered, not streamed. The former 1 MiB cap is removed;
130
+ only the unsigned 32-bit payload length remains (4 GiB minus 1 byte). Large
131
+ queries can still exhaust memory, so paginate bulk reads with `.slice(...)`.
132
+ Store large assets externally and keep references in ShelfDB.
133
+
134
+ See [pagination and its limits](docs-src/usage/remote.md#large-results-and-pagination).
135
+
114
136
  ## Example
115
137
 
116
138
  ```python
@@ -4,14 +4,16 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "shelfdb"
7
- version = "3.0.2"
7
+ version = "3.1.0"
8
8
  description = "A tiny database for Python"
9
9
  readme = "README.md"
10
- requires-python = ">=3.12"
10
+ requires-python = ">=3.12,<3.15"
11
11
  license = "MIT"
12
12
  license-files = ["LICENSE"]
13
13
  classifiers = [
14
14
  "Programming Language :: Python :: 3.12",
15
+ "Programming Language :: Python :: 3.13",
16
+ "Programming Language :: Python :: 3.14",
15
17
  "Operating System :: OS Independent",
16
18
  ]
17
19
  dependencies = [
@@ -43,10 +45,10 @@ dev = [
43
45
  "ty>=0.0.1a17,<0.0.2",
44
46
  ]
45
47
 
48
+ [tool.pytest.ini_options]
49
+ addopts = ["--import-mode=importlib"]
50
+ pythonpath = ["src"]
51
+
46
52
  [tool.uv.build-backend]
47
53
  module-name = "shelfdb"
48
54
  module-root = "src"
49
- source-include = ["ai-skill/**"]
50
-
51
- [tool.uv.build-backend.data]
52
- data = "ai-skill"
@@ -4,10 +4,10 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "shelfdb"
7
- version = "3.0.2"
7
+ version = "3.1.0"
8
8
  description = "A tiny database for Python"
9
9
  readme = "README.md"
10
- requires-python = ">=3.12"
10
+ requires-python = ">=3.12,<3.15"
11
11
  license = "MIT"
12
12
  license-files = ["LICENSE"]
13
13
  authors = [
@@ -15,6 +15,8 @@ authors = [
15
15
  ]
16
16
  classifiers = [
17
17
  "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Programming Language :: Python :: 3.14",
18
20
  "Operating System :: OS Independent",
19
21
  ]
20
22
  dependencies = [
@@ -42,8 +44,10 @@ dev = [
42
44
  "ty>=0.0.1a17,<0.0.2",
43
45
  ]
44
46
 
47
+ [tool.pytest.ini_options]
48
+ addopts = ["--import-mode=importlib"]
49
+ pythonpath = ["src"]
50
+
45
51
  [tool.uv.build-backend]
46
52
  module-name = "shelfdb"
47
53
  module-root = "src"
48
- data = { data = "ai-skill" }
49
- source-include = ["ai-skill/**"]
@@ -0,0 +1,53 @@
1
+ """Command-line interface for ShelfDB."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from contextlib import suppress
6
+ from pathlib import Path
7
+
8
+ from cyclopts import App
9
+
10
+ from shelfdb.protocol import serve, serve_unix
11
+ from shelfdb.shelf import DB
12
+ from shelfdb.target import parse_target, parse_tcp_location, parse_unix_location
13
+
14
+ app = App("shelfdb")
15
+
16
+
17
+ async def run_server(
18
+ *,
19
+ db_path: str,
20
+ url: str = "tcp://127.0.0.1:31337",
21
+ ) -> None:
22
+ scheme, location = parse_target(url)
23
+ socket_path = None
24
+
25
+ with DB(db_path) as db:
26
+ if scheme == "tcp":
27
+ host, port = parse_tcp_location(location)
28
+ server = await serve(db, host=host, port=port)
29
+ else:
30
+ socket_path = str(Path(parse_unix_location(location)))
31
+ server = await serve_unix(db, path=socket_path)
32
+
33
+ try:
34
+ await server.serve_forever()
35
+ finally:
36
+ server.close()
37
+ await server.wait_closed()
38
+ if socket_path is not None:
39
+ with suppress(FileNotFoundError):
40
+ Path(socket_path).unlink()
41
+
42
+
43
+ @app.command
44
+ async def server(
45
+ db_path: str = "db",
46
+ url: str = "tcp://127.0.0.1:31337",
47
+ ) -> None:
48
+ """Run the ShelfDB protocol server."""
49
+ await run_server(db_path=db_path, url=url)
50
+
51
+
52
+ def main(argv: list[str] | None = None) -> None:
53
+ app(argv, backend="asyncio", result_action="return_none")
@@ -6,7 +6,7 @@ from asyncio import StreamReader, StreamWriter, open_connection, open_unix_conne
6
6
  from contextlib import suppress
7
7
  from typing import Any
8
8
 
9
- from shelfdb.protocol import read_response, write_request
9
+ from shelfdb.protocol.protocol import read_response, write_request
10
10
  from shelfdb.protocol.query_result import denormalize_query_result
11
11
  from shelfdb.shelf import Item
12
12
  from shelfdb.target import parse_target, parse_tcp_location, parse_unix_location
@@ -0,0 +1,59 @@
1
+ """Wire helpers, with backward-compatible lazy server-side exports."""
2
+
3
+ from importlib import import_module
4
+ from typing import TYPE_CHECKING
5
+
6
+ from .protocol import (
7
+ MAX_FRAME_SIZE,
8
+ decode_request,
9
+ decode_response,
10
+ encode_request,
11
+ encode_response,
12
+ frame_payload,
13
+ read_payload,
14
+ read_request,
15
+ read_response,
16
+ write_payload,
17
+ write_request,
18
+ write_response,
19
+ )
20
+
21
+ if TYPE_CHECKING:
22
+ from .server import handle_client, serve, serve_unix
23
+ from .session import Session
24
+
25
+
26
+ def __getattr__(name: str):
27
+ if name in {"handle_client", "serve", "serve_unix"}:
28
+ module = import_module(".server", __name__)
29
+ elif name == "Session":
30
+ module = import_module(".session", __name__)
31
+ else:
32
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
33
+ value = getattr(module, name)
34
+ globals()[name] = value
35
+ return value
36
+
37
+
38
+ def __dir__() -> list[str]:
39
+ return sorted(set(globals()) | set(__all__))
40
+
41
+
42
+ __all__ = [
43
+ "MAX_FRAME_SIZE",
44
+ "decode_request",
45
+ "decode_response",
46
+ "encode_request",
47
+ "encode_response",
48
+ "frame_payload",
49
+ "handle_client",
50
+ "read_payload",
51
+ "read_request",
52
+ "read_response",
53
+ "Session",
54
+ "serve",
55
+ "serve_unix",
56
+ "write_payload",
57
+ "write_request",
58
+ "write_response",
59
+ ]
@@ -10,7 +10,8 @@ import dill
10
10
  import msgpack
11
11
 
12
12
  _FRAME_HEADER: Final = struct.Struct(">I")
13
- MAX_FRAME_SIZE: Final = 1024 * 1024
13
+ # The unsigned 32-bit length header is the only frame-size limit.
14
+ MAX_FRAME_SIZE: Final = (1 << 32) - 1
14
15
 
15
16
 
16
17
  class WritableStream(Protocol):
@@ -20,9 +21,9 @@ class WritableStream(Protocol):
20
21
 
21
22
 
22
23
  def frame_payload(payload: bytes) -> bytes:
23
- """Prefix payload bytes with a 4-byte big-endian length."""
24
+ """Prefix a buffered payload with its unsigned 32-bit big-endian length."""
24
25
  if len(payload) > MAX_FRAME_SIZE:
25
- raise ValueError("payload exceeds max frame size")
26
+ raise ValueError("payload exceeds 32-bit frame length (4294967295 bytes)")
26
27
  return _FRAME_HEADER.pack(len(payload)) + payload
27
28
 
28
29
 
@@ -50,8 +51,6 @@ async def read_payload(reader: StreamReader) -> bytes:
50
51
  """Read one length-prefixed payload from a stream."""
51
52
  size_data = await reader.readexactly(_FRAME_HEADER.size)
52
53
  (size,) = _FRAME_HEADER.unpack(size_data)
53
- if size > MAX_FRAME_SIZE:
54
- raise ValueError("frame exceeds max frame size")
55
54
  return await reader.readexactly(size)
56
55
 
57
56
 
@@ -1,4 +1,5 @@
1
1
  from .db import DB
2
- from .shelf import UNDEF, Item, MutationResult, ShelfQuery
2
+ from .query import ShelfQuery
3
+ from .schema import UNDEF, Item, MutationResult
3
4
 
4
5
  __all__ = ["DB", "UNDEF", "Item", "MutationResult", "ShelfQuery"]
@@ -9,8 +9,8 @@ from typing import Any
9
9
  import lmdb
10
10
 
11
11
  # lib: local
12
- from .shelf.query import ShelfQuery
13
- from .shelf.shelf import ShelfCursor, ShelfStore
12
+ from .query import ShelfQuery
13
+ from .storage import ShelfCursor, ShelfStore
14
14
 
15
15
  LmdbEnvironment = Any
16
16
  LmdbTransaction = Any
@@ -20,7 +20,7 @@ from itertools import islice
20
20
  from typing import Any
21
21
 
22
22
  from .schema import UNDEF, Item, MutationResult
23
- from .shelf import ShelfCursor, ShelfStore
23
+ from .storage import ShelfCursor, ShelfStore
24
24
 
25
25
  type Transform = Callable[[Iterator[Item]], Iterator[Item]]
26
26
 
@@ -1,56 +0,0 @@
1
- ---
2
- name: shelfdb-usage
3
- description: Use when writing, reviewing, or explaining ShelfDB local database code, async client/server code, transactions, or fluent queries.
4
- ---
5
-
6
- # ShelfDB usage
7
-
8
- Choose access mode from the deployment:
9
-
10
- - Use `shelfdb.shelf.DB` when the process can open the LMDB database directly.
11
- - Use `shelfdb.client.Client` when a separate trusted process runs `shelfdb server`.
12
-
13
- ## Safety boundary
14
-
15
- The remote protocol uses `dill` so queries can contain Python callables. Deserializing a
16
- request can execute arbitrary code in the server process. Never expose the server to
17
- untrusted clients or public networks.
18
-
19
- ## Local API
20
-
21
- Use synchronous transactions. Operations execute directly; do not add `.query()`.
22
-
23
- ```python
24
- from shelfdb.shelf import DB
25
-
26
- with DB("db") as db:
27
- with db.transaction(write=False) as tx:
28
- users = list(tx.shelf("users").items())
29
- ```
30
-
31
- ## Remote API
32
-
33
- Connect asynchronously, use read transactions by default, and pass `write=True` for
34
- mutations. Fluent operations only build a remote query; `await ...query()` sends it.
35
-
36
- ```python
37
- from shelfdb.client import Client
38
-
39
- client = await Client.connect("tcp://127.0.0.1:31337")
40
- try:
41
- async with client.transaction() as tx:
42
- users = await tx.shelf("users").items().query()
43
- finally:
44
- await client.close()
45
- ```
46
-
47
- ## Rules
48
-
49
- - Use `client.transaction()` for remote reads.
50
- - Use `client.transaction(write=True)` for remote mutations.
51
- - Always close a remote client with `await client.close()`.
52
- - Keep local and remote terminal behavior distinct.
53
- - In a ShelfDB source checkout, use `tests/usage/` as executable examples.
54
- - Consult the current documentation before assuming unsupported query behavior.
55
-
56
- Documentation: https://keenlycode.github.io/shelfdb/
@@ -1,109 +0,0 @@
1
- """Command-line interface for ShelfDB."""
2
-
3
- from __future__ import annotations
4
-
5
- import shutil
6
- import sysconfig
7
- from contextlib import suppress
8
- from pathlib import Path
9
-
10
- from cyclopts import App
11
-
12
- from shelfdb.protocol import serve, serve_unix
13
- from shelfdb.shelf import DB
14
- from shelfdb.target import parse_target, parse_tcp_location, parse_unix_location
15
-
16
- app = App("shelfdb")
17
- DEFAULT_AI_SKILL_INSTALL_PATH = ".agents/skills/shelfdb-usage"
18
-
19
-
20
- def repo_root() -> Path:
21
- return Path(__file__).resolve().parents[2]
22
-
23
-
24
- def bundled_ai_skill_path() -> Path:
25
- data_root = Path(sysconfig.get_path("data"))
26
- installed_path = data_root / "shelfdb-usage"
27
- if installed_path.exists():
28
- return installed_path
29
-
30
- repo_path = repo_root() / "ai-skill" / "shelfdb-usage"
31
- if repo_path.exists():
32
- return repo_path
33
-
34
- raise FileNotFoundError("Bundled ShelfDB AI skill not found.")
35
-
36
-
37
- def install_ai_skill(destination: Path, *, force: bool = False) -> Path:
38
- source_file = bundled_ai_skill_path() / "SKILL.md"
39
- if not source_file.is_file():
40
- raise FileNotFoundError("Bundled ShelfDB SKILL.md not found.")
41
-
42
- if destination.exists():
43
- if not destination.is_dir():
44
- raise NotADirectoryError(
45
- f"AI skill destination is not a directory: {destination}"
46
- )
47
- if not force:
48
- raise FileExistsError(
49
- f"AI skill destination already exists: {destination}. Use --force to replace SKILL.md."
50
- )
51
- else:
52
- destination.mkdir(parents=True)
53
-
54
- shutil.copy2(source_file, destination / "SKILL.md")
55
- return destination
56
-
57
-
58
- def prompt_ai_skill_install_path(
59
- default_path: str = DEFAULT_AI_SKILL_INSTALL_PATH,
60
- ) -> Path:
61
- response = input(f"Install ShelfDB AI skill to [{default_path}]: ").strip()
62
- return Path(response or default_path)
63
-
64
-
65
- async def run_server(
66
- *,
67
- db_path: str,
68
- url: str = "tcp://127.0.0.1:31337",
69
- ) -> None:
70
- scheme, location = parse_target(url)
71
- socket_path = None
72
-
73
- with DB(db_path) as db:
74
- if scheme == "tcp":
75
- host, port = parse_tcp_location(location)
76
- server = await serve(db, host=host, port=port)
77
- else:
78
- socket_path = str(Path(parse_unix_location(location)))
79
- server = await serve_unix(db, path=socket_path)
80
-
81
- try:
82
- await server.serve_forever()
83
- finally:
84
- server.close()
85
- await server.wait_closed()
86
- if socket_path is not None:
87
- with suppress(FileNotFoundError):
88
- Path(socket_path).unlink()
89
-
90
-
91
- @app.command
92
- async def server(
93
- db_path: str = "db",
94
- url: str = "tcp://127.0.0.1:31337",
95
- ) -> None:
96
- """Run the ShelfDB protocol server."""
97
- await run_server(db_path=db_path, url=url)
98
-
99
-
100
- @app.command(name="ai-skill-install")
101
- def ai_skill_install(path: str | None = None, force: bool = False) -> None:
102
- """Install the bundled ShelfDB AI skill to a local path."""
103
- destination = Path(path) if path is not None else prompt_ai_skill_install_path()
104
- installed = install_ai_skill(destination, force=force)
105
- print(f"Installed ShelfDB AI skill to {installed}")
106
-
107
-
108
- def main(argv: list[str] | None = None) -> None:
109
- app(argv, backend="asyncio", result_action="return_none")
@@ -1,35 +0,0 @@
1
- from .protocol import (
2
- MAX_FRAME_SIZE,
3
- decode_request,
4
- decode_response,
5
- encode_request,
6
- encode_response,
7
- frame_payload,
8
- read_payload,
9
- read_request,
10
- read_response,
11
- write_payload,
12
- write_request,
13
- write_response,
14
- )
15
- from .server import handle_client, serve, serve_unix
16
- from .session import Session
17
-
18
- __all__ = [
19
- "MAX_FRAME_SIZE",
20
- "decode_request",
21
- "decode_response",
22
- "encode_request",
23
- "encode_response",
24
- "frame_payload",
25
- "handle_client",
26
- "read_payload",
27
- "read_request",
28
- "read_response",
29
- "Session",
30
- "serve",
31
- "serve_unix",
32
- "write_payload",
33
- "write_request",
34
- "write_response",
35
- ]
@@ -1,4 +0,0 @@
1
- from .query import ShelfQuery
2
- from .schema import UNDEF, Item, MutationResult
3
-
4
- __all__ = ["UNDEF", "Item", "MutationResult", "ShelfQuery"]
File without changes
File without changes
File without changes
File without changes