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.
Files changed (24) hide show
  1. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/PKG-INFO +1 -1
  2. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/cli.py +28 -18
  3. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/PKG-INFO +1 -1
  4. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/storage.py +25 -0
  5. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/pyproject.toml +1 -1
  6. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/tests/test_cli.py +20 -0
  7. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/README.md +0 -0
  8. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/__init__.py +0 -0
  9. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/__main__.py +0 -0
  10. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/manifest.py +0 -0
  11. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo/sync.py +0 -0
  12. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/SOURCES.txt +0 -0
  13. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/dependency_links.txt +0 -0
  14. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/entry_points.txt +0 -0
  15. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/requires.txt +0 -0
  16. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_cli.egg-info/top_level.txt +0 -0
  17. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/__init__.py +0 -0
  18. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/config.py +0 -0
  19. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/crypto.py +0 -0
  20. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/identity.py +0 -0
  21. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/login.py +0 -0
  22. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/obsideo_core/names.py +0 -0
  23. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/setup.cfg +0 -0
  24. {obsideo_cli-0.2.7 → obsideo_cli-0.2.8}/tests/test_core.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: obsideo-cli
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Obsideo Cloud - encrypted storage we can't read. Save, browse, and sync whatever you want, from your terminal.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://obsideo.io
@@ -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
- if not config.account_token():
567
- # No shim token (e.g. creds set via env / pre-login account): we can't
568
- # query usage. Don't claim the service is down — tell them how to link it.
569
- print(" Usage: sign in with `obsideo login` to see usage details")
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
- usage = _fetch_usage()
572
- if usage:
573
- used, quota = usage["used_bytes"], usage["quota_bytes"]
574
- pct = usage.get("percent_used", (used / quota if quota else 0))
575
- print(f" Used: {_human(used)} / {_human(quota)} ({pct*100:.1f}%)")
576
- bar_len = 30
577
- filled = int(bar_len * min(pct, 1.0))
578
- print(f" [{'#'*filled}{'-'*(bar_len-filled)}]")
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: obsideo-cli
3
- Version: 0.2.7
3
+ Version: 0.2.8
4
4
  Summary: Obsideo Cloud - encrypted storage we can't read. Save, browse, and sync whatever you want, from your terminal.
5
5
  License: MIT
6
6
  Project-URL: Homepage, https://obsideo.io
@@ -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"
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