cortexdb-cli 0.5.4__py3-none-any.whl → 0.6.0__py3-none-any.whl

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.
cortexdb_cli/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- """CortexDB CLI - command-line interface for the v1 memory API."""
2
-
3
- __version__ = "0.5.4"
1
+ """CortexDB CLI - command-line interface for the v1 memory API."""
2
+
3
+ __version__ = "0.6.0"
@@ -1 +1 @@
1
- """CortexDB CLI command modules."""
1
+ """CortexDB CLI command modules."""
@@ -83,11 +83,29 @@ def auth_signup(ctx: click.Context, endpoint: str, save_state_flag: bool) -> Non
83
83
  show_default=True,
84
84
  help="Token lifetime in seconds (server enforces a tenant ceiling).",
85
85
  )
86
- @click.option("--scope", "scopes", multiple=True, help="`scopes` claim - repeat for multiple.")
86
+ @click.option(
87
+ "--scope",
88
+ "scope",
89
+ default=None,
90
+ help="Scope path where YOUR auth.mint is checked (omit for a deployment-wide "
91
+ "check). Does not confine the minted token - use --confine.",
92
+ )
93
+ @click.option(
94
+ "--confine",
95
+ "confine",
96
+ multiple=True,
97
+ metavar="CAP:PREFIX",
98
+ help="Confine a capability (or capability prefix) to a scope subtree, e.g. "
99
+ "scope.read:org:acme/team:b/* - repeat for multiple. Sent as the `scopes` claim.",
100
+ )
87
101
  @click.pass_context
88
102
  @handle_errors
89
103
  def auth_tokens(
90
- ctx: click.Context, subject: str, ttl_seconds: int, scopes: tuple[str, ...]
104
+ ctx: click.Context,
105
+ subject: str,
106
+ ttl_seconds: int,
107
+ scope: str | None,
108
+ confine: tuple[str, ...],
91
109
  ) -> None:
92
110
  """POST /v1/auth/tokens - mint a service-account token.
93
111
 
@@ -95,12 +113,27 @@ def auth_tokens(
95
113
  have it - you must hold an IdP-issued token (or a token previously
96
114
  minted by an account that does).
97
115
 
98
- cortexdb auth tokens --subject user:alice --ttl 3600 --scope org:acme/user:alice
116
+ --scope only says where your auth.mint is evaluated. The token is
117
+ confined only by --confine entries (CAP:PREFIX); the server rejects an
118
+ entry whose capability part names no capability.
119
+
120
+ cortexdb auth tokens --subject agent:b --confine scope.read:org:acme/team:b/*
99
121
  """
122
+ # Before --confine existed, --scope carried `scopes` entries. A value
123
+ # that starts with a capability (dots before the first ':') is one of
124
+ # those, not a scope path; point at the right flag instead of letting
125
+ # the server 422 on the scope grammar.
126
+ if scope is not None and "." in scope.split(":", 1)[0]:
127
+ raise click.UsageError(
128
+ f"--scope {scope!r} looks like a CAP:PREFIX confinement entry; "
129
+ "pass it with --confine"
130
+ )
100
131
  cfg = ctx.obj
101
132
  body: dict[str, object] = {"subject": subject, "ttl_seconds": ttl_seconds}
102
- if scopes:
103
- body["scopes"] = list(scopes)
133
+ if scope is not None:
134
+ body["scope"] = scope
135
+ if confine:
136
+ body["scopes"] = list(confine)
104
137
  with get_client(cfg) as client:
105
138
  if not is_json_mode(cfg):
106
139
  with spinner("Minting..."):
@@ -1,50 +1,50 @@
1
- """cortexdb config -- View and edit configuration."""
2
-
3
- from __future__ import annotations
4
-
5
- import click
6
-
7
- from cortexdb_cli.config import config_path, load_config, save_config
8
- from cortexdb_cli.output import console, print_json, is_json_mode
9
-
10
-
11
- @click.group("config")
12
- def config_group() -> None:
13
- """View and manage CLI configuration."""
14
-
15
-
16
- @config_group.command("show")
17
- @click.pass_context
18
- def config_show(ctx: click.Context) -> None:
19
- """Show current configuration."""
20
- cfg = load_config()
21
- if is_json_mode(ctx.obj):
22
- # Redact API key in JSON output
23
- safe = dict(cfg)
24
- if "api_key" in safe:
25
- key = safe["api_key"]
26
- safe["api_key"] = key[:8] + "..." + key[-4:] if len(key) > 12 else "***"
27
- print_json(safe)
28
- else:
29
- console.print(f"[dim]Config file: {config_path()}[/dim]\n")
30
- for k, v in cfg.items():
31
- if k == "api_key" and v:
32
- v = v[:8] + "..." + v[-4:] if len(v) > 12 else "***"
33
- console.print(f" [bold]{k}[/bold] = {v}")
34
- console.print()
35
-
36
-
37
- @config_group.command("set")
38
- @click.argument("key")
39
- @click.argument("value")
40
- def config_set(key: str, value: str) -> None:
41
- """Set a configuration value.
42
-
43
- cortexdb config set api_key cx_live_...
44
-
45
- cortexdb config set endpoint https://api-v1.cortexdb.ai
46
- """
47
- cfg = load_config()
48
- cfg[key] = value
49
- save_config("default", cfg)
50
- console.print(f"[success]Set {key}[/success]")
1
+ """cortexdb config -- View and edit configuration."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from cortexdb_cli.config import config_path, load_config, save_config
8
+ from cortexdb_cli.output import console, print_json, is_json_mode
9
+
10
+
11
+ @click.group("config")
12
+ def config_group() -> None:
13
+ """View and manage CLI configuration."""
14
+
15
+
16
+ @config_group.command("show")
17
+ @click.pass_context
18
+ def config_show(ctx: click.Context) -> None:
19
+ """Show current configuration."""
20
+ cfg = load_config()
21
+ if is_json_mode(ctx.obj):
22
+ # Redact API key in JSON output
23
+ safe = dict(cfg)
24
+ if "api_key" in safe:
25
+ key = safe["api_key"]
26
+ safe["api_key"] = key[:8] + "..." + key[-4:] if len(key) > 12 else "***"
27
+ print_json(safe)
28
+ else:
29
+ console.print(f"[dim]Config file: {config_path()}[/dim]\n")
30
+ for k, v in cfg.items():
31
+ if k == "api_key" and v:
32
+ v = v[:8] + "..." + v[-4:] if len(v) > 12 else "***"
33
+ console.print(f" [bold]{k}[/bold] = {v}")
34
+ console.print()
35
+
36
+
37
+ @config_group.command("set")
38
+ @click.argument("key")
39
+ @click.argument("value")
40
+ def config_set(key: str, value: str) -> None:
41
+ """Set a configuration value.
42
+
43
+ cortexdb config set api_key cx_live_...
44
+
45
+ cortexdb config set endpoint https://api-v1.cortexdb.ai
46
+ """
47
+ cfg = load_config()
48
+ cfg[key] = value
49
+ save_config("default", cfg)
50
+ console.print(f"[success]Set {key}[/success]")
cortexdb_cli/errors.py CHANGED
@@ -1,96 +1,96 @@
1
- """Error handling for CortexDB CLI commands."""
2
-
3
- from __future__ import annotations
4
-
5
- import functools
6
- import sys
7
- from typing import Any, Callable
8
-
9
- import click
10
- import httpx
11
-
12
- from cortexdb_cli.output import err_console
13
-
14
-
15
- def escape_safe(s: Any) -> str:
16
- """Rich-safe escape used in the error decorator (avoids circular imports)."""
17
- from rich.markup import escape
18
-
19
- return escape(str(s))
20
-
21
-
22
- def handle_errors(fn: Callable[..., Any]) -> Callable[..., Any]:
23
- """Decorator that catches common errors and prints friendly messages."""
24
-
25
- @functools.wraps(fn)
26
- def wrapper(*args: Any, **kwargs: Any) -> Any:
27
- try:
28
- return fn(*args, **kwargs)
29
- except httpx.HTTPStatusError as exc:
30
- status = exc.response.status_code
31
- body: dict[str, Any] = {}
32
- try:
33
- body = exc.response.json()
34
- except Exception:
35
- body = {}
36
- code = body.get("error_code") or body.get("code") or ""
37
- msg = body.get("message") or body.get("error") or exc.response.text[:200]
38
-
39
- if status == 401:
40
- if code in ("TOKEN_EXPIRED", "INVALID_TOKEN_SIGNATURE", "MISSING_TOKEN"):
41
- err_console.print(
42
- f"[error]Token rejected ({code}).[/error] "
43
- "Run [bold]cortexdb init[/bold] to mint a fresh one."
44
- )
45
- elif code == "actor_mismatch":
46
- err_console.print(
47
- "[error]Actor mismatch.[/error] The X-Cortex-Actor header "
48
- "doesn't match the token's subject. Re-run "
49
- "[bold]cortexdb init[/bold] to re-bind."
50
- )
51
- else:
52
- err_console.print(
53
- f"[error]Authentication failed:[/error] {msg}\n"
54
- "Run [bold]cortexdb init[/bold] to set up credentials."
55
- )
56
- elif status == 403:
57
- # The v1 surface always cites the missing capability.
58
- details = body.get("details") or {}
59
- cap = details.get("capability") or ""
60
- tier = details.get("decided_by_tier") or ""
61
- if cap:
62
- err_console.print(
63
- f"[error]Denied by {escape_safe(tier)}:[/error] "
64
- f"missing capability [bold]{escape_safe(cap)}[/bold]."
65
- )
66
- if cap == "diagnostics.read":
67
- err_console.print(
68
- "[dim]Hint: pass [bold]--diagnostics none[/bold] (free tier).[/dim]"
69
- )
70
- else:
71
- err_console.print(f"[error]Forbidden:[/error] {msg}")
72
- elif status == 429:
73
- retry = exc.response.headers.get("Retry-After", "?")
74
- err_console.print(f"[error]Rate limited.[/error] Retry after {retry}s.")
75
- elif status == 402:
76
- err_console.print(f"[warning]Feature not available on your tier.[/warning] {msg}")
77
- else:
78
- err_console.print(f"[error]API error (HTTP {status}, {code}):[/error] {msg}")
79
- sys.exit(1)
80
- except httpx.ConnectError:
81
- err_console.print(
82
- "[error]Cannot connect to CortexDB.[/error] "
83
- "Check your endpoint and network."
84
- )
85
- sys.exit(2)
86
- except httpx.TimeoutException:
87
- err_console.print("[error]Request timed out.[/error]")
88
- sys.exit(2)
89
- except click.Abort:
90
- err_console.print("\n[dim]Aborted.[/dim]")
91
- sys.exit(130)
92
- except KeyboardInterrupt:
93
- err_console.print("\n[dim]Interrupted.[/dim]")
94
- sys.exit(130)
95
-
96
- return wrapper
1
+ """Error handling for CortexDB CLI commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import functools
6
+ import sys
7
+ from typing import Any, Callable
8
+
9
+ import click
10
+ import httpx
11
+
12
+ from cortexdb_cli.output import err_console
13
+
14
+
15
+ def escape_safe(s: Any) -> str:
16
+ """Rich-safe escape used in the error decorator (avoids circular imports)."""
17
+ from rich.markup import escape
18
+
19
+ return escape(str(s))
20
+
21
+
22
+ def handle_errors(fn: Callable[..., Any]) -> Callable[..., Any]:
23
+ """Decorator that catches common errors and prints friendly messages."""
24
+
25
+ @functools.wraps(fn)
26
+ def wrapper(*args: Any, **kwargs: Any) -> Any:
27
+ try:
28
+ return fn(*args, **kwargs)
29
+ except httpx.HTTPStatusError as exc:
30
+ status = exc.response.status_code
31
+ body: dict[str, Any] = {}
32
+ try:
33
+ body = exc.response.json()
34
+ except Exception:
35
+ body = {}
36
+ code = body.get("error_code") or body.get("code") or ""
37
+ msg = body.get("message") or body.get("error") or exc.response.text[:200]
38
+
39
+ if status == 401:
40
+ if code in ("TOKEN_EXPIRED", "INVALID_TOKEN_SIGNATURE", "MISSING_TOKEN"):
41
+ err_console.print(
42
+ f"[error]Token rejected ({code}).[/error] "
43
+ "Run [bold]cortexdb init[/bold] to mint a fresh one."
44
+ )
45
+ elif code == "actor_mismatch":
46
+ err_console.print(
47
+ "[error]Actor mismatch.[/error] The X-Cortex-Actor header "
48
+ "doesn't match the token's subject. Re-run "
49
+ "[bold]cortexdb init[/bold] to re-bind."
50
+ )
51
+ else:
52
+ err_console.print(
53
+ f"[error]Authentication failed:[/error] {msg}\n"
54
+ "Run [bold]cortexdb init[/bold] to set up credentials."
55
+ )
56
+ elif status == 403:
57
+ # The v1 surface always cites the missing capability.
58
+ details = body.get("details") or {}
59
+ cap = details.get("capability") or ""
60
+ tier = details.get("decided_by_tier") or ""
61
+ if cap:
62
+ err_console.print(
63
+ f"[error]Denied by {escape_safe(tier)}:[/error] "
64
+ f"missing capability [bold]{escape_safe(cap)}[/bold]."
65
+ )
66
+ if cap == "diagnostics.read":
67
+ err_console.print(
68
+ "[dim]Hint: pass [bold]--diagnostics none[/bold] (free tier).[/dim]"
69
+ )
70
+ else:
71
+ err_console.print(f"[error]Forbidden:[/error] {msg}")
72
+ elif status == 429:
73
+ retry = exc.response.headers.get("Retry-After", "?")
74
+ err_console.print(f"[error]Rate limited.[/error] Retry after {retry}s.")
75
+ elif status == 402:
76
+ err_console.print(f"[warning]Feature not available on your tier.[/warning] {msg}")
77
+ else:
78
+ err_console.print(f"[error]API error (HTTP {status}, {code}):[/error] {msg}")
79
+ sys.exit(1)
80
+ except httpx.ConnectError:
81
+ err_console.print(
82
+ "[error]Cannot connect to CortexDB.[/error] "
83
+ "Check your endpoint and network."
84
+ )
85
+ sys.exit(2)
86
+ except httpx.TimeoutException:
87
+ err_console.print("[error]Request timed out.[/error]")
88
+ sys.exit(2)
89
+ except click.Abort:
90
+ err_console.print("\n[dim]Aborted.[/dim]")
91
+ sys.exit(130)
92
+ except KeyboardInterrupt:
93
+ err_console.print("\n[dim]Interrupted.[/dim]")
94
+ sys.exit(130)
95
+
96
+ return wrapper
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cortexdb-cli
3
- Version: 0.5.4
3
+ Version: 0.6.0
4
4
  Summary: Command-line interface for CortexDB — the long-term memory layer for AI.
5
5
  Project-URL: Homepage, https://cortexdb.ai
6
6
  Project-URL: Documentation, https://cortexdb.ai/docs
@@ -1,22 +1,22 @@
1
- cortexdb_cli/__init__.py,sha256=R-3t2jLxSGdc1xNb9pu6tbckqSCXYYrp2zCV8lOqSsE,93
1
+ cortexdb_cli/__init__.py,sha256=T_HhRfBox9rKO8_8r9aytlz_MMBHnWF-LimTRVTQFss,90
2
2
  cortexdb_cli/client.py,sha256=Abgv8CLbhNYKG0QqHjsdGFXWZuVhke4Zuc4TAOuGjG4,3086
3
3
  cortexdb_cli/config.py,sha256=ee1qmOfqMwilL81ujaqjf6TjjlrFG7WZAHRIgeyif9U,5690
4
- cortexdb_cli/errors.py,sha256=xwxAWYY7KtNa-e7dyLQECnsKcciVcKC41CXFFF_-0VU,3924
4
+ cortexdb_cli/errors.py,sha256=yk6J6Gm-lBVsIPVl10ezECqG0Wds3vDOCUqKx7ct7z4,3828
5
5
  cortexdb_cli/harness_packs_data.py,sha256=38oyPX47a_S3BRgiNxml37auhIUyWafeJ_MtApX9tWE,10693
6
6
  cortexdb_cli/main.py,sha256=xnsAgPQMvZOuRxgxREsEN1QM8wE9DSUN2Cd0kxSAU3A,10014
7
7
  cortexdb_cli/onboarding.py,sha256=kjCxhMOY2nReNyEfGGGsmHO6_j4CXS5o2LIwzpFzIok,17527
8
8
  cortexdb_cli/output.py,sha256=9NvXhX3ncCIgP9BMzt7c9VReQc0O1FUbQTwj2vUQ2JI,13611
9
9
  cortexdb_cli/state.py,sha256=pfslyJ9xuZno4n13-maJ0qKmD7nxWkFznjrVvItYK4I,1527
10
- cortexdb_cli/commands/__init__.py,sha256=TeF4ieMQkbgBr9Fco9VAiIZvKQ4vdg4yhkz4fvwfToc,37
10
+ cortexdb_cli/commands/__init__.py,sha256=Uqz-6IAeIxrSS94FHI6Q0iqDf_2MQeHKY0LaHgEC4HU,36
11
11
  cortexdb_cli/commands/admin.py,sha256=KzCiRSLeDkVHGa_V1AYwk4--KVkC2GI9giZc0LDN8pY,21633
12
12
  cortexdb_cli/commands/answer.py,sha256=41Yl9pjfODyRnWzYL68nRA7GPDb73Cccj0Kq-8sLv9k,3343
13
13
  cortexdb_cli/commands/audit.py,sha256=br9ksMeR4dHBkfvNvGG4mjYdhh7E1IRxJ1FOX1klckQ,2620
14
- cortexdb_cli/commands/auth.py,sha256=6YoKl5mZcyep2FRMrnXRv_EKvblC6H2vKfVOBzDXgpo,4505
14
+ cortexdb_cli/commands/auth.py,sha256=SE9nUfE6C9ix8IgBi4efDI_o1GfE_ezSk40LzzOZKAE,5655
15
15
  cortexdb_cli/commands/beliefs.py,sha256=akjOg2drLqQKlrGVL-Z6oLdP6TY-4YHe3yg9lJpQPH4,6180
16
16
  cortexdb_cli/commands/claims.py,sha256=NApq0i8NXX0j7uwnHdHtendnblMBnHh0OXlwQwB-tCQ,3720
17
17
  cortexdb_cli/commands/code.py,sha256=QMlE9RwNFEQsRqQrUvqlKA2c0v7sKLU2vcyQiW1uZ10,10719
18
18
  cortexdb_cli/commands/compose.py,sha256=i9fEugN818mt1t8nWsAxvv9h0nNLvBAf-WZZIl64xiw,3853
19
- cortexdb_cli/commands/config_cmd.py,sha256=MCb5XoulnGLkeOzYY54R_hFnTxGWAboLARSe8zAEhXA,1522
19
+ cortexdb_cli/commands/config_cmd.py,sha256=i-LICYVUrFVqFICeS-ddwI-aX7pxzxdxL2I8hc3tW3A,1472
20
20
  cortexdb_cli/commands/conflicts.py,sha256=s4sp3BtPzPEy3T73inx68vhmucvjh-zQRnWYsJ_ADQQ,10506
21
21
  cortexdb_cli/commands/entities.py,sha256=aPnyVnK6LlKkc7t_aEuplqnX5K4DH6jToWTVCAG_GM8,6055
22
22
  cortexdb_cli/commands/episodes.py,sha256=bMnGOT4Kzz6b5o3JVhcCEsOmEzqoVrxUFPhKCn70TC8,4131
@@ -34,7 +34,7 @@ cortexdb_cli/commands/scopes.py,sha256=Cx_zV-zyIrnDxwp_I4AX_Teh7UFu4EdvDOYe9hTEF
34
34
  cortexdb_cli/commands/search.py,sha256=4wsgNU47deb0x5o7xC9eWlhwdIIbz56q7ne8GIiCmYo,1953
35
35
  cortexdb_cli/commands/understanding.py,sha256=BktqOn-GloOzSSGJGK9xh5kGCpxcrwkNogtXhjj4pBI,5371
36
36
  cortexdb_cli/commands/wire.py,sha256=M3UUHScmvr_MDrX_RaMBg-YLTcHKUq9woNkT4KHJKfs,8131
37
- cortexdb_cli-0.5.4.dist-info/METADATA,sha256=wNeADaguuwEJjnQgsLpwnOnMKaWGSZMfnKfgXARS2JU,5234
38
- cortexdb_cli-0.5.4.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
39
- cortexdb_cli-0.5.4.dist-info/entry_points.txt,sha256=h3sEbdqqvtv8LBbRLUgybYPo9N2lpU85fQUSh5ToEvs,51
40
- cortexdb_cli-0.5.4.dist-info/RECORD,,
37
+ cortexdb_cli-0.6.0.dist-info/METADATA,sha256=2-OQEQC65mQW-q4Y9hbYpMvbF0rkExxH-qAAj3UOb6M,5234
38
+ cortexdb_cli-0.6.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
39
+ cortexdb_cli-0.6.0.dist-info/entry_points.txt,sha256=h3sEbdqqvtv8LBbRLUgybYPo9N2lpU85fQUSh5ToEvs,51
40
+ cortexdb_cli-0.6.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.32.0
2
+ Generator: hatchling 1.32.4
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any