From ed0c680790950354f15fb5835e1d7b213efa1bf8 Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 8 Oct 2026 01:11:38 +0200 Subject: fix(node): name a root after its drive when its basename is taken Two drives with a folder of the same name made the second add fail, and no screen could supply another name. add_root now names it "Name (H)" or "Name (parent)"; a name the operator typed is still refused on a clash. Co-Authored-By: Claude Opus 5.5 --- packages/meshbay-node/src/meshbay_node/ops/roots.py | 15 ++++++++++++--- packages/meshbay-node/src/meshbay_node/roots.py | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) (limited to 'packages/meshbay-node/src/meshbay_node') diff --git a/packages/meshbay-node/src/meshbay_node/ops/roots.py b/packages/meshbay-node/src/meshbay_node/ops/roots.py index b976e8f..c272978 100644 --- a/packages/meshbay-node/src/meshbay_node/ops/roots.py +++ b/packages/meshbay-node/src/meshbay_node/ops/roots.py @@ -14,7 +14,7 @@ from meshbay_node.ops.node_toml import ( _update_root_field, toml_string, ) -from meshbay_node.roots import RootError, RootSet, off_disk +from meshbay_node.roots import RootError, RootSet, distinct_name, off_disk log = logging.getLogger("meshbay_node.ops") @@ -36,12 +36,21 @@ async def add_root(state: dict, group_id: str, path: str, *, raise OpError("Group not configured on this node", status=404) specs = [asdict(r) for r in cfg.roots] - specs.append({"path": path, "name": name, "kind": kind, - "writable": writable, "removable": removable}) try: + # Every caller sends the folder's basename when nobody typed a name, so + # that is the case that gets a distinct one. A name somebody chose is + # still refused on a clash. + target = Path(path).expanduser().resolve() + if name.strip() in ("", target.name): + taken = {r.folded for r in RootSet.build(specs).roots} + name = distinct_name(target, taken) + specs.append({"path": path, "name": name, "kind": kind, + "writable": writable, "removable": removable}) built = RootSet.build(specs) except RootError as e: raise OpError(str(e)) from e + except OSError as e: + raise OpError(f"{path}: {e}") from e added = built.roots[-1] diff --git a/packages/meshbay-node/src/meshbay_node/roots.py b/packages/meshbay-node/src/meshbay_node/roots.py index 2b48292..7dded1f 100644 --- a/packages/meshbay-node/src/meshbay_node/roots.py +++ b/packages/meshbay-node/src/meshbay_node/roots.py @@ -211,6 +211,27 @@ def derive_name(path: Path) -> str: return name +def distinct_name(path: Path, taken: set[str]) -> str: + """ + `derive_name(path)`, or, when a root in `taken` (folded names) already has + it, that name followed by where the directory is: `Archives (H)` for + `H:\\Archives` beside `G:\\Archives`, `Films (b)` for `/mnt/b/Films`. + + The same folder name on two drives is the ordinary case, and the interface + has no field to type a name into, so refusing it left no way forward. + """ + base = derive_name(path) + candidates = [base] + where = path.parent.name or path.drive.rstrip(":") + if where: + candidates.append(f"{base} ({where})") + candidates += [f"{base} ({n})" for n in range(2, 100)] + for name in candidates: + if fold(name) not in taken and not portable_name_problem(name): + return name + return base + + @dataclass class RootSet: """ -- cgit v1.2.3