obsideo-cli 0.2.7__tar.gz → 0.2.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.
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/PKG-INFO +1 -1
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/cli.py +28 -18
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/PKG-INFO +1 -1
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/storage.py +25 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/pyproject.toml +1 -1
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/tests/test_cli.py +20 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/README.md +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/__init__.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/__main__.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/manifest.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/sync.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/SOURCES.txt +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/dependency_links.txt +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/entry_points.txt +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/requires.txt +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/top_level.txt +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/__init__.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/config.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/crypto.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/identity.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/login.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/names.py +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/setup.cfg +0 -0
- {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/tests/test_core.py +0 -0
|
@@ -339,6 +339,14 @@ class ObsideoShell(cmd.Cmd):
|
|
|
339
339
|
self._cwd = "" # S3 key prefix; "" = root
|
|
340
340
|
self._refresh_prompt()
|
|
341
341
|
|
|
342
|
+
def precmd(self, line: str) -> str:
|
|
343
|
+
# Tolerate a leading "obsideo " typed out of habit inside the shell
|
|
344
|
+
# (e.g. "obsideo ls" -> "ls"), so it doesn't error with Unknown syntax.
|
|
345
|
+
stripped = line.lstrip()
|
|
346
|
+
if stripped.lower().startswith("obsideo "):
|
|
347
|
+
return stripped[len("obsideo "):]
|
|
348
|
+
return line
|
|
349
|
+
|
|
342
350
|
# ── path helpers ────────────────────────────────────────────────────────
|
|
343
351
|
def _refresh_prompt(self):
|
|
344
352
|
self.prompt = f"obsideo:/{self._cwd} "
|
|
@@ -563,25 +571,27 @@ class ObsideoShell(cmd.Cmd):
|
|
|
563
571
|
print()
|
|
564
572
|
print(" -- Obsideo account --------------------------")
|
|
565
573
|
print(" Plan: Free")
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
574
|
+
# Prefer the signup service (it knows your quota); otherwise compute usage
|
|
575
|
+
# straight from your storage so this always works - never nag to "log in".
|
|
576
|
+
usage = _fetch_usage() if config.account_token() else None
|
|
577
|
+
if usage:
|
|
578
|
+
used, quota = usage["used_bytes"], usage["quota_bytes"]
|
|
579
|
+
pct = usage.get("percent_used", (used / quota if quota else 0))
|
|
580
|
+
print(f" Used: {_human(used)} / {_human(quota)} ({pct*100:.1f}%)")
|
|
581
|
+
bar_len = 30
|
|
582
|
+
filled = int(bar_len * min(pct, 1.0))
|
|
583
|
+
print(f" [{'#'*filled}{'-'*(bar_len-filled)}]")
|
|
584
|
+
if pct >= 0.8:
|
|
585
|
+
print(" You're near your limit - reply to any Obsideo email to upgrade.")
|
|
570
586
|
else:
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
used
|
|
574
|
-
|
|
575
|
-
print(
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
if pct >= 0.8:
|
|
580
|
-
print(" You're near your limit - reply to any Obsideo email to upgrade.")
|
|
581
|
-
else:
|
|
582
|
-
print(" Usage: couldn't reach the account service - try again shortly")
|
|
583
|
-
print(f" Files: bucket '{storage.bucket()}' · sync folder {sync_mod.ensure_sync_dir()}")
|
|
584
|
-
print(f" Keys: {config.CONFIG_DIR} (back up data.key)")
|
|
587
|
+
try:
|
|
588
|
+
used, n = storage.total_usage()
|
|
589
|
+
print(f" Used: {_human(used)} across {n} file(s)")
|
|
590
|
+
except Exception:
|
|
591
|
+
print(" Used: (couldn't read storage just now)")
|
|
592
|
+
print(f" Bucket: {storage.bucket()}")
|
|
593
|
+
print(f" Sync folder: {sync_mod.ensure_sync_dir()}")
|
|
594
|
+
print(f" Keys: {config.CONFIG_DIR} (back up data.key)")
|
|
585
595
|
print(" ---------------------------------------------")
|
|
586
596
|
print()
|
|
587
597
|
|
|
@@ -189,6 +189,31 @@ def exists(key: str) -> bool:
|
|
|
189
189
|
return head(key) is not None
|
|
190
190
|
|
|
191
191
|
|
|
192
|
+
def total_usage() -> tuple[int, int]:
|
|
193
|
+
"""Total stored bytes + object count across the account's bucket (flat list).
|
|
194
|
+
Lets `account` show real usage without the signup-service token — it just reads
|
|
195
|
+
the storage the account can already see. Names stay opaque; only sizes summed."""
|
|
196
|
+
s3 = _s3()
|
|
197
|
+
total = 0
|
|
198
|
+
count = 0
|
|
199
|
+
token = None
|
|
200
|
+
while True:
|
|
201
|
+
kwargs = dict(Bucket=bucket())
|
|
202
|
+
if token:
|
|
203
|
+
kwargs["ContinuationToken"] = token
|
|
204
|
+
resp = s3.list_objects_v2(**kwargs)
|
|
205
|
+
for obj in resp.get("Contents", []):
|
|
206
|
+
if obj["Key"].endswith("/"):
|
|
207
|
+
continue # folder marker, not a real object
|
|
208
|
+
total += obj.get("Size", 0)
|
|
209
|
+
count += 1
|
|
210
|
+
if resp.get("IsTruncated"):
|
|
211
|
+
token = resp.get("NextContinuationToken")
|
|
212
|
+
else:
|
|
213
|
+
break
|
|
214
|
+
return total, count
|
|
215
|
+
|
|
216
|
+
|
|
192
217
|
def list_prefix(prefix: str = "", delimiter: str = "/") -> dict:
|
|
193
218
|
"""List one VFS level. Returns {'folders': [name...], 'files': [{name,key,size}]}.
|
|
194
219
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "obsideo-cli"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.8"
|
|
8
8
|
description = "Obsideo Cloud - encrypted storage we can't read. Save, browse, and sync whatever you want, from your terminal."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -295,3 +295,23 @@ def test_sync_readme_created_and_not_pushed(tmp_path, monkeypatch, capsys):
|
|
|
295
295
|
n = sync.push(verbose=True)
|
|
296
296
|
assert n == 0
|
|
297
297
|
assert "empty" in capsys.readouterr().out.lower()
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def test_account_computes_usage_without_token(monkeypatch, tmp_path, capsys):
|
|
301
|
+
# No signup token -> account must compute usage from storage, NOT nag to log in.
|
|
302
|
+
from obsideo import sync
|
|
303
|
+
monkeypatch.setattr(cli.config, "account_token", lambda: None)
|
|
304
|
+
monkeypatch.setattr(cli.storage, "total_usage", lambda: (1_500_000, 7))
|
|
305
|
+
monkeypatch.setattr(cli.storage, "bucket", lambda: "tb")
|
|
306
|
+
monkeypatch.setattr(sync, "_sync_dir", lambda: tmp_path / "s")
|
|
307
|
+
cli.ObsideoShell().do_account("")
|
|
308
|
+
out = capsys.readouterr().out
|
|
309
|
+
assert "across 7 file" in out
|
|
310
|
+
assert "obsideo login" not in out and "sign in" not in out.lower()
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def test_precmd_strips_obsideo_prefix():
|
|
314
|
+
sh = cli.ObsideoShell()
|
|
315
|
+
assert sh.precmd("obsideo ls") == "ls"
|
|
316
|
+
assert sh.precmd("obsideo login") == "login"
|
|
317
|
+
assert sh.precmd("ls") == "ls" # unchanged when no prefix
|
|
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
|