sibyl-memory-cli 0.3.4__tar.gz → 0.3.6__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sibyl-memory-cli
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Command-line interface for the Sibyl Memory Plugin. `sibyl init` activates, `sibyl upgrade` runs the staker / subscription flow, `sibyl status` shows current tier and DB stats, `sibyl whoami` gives a one-line account summary, `sibyl devices` lists active devices and supports per-device revoke.
5
5
  Author-email: "SIBYL, Sibyl Labs LLC" <sibyl@sibyllabs.org>
6
6
  License: MIT
@@ -48,39 +48,37 @@ sibyl health Run the SibylMemoryProvider self-check (schema versi
48
48
  ```bash
49
49
  $ sibyl init
50
50
 
51
- activation · three paths · pick whichever fits your device
51
+ Sibyl Memory Plugin · activation
52
52
 
53
- Session a1b2c3d4…e5f6
54
- Code 1 2 3 4 5 6 (use this in the email panel)
55
- Opening https://auth.sibyllabs.org/a1b2c3d4-…
53
+ Session: a1b2c3d4…e5f6
54
+ Opening: https://sibyllabs.org/plugin/activate?session=a1b2c3d4-…
56
55
 
57
- desktop wallet · email + code · or send USDC from any mobile wallet
58
- this terminal will pick up automatically when you bind.
56
+ Sign in with your wallet in the browser. This terminal will pick up automatically.
59
57
 
60
- ⠹ watching the network for your bind … 9:42 left
58
+ ⠹ waiting for browser activation … 9:42 left
61
59
  ```
62
60
 
63
- The browser opens to a dedicated short-URL auth subdomain (`auth.sibyllabs.org/<session-uuid>`). Pick a wallet from the EIP-6963 picker (Rabby / MetaMask / Coinbase Wallet / Frame / Brave / Rainbow), enter email + 6-digit code, or send a unique sub-cent USDC amount from any mobile wallet on Base. The terminal picks up the moment any of the three paths binds. Credentials are written to `~/.sibyl-memory/credentials.json` at mode 0600.
61
+ The browser opens. Sign a SIWE message with your wallet. The terminal picks up the moment the binding lands. Credentials are written to `~/.sibyl-memory/credentials.json` at mode 0600.
64
62
 
65
63
  ## Upgrade
66
64
 
67
65
  ```bash
68
66
  $ sibyl upgrade
69
67
 
70
- upgrade · lift the 2 MB free-tier cap
68
+ Sibyl Memory Plugin · upgrade
71
69
 
72
70
  Account a1b2c3d4…e5f6
73
71
  Current tier FREE
74
72
  Opening https://sibyllabs.org/plugin/upgrade?session=…
75
73
 
76
- two paths in the browser:
77
- 1. stake $SIBYL on Base (free unlimited if you qualify)
78
- 2. subscribe in USDC (monthly / quarterly / annual)
74
+ Two paths in your browser:
75
+ 1. Stake $SIBYL on Base (free unlimited if you qualify)
76
+ 2. Subscribe in USDC (monthly / quarterly / annual)
79
77
  ```
80
78
 
81
79
  In the browser:
82
- - **Stake** — connect your wallet (browser or Coinbase Smart Wallet), sign to bind, and the page checks your `$SIBYL` balance on Base. If you hold the threshold (default 100,000 $SIBYL liquid+staked, configurable), the local cap lifts.
83
- - **Subscribe** — pick monthly ($29) / quarterly ($79) / annual ($290) USDC, sign the transfer, the server records the subscription. Tier flips immediately.
80
+ - **Stake**: connect your wallet (browser or Coinbase Smart Wallet), sign to bind, and the page checks your `$SIBYL` balance on Base. If you hold the threshold (default 100,000 $SIBYL liquid+staked, configurable), the local cap lifts.
81
+ - **Subscribe**: pick monthly ($29) / quarterly ($79) / annual ($290) USDC, sign the transfer, the server records the subscription. Tier flips immediately.
84
82
 
85
83
  On either path, the CLI sees the tier change, rewrites `credentials.json`, and clears `tier_cache.json` so your next write picks up the new entitlement without delay.
86
84
 
@@ -115,16 +113,14 @@ For internal testing only:
115
113
 
116
114
  ```bash
117
115
  SIBYL_API_BASE=https://staging.api.sibyllabs.org sibyl init
118
- SIBYL_ACTIVATE_BASE=https://staging.auth.sibyllabs.org sibyl init
116
+ SIBYL_ACTIVATE_BASE=https://staging.sibyllabs.org/plugin/activate sibyl init
119
117
  SIBYL_UPGRADE_BASE=https://staging.sibyllabs.org/plugin/upgrade sibyl upgrade
120
118
  ```
121
119
 
122
- `SIBYL_ACTIVATE_BASE` accepts both subdomain form (`https://auth.example.org` → path-based session URL: `https://auth.example.org/<uuid>`) and legacy path form (`https://example.org/plugin/activate` → query-based: `?session=<uuid>`). The CLI auto-detects from the hostname.
123
-
124
120
  ## Security
125
121
 
126
122
  - `credentials.json` is written atomically at mode 0600.
127
- - `session_token` is never printed in full — only a short slice.
123
+ - `session_token` is never printed in full: only a short slice.
128
124
  - No memory content ever transits these endpoints. The CLI never reads `memory.db` content; it only checks file size.
129
125
  - Wallet operations happen in the browser. The CLI sees only the resulting tier change.
130
126
 
@@ -22,39 +22,37 @@ sibyl health Run the SibylMemoryProvider self-check (schema versi
22
22
  ```bash
23
23
  $ sibyl init
24
24
 
25
- activation · three paths · pick whichever fits your device
25
+ Sibyl Memory Plugin · activation
26
26
 
27
- Session a1b2c3d4…e5f6
28
- Code 1 2 3 4 5 6 (use this in the email panel)
29
- Opening https://auth.sibyllabs.org/a1b2c3d4-…
27
+ Session: a1b2c3d4…e5f6
28
+ Opening: https://sibyllabs.org/plugin/activate?session=a1b2c3d4-…
30
29
 
31
- desktop wallet · email + code · or send USDC from any mobile wallet
32
- this terminal will pick up automatically when you bind.
30
+ Sign in with your wallet in the browser. This terminal will pick up automatically.
33
31
 
34
- ⠹ watching the network for your bind … 9:42 left
32
+ ⠹ waiting for browser activation … 9:42 left
35
33
  ```
36
34
 
37
- The browser opens to a dedicated short-URL auth subdomain (`auth.sibyllabs.org/<session-uuid>`). Pick a wallet from the EIP-6963 picker (Rabby / MetaMask / Coinbase Wallet / Frame / Brave / Rainbow), enter email + 6-digit code, or send a unique sub-cent USDC amount from any mobile wallet on Base. The terminal picks up the moment any of the three paths binds. Credentials are written to `~/.sibyl-memory/credentials.json` at mode 0600.
35
+ The browser opens. Sign a SIWE message with your wallet. The terminal picks up the moment the binding lands. Credentials are written to `~/.sibyl-memory/credentials.json` at mode 0600.
38
36
 
39
37
  ## Upgrade
40
38
 
41
39
  ```bash
42
40
  $ sibyl upgrade
43
41
 
44
- upgrade · lift the 2 MB free-tier cap
42
+ Sibyl Memory Plugin · upgrade
45
43
 
46
44
  Account a1b2c3d4…e5f6
47
45
  Current tier FREE
48
46
  Opening https://sibyllabs.org/plugin/upgrade?session=…
49
47
 
50
- two paths in the browser:
51
- 1. stake $SIBYL on Base (free unlimited if you qualify)
52
- 2. subscribe in USDC (monthly / quarterly / annual)
48
+ Two paths in your browser:
49
+ 1. Stake $SIBYL on Base (free unlimited if you qualify)
50
+ 2. Subscribe in USDC (monthly / quarterly / annual)
53
51
  ```
54
52
 
55
53
  In the browser:
56
- - **Stake** — connect your wallet (browser or Coinbase Smart Wallet), sign to bind, and the page checks your `$SIBYL` balance on Base. If you hold the threshold (default 100,000 $SIBYL liquid+staked, configurable), the local cap lifts.
57
- - **Subscribe** — pick monthly ($29) / quarterly ($79) / annual ($290) USDC, sign the transfer, the server records the subscription. Tier flips immediately.
54
+ - **Stake**: connect your wallet (browser or Coinbase Smart Wallet), sign to bind, and the page checks your `$SIBYL` balance on Base. If you hold the threshold (default 100,000 $SIBYL liquid+staked, configurable), the local cap lifts.
55
+ - **Subscribe**: pick monthly ($29) / quarterly ($79) / annual ($290) USDC, sign the transfer, the server records the subscription. Tier flips immediately.
58
56
 
59
57
  On either path, the CLI sees the tier change, rewrites `credentials.json`, and clears `tier_cache.json` so your next write picks up the new entitlement without delay.
60
58
 
@@ -89,16 +87,14 @@ For internal testing only:
89
87
 
90
88
  ```bash
91
89
  SIBYL_API_BASE=https://staging.api.sibyllabs.org sibyl init
92
- SIBYL_ACTIVATE_BASE=https://staging.auth.sibyllabs.org sibyl init
90
+ SIBYL_ACTIVATE_BASE=https://staging.sibyllabs.org/plugin/activate sibyl init
93
91
  SIBYL_UPGRADE_BASE=https://staging.sibyllabs.org/plugin/upgrade sibyl upgrade
94
92
  ```
95
93
 
96
- `SIBYL_ACTIVATE_BASE` accepts both subdomain form (`https://auth.example.org` → path-based session URL: `https://auth.example.org/<uuid>`) and legacy path form (`https://example.org/plugin/activate` → query-based: `?session=<uuid>`). The CLI auto-detects from the hostname.
97
-
98
94
  ## Security
99
95
 
100
96
  - `credentials.json` is written atomically at mode 0600.
101
- - `session_token` is never printed in full — only a short slice.
97
+ - `session_token` is never printed in full: only a short slice.
102
98
  - No memory content ever transits these endpoints. The CLI never reads `memory.db` content; it only checks file size.
103
99
  - Wallet operations happen in the browser. The CLI sees only the resulting tier change.
104
100
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "sibyl-memory-cli"
7
- version = "0.3.4"
7
+ version = "0.3.6"
8
8
  description = "Command-line interface for the Sibyl Memory Plugin. `sibyl init` activates, `sibyl upgrade` runs the staker / subscription flow, `sibyl status` shows current tier and DB stats, `sibyl whoami` gives a one-line account summary, `sibyl devices` lists active devices and supports per-device revoke."
9
9
  authors = [{ name = "SIBYL, Sibyl Labs LLC", email = "sibyl@sibyllabs.org" }]
10
10
  license = { text = "MIT" }
@@ -58,16 +58,16 @@ DEFAULT_DB_PATH = Path("~/.sibyl-memory/memory.db").expanduser()
58
58
  DEFAULT_TIER_CACHE_PATH = Path("~/.sibyl-memory/tier_cache.json").expanduser()
59
59
 
60
60
  POLL_INTERVAL_SEC = 3
61
- # v0.3.4 fix: bumped from 10min to 30min to match the server-side
62
- # PAIRING_TTL_SECONDS (also raised to 30min in session-init.js). At the prior
63
- # 10min CLI timeout the server kept the pairing code valid 5min longer than
64
- # the CLI was listening — users who took 10-15min to find the code in their
65
- # inbox would hit a silent-success failure: browser shows the success modal,
66
- # server binds the session, but the CLI has already exited and the local
67
- # credentials.json never lands. The user sees no error anywhere and concludes
68
- # "the plugin doesn't work." The two windows now match.
69
- INIT_TIMEOUT_SEC = 30 * 60 # 30 minutes for /init activation
70
- UPGRADE_TIMEOUT_SEC = 30 * 60 # 30 minutes for upgrade — wallet ux can be slow
61
+ # v0.3.5 fix: the CLI no longer carries its own activation deadline. The
62
+ # server's /session-init response includes pairing_ttl_seconds — the CLI
63
+ # polls until that timestamp, deferring to the server as the single source
64
+ # of truth. The constants below are fallbacks only, used when session-init
65
+ # fails to return a value (network error, schema drift). Drift between CLI
66
+ # and server is now impossible by construction; the prior 10min/15min
67
+ # silent-success gap can't recur because there is no CLI-side number to
68
+ # diverge from the server's.
69
+ INIT_TIMEOUT_FALLBACK_SEC = 30 * 60 # used only if session-init returns no TTL
70
+ UPGRADE_TIMEOUT_SEC = 30 * 60 # upgrade flow uses local constant — no server handshake to defer to
71
71
 
72
72
  # ---- Color / output ----------------------------------------------------
73
73
 
@@ -262,8 +262,16 @@ def cmd_init(args: argparse.Namespace) -> int:
262
262
  # Pre-register the session + pairing code hash with the server.
263
263
  # The code itself never leaves the user's machine until they type it
264
264
  # into the browser.
265
+ #
266
+ # v0.3.5: capture pairing_ttl_seconds from the response and use it as
267
+ # the activation deadline. Server is the single source of truth — if
268
+ # the server-side TTL ever changes, the CLI adopts the new value
269
+ # automatically without a re-publish. INIT_TIMEOUT_FALLBACK_SEC only
270
+ # applies when the call fails entirely (network error) or the response
271
+ # is missing the field (schema drift).
272
+ pairing_ttl_seconds = None
265
273
  try:
266
- http_request(
274
+ init_resp = http_request(
267
275
  "POST",
268
276
  "/api/plugin/session-init",
269
277
  body={
@@ -277,11 +285,17 @@ def cmd_init(args: argparse.Namespace) -> int:
277
285
  },
278
286
  timeout=10.0,
279
287
  )
288
+ if isinstance(init_resp, dict):
289
+ v = init_resp.get("pairing_ttl_seconds")
290
+ if isinstance(v, (int, float)) and v > 0:
291
+ pairing_ttl_seconds = int(v)
280
292
  except HttpError as e:
281
293
  # Non-fatal: SIWE path doesn't need the pairing code. If session-init
282
294
  # fails the user can still complete SIWE. Surface the warning.
283
295
  print(yellow(f"Warning: session-init failed ({e.status}). Wallet path still works; email path may not."))
284
296
 
297
+ activation_window_sec = pairing_ttl_seconds if pairing_ttl_seconds else INIT_TIMEOUT_FALLBACK_SEC
298
+
285
299
  print()
286
300
  print(a.section_header("activation", subtitle="three paths · pick whichever fits your device"))
287
301
  print()
@@ -301,7 +315,7 @@ def cmd_init(args: argparse.Namespace) -> int:
301
315
  pass
302
316
 
303
317
  # Poll /api/plugin/check
304
- deadline = time.time() + INIT_TIMEOUT_SEC
318
+ deadline = time.time() + activation_window_sec
305
319
  last_status = ""
306
320
  spinner = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
307
321
  spin_i = 0
@@ -811,6 +825,162 @@ def cmd_health(args: argparse.Namespace) -> int:
811
825
  return 0 if ok_state else 1
812
826
 
813
827
 
828
+ # ---- `sibyl update` ----------------------------------------------------
829
+
830
+ # Three user-facing packages we offer to upgrade. `mcp` is opt-in and not
831
+ # bundled by default — skip it here so we don't tell users to "update"
832
+ # something they may not have installed. Add it back when an `--include-mcp`
833
+ # flag is shipped.
834
+ UPDATE_PACKAGES = ("sibyl-memory-cli", "sibyl-memory-hermes", "sibyl-memory-client")
835
+
836
+
837
+ def _installed_version(pkg: str) -> str | None:
838
+ """Return the locally-installed version of a package, or None if not installed."""
839
+ try:
840
+ from importlib.metadata import PackageNotFoundError, version as _v
841
+ try:
842
+ return _v(pkg)
843
+ except PackageNotFoundError:
844
+ return None
845
+ except Exception:
846
+ return None
847
+
848
+
849
+ def _pypi_latest(pkg: str, timeout: float = 4.0) -> str | None:
850
+ """Hit PyPI's JSON endpoint for the latest published version. Best-effort."""
851
+ url = f"https://pypi.org/pypi/{pkg}/json"
852
+ try:
853
+ req = urllib.request.Request(url, headers={"User-Agent": f"sibyl-memory-cli/{_client_version()}"})
854
+ with urllib.request.urlopen(req, timeout=timeout) as r:
855
+ data = json.loads(r.read().decode("utf-8"))
856
+ return (data.get("info") or {}).get("version")
857
+ except Exception:
858
+ return None
859
+
860
+
861
+ def _ver_tuple(v: str) -> tuple:
862
+ """Lenient version tuple for comparison. Splits on '.', tolerates non-numeric tails."""
863
+ out = []
864
+ for part in (v or "").split("."):
865
+ digits = ""
866
+ for ch in part:
867
+ if ch.isdigit():
868
+ digits += ch
869
+ else:
870
+ break
871
+ out.append(int(digits) if digits else 0)
872
+ return tuple(out)
873
+
874
+
875
+ def _detect_install_method() -> str:
876
+ """Best-guess of how the CLI was installed — pipx / venv / system-pip / pep668-blocked."""
877
+ exe = sys.executable
878
+ if "/pipx/" in exe or "/.local/pipx/" in exe:
879
+ return "pipx"
880
+ if exe and ("venv" in exe.lower() or "virtualenv" in exe.lower() or os.environ.get("VIRTUAL_ENV")):
881
+ return "venv"
882
+ # Look for PEP 668 marker file
883
+ for parent in Path(exe).resolve().parents:
884
+ marker = parent / "lib" / "EXTERNALLY-MANAGED"
885
+ if marker.exists():
886
+ return "pep668"
887
+ marker2 = parent / "EXTERNALLY-MANAGED"
888
+ if marker2.exists():
889
+ return "pep668"
890
+ if str(parent) in ("/", "/home", "/usr"):
891
+ break
892
+ return "system"
893
+
894
+
895
+ def cmd_update(args: argparse.Namespace) -> int:
896
+ """Check installed package versions against PyPI, optionally apply upgrade."""
897
+ rows = []
898
+ any_outdated = False
899
+ for pkg in UPDATE_PACKAGES:
900
+ installed = _installed_version(pkg)
901
+ latest = _pypi_latest(pkg)
902
+ outdated = False
903
+ if installed and latest:
904
+ outdated = _ver_tuple(installed) < _ver_tuple(latest)
905
+ rows.append({"pkg": pkg, "installed": installed, "latest": latest, "outdated": outdated})
906
+ if outdated:
907
+ any_outdated = True
908
+
909
+ if args.json:
910
+ print(json.dumps({"packages": rows, "any_outdated": any_outdated}, indent=2))
911
+ return 0 if not any_outdated else 2
912
+
913
+ # ASCII output — keep it small and readable, follow `sibyl status` style.
914
+ print()
915
+ if any_outdated:
916
+ print(a.err_line("Updates available."))
917
+ else:
918
+ # Distinguish "all current" from "could not reach PyPI"
919
+ any_unreachable = any(r["latest"] is None for r in rows)
920
+ if any_unreachable:
921
+ print(a.dim("Could not reach PyPI for one or more packages — showing what we know."))
922
+ else:
923
+ print(a.success_line("All packages current."))
924
+ print()
925
+
926
+ name_w = max(len(r["pkg"]) for r in rows)
927
+ for r in rows:
928
+ installed = r["installed"] or "(not installed)"
929
+ latest = r["latest"] or "(unreachable)"
930
+ if r["outdated"]:
931
+ line = f" {yellow(r['pkg'].ljust(name_w))} {installed} → {green(latest)}"
932
+ elif r["installed"] is None:
933
+ line = f" {a.dim(r['pkg'].ljust(name_w))} {a.dim(installed)}"
934
+ else:
935
+ line = f" {r['pkg'].ljust(name_w)} {a.dim(installed)}"
936
+ print(line)
937
+ print()
938
+
939
+ if not any_outdated:
940
+ return 0
941
+
942
+ pip_cmd_pkgs = " ".join(r["pkg"] for r in rows if r["outdated"])
943
+ method = _detect_install_method()
944
+
945
+ if args.apply:
946
+ # Best-effort in-process pip invocation
947
+ import subprocess
948
+ pip_args = [sys.executable, "-m", "pip", "install", "-U", *pip_cmd_pkgs.split()]
949
+ if method == "pep668":
950
+ pip_args.append("--break-system-packages")
951
+ if method == "pipx":
952
+ # pipx is a separate tool; we can't drive it via `pip install`.
953
+ print(a.err_line("Detected pipx install. Run instead:"))
954
+ print(f" pipx upgrade {' '.join(r['pkg'] for r in rows if r['outdated'])}")
955
+ return 2
956
+ print(a.dim("Running: ") + " ".join(pip_args))
957
+ try:
958
+ rc = subprocess.call(pip_args)
959
+ except FileNotFoundError:
960
+ print(a.err_line("pip not found at " + sys.executable + " -m pip"))
961
+ return 2
962
+ if rc == 0:
963
+ print()
964
+ print(a.success_line("Upgrade complete. Re-run `sibyl update` to confirm."))
965
+ return rc
966
+
967
+ # Default: print the command, do not execute
968
+ print(a.dim("To upgrade, run:"))
969
+ if method == "pipx":
970
+ print(f" pipx upgrade {pip_cmd_pkgs}")
971
+ elif method == "pep668":
972
+ print(f" pip install --break-system-packages -U {pip_cmd_pkgs}")
973
+ print()
974
+ print(a.dim(" (Your Python flags itself as externally-managed under PEP 668.)"))
975
+ print(a.dim(" (Cleanest: install inside a venv. See https://beta.sibyllabs.org for the recommended path.)"))
976
+ else:
977
+ print(f" pip install -U {pip_cmd_pkgs}")
978
+ print()
979
+ print(a.dim("Or let sibyl run it:") + " sibyl update --apply")
980
+ print()
981
+ return 2 # exit 2 signals "outdated" without being a hard error
982
+
983
+
814
984
  # ---- Dispatch ----------------------------------------------------------
815
985
 
816
986
  def build_parser() -> argparse.ArgumentParser:
@@ -857,6 +1027,14 @@ def build_parser() -> argparse.ArgumentParser:
857
1027
  p_h = sub.add_parser("health", help="Run the provider self-check")
858
1028
  p_h.set_defaults(func=cmd_health)
859
1029
 
1030
+ p_update = sub.add_parser(
1031
+ "update",
1032
+ help="Check for newer sibyl-memory-* releases on PyPI (use --apply to upgrade)",
1033
+ )
1034
+ p_update.add_argument("--apply", action="store_true", help="Run pip install -U for the outdated packages")
1035
+ p_update.add_argument("--json", action="store_true", help="Machine-readable output")
1036
+ p_update.set_defaults(func=cmd_update)
1037
+
860
1038
  # v0.1.4: one-command auto-detect-and-wire setup for any agent stack
861
1039
  from .setup import cmd_setup
862
1040
  p_setup = sub.add_parser(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sibyl-memory-cli
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Command-line interface for the Sibyl Memory Plugin. `sibyl init` activates, `sibyl upgrade` runs the staker / subscription flow, `sibyl status` shows current tier and DB stats, `sibyl whoami` gives a one-line account summary, `sibyl devices` lists active devices and supports per-device revoke.
5
5
  Author-email: "SIBYL, Sibyl Labs LLC" <sibyl@sibyllabs.org>
6
6
  License: MIT
@@ -48,39 +48,37 @@ sibyl health Run the SibylMemoryProvider self-check (schema versi
48
48
  ```bash
49
49
  $ sibyl init
50
50
 
51
- activation · three paths · pick whichever fits your device
51
+ Sibyl Memory Plugin · activation
52
52
 
53
- Session a1b2c3d4…e5f6
54
- Code 1 2 3 4 5 6 (use this in the email panel)
55
- Opening https://auth.sibyllabs.org/a1b2c3d4-…
53
+ Session: a1b2c3d4…e5f6
54
+ Opening: https://sibyllabs.org/plugin/activate?session=a1b2c3d4-…
56
55
 
57
- desktop wallet · email + code · or send USDC from any mobile wallet
58
- this terminal will pick up automatically when you bind.
56
+ Sign in with your wallet in the browser. This terminal will pick up automatically.
59
57
 
60
- ⠹ watching the network for your bind … 9:42 left
58
+ ⠹ waiting for browser activation … 9:42 left
61
59
  ```
62
60
 
63
- The browser opens to a dedicated short-URL auth subdomain (`auth.sibyllabs.org/<session-uuid>`). Pick a wallet from the EIP-6963 picker (Rabby / MetaMask / Coinbase Wallet / Frame / Brave / Rainbow), enter email + 6-digit code, or send a unique sub-cent USDC amount from any mobile wallet on Base. The terminal picks up the moment any of the three paths binds. Credentials are written to `~/.sibyl-memory/credentials.json` at mode 0600.
61
+ The browser opens. Sign a SIWE message with your wallet. The terminal picks up the moment the binding lands. Credentials are written to `~/.sibyl-memory/credentials.json` at mode 0600.
64
62
 
65
63
  ## Upgrade
66
64
 
67
65
  ```bash
68
66
  $ sibyl upgrade
69
67
 
70
- upgrade · lift the 2 MB free-tier cap
68
+ Sibyl Memory Plugin · upgrade
71
69
 
72
70
  Account a1b2c3d4…e5f6
73
71
  Current tier FREE
74
72
  Opening https://sibyllabs.org/plugin/upgrade?session=…
75
73
 
76
- two paths in the browser:
77
- 1. stake $SIBYL on Base (free unlimited if you qualify)
78
- 2. subscribe in USDC (monthly / quarterly / annual)
74
+ Two paths in your browser:
75
+ 1. Stake $SIBYL on Base (free unlimited if you qualify)
76
+ 2. Subscribe in USDC (monthly / quarterly / annual)
79
77
  ```
80
78
 
81
79
  In the browser:
82
- - **Stake** — connect your wallet (browser or Coinbase Smart Wallet), sign to bind, and the page checks your `$SIBYL` balance on Base. If you hold the threshold (default 100,000 $SIBYL liquid+staked, configurable), the local cap lifts.
83
- - **Subscribe** — pick monthly ($29) / quarterly ($79) / annual ($290) USDC, sign the transfer, the server records the subscription. Tier flips immediately.
80
+ - **Stake**: connect your wallet (browser or Coinbase Smart Wallet), sign to bind, and the page checks your `$SIBYL` balance on Base. If you hold the threshold (default 100,000 $SIBYL liquid+staked, configurable), the local cap lifts.
81
+ - **Subscribe**: pick monthly ($29) / quarterly ($79) / annual ($290) USDC, sign the transfer, the server records the subscription. Tier flips immediately.
84
82
 
85
83
  On either path, the CLI sees the tier change, rewrites `credentials.json`, and clears `tier_cache.json` so your next write picks up the new entitlement without delay.
86
84
 
@@ -115,16 +113,14 @@ For internal testing only:
115
113
 
116
114
  ```bash
117
115
  SIBYL_API_BASE=https://staging.api.sibyllabs.org sibyl init
118
- SIBYL_ACTIVATE_BASE=https://staging.auth.sibyllabs.org sibyl init
116
+ SIBYL_ACTIVATE_BASE=https://staging.sibyllabs.org/plugin/activate sibyl init
119
117
  SIBYL_UPGRADE_BASE=https://staging.sibyllabs.org/plugin/upgrade sibyl upgrade
120
118
  ```
121
119
 
122
- `SIBYL_ACTIVATE_BASE` accepts both subdomain form (`https://auth.example.org` → path-based session URL: `https://auth.example.org/<uuid>`) and legacy path form (`https://example.org/plugin/activate` → query-based: `?session=<uuid>`). The CLI auto-detects from the hostname.
123
-
124
120
  ## Security
125
121
 
126
122
  - `credentials.json` is written atomically at mode 0600.
127
- - `session_token` is never printed in full — only a short slice.
123
+ - `session_token` is never printed in full: only a short slice.
128
124
  - No memory content ever transits these endpoints. The CLI never reads `memory.db` content; it only checks file size.
129
125
  - Wallet operations happen in the browser. The CLI sees only the resulting tier change.
130
126
 
@@ -36,7 +36,7 @@ from sibyl_memory_cli.setup import ( # noqa: E402
36
36
  # ----------------------------------------------------------------------
37
37
 
38
38
  def _stub_install_plugin(hermes_home: str):
39
- """Replacement for sibyl_memory_hermes.install_plugin.install — drops a fake
39
+ """Replacement for sibyl_memory_hermes.install_plugin.install: drops a fake
40
40
  adapter file so the wirer sees plugin_installed=True afterwards."""
41
41
  plugin_dir = Path(hermes_home) / "plugins" / "sibyl"
42
42
  plugin_dir.mkdir(parents=True, exist_ok=True)