cortexdb-cli 0.5.1__tar.gz → 0.5.3__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 (41) hide show
  1. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/PKG-INFO +20 -4
  2. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/README.md +106 -90
  3. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/__init__.py +1 -1
  4. cortexdb_cli-0.5.3/cortexdb_cli/commands/init.py +230 -0
  5. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/pyproject.toml +1 -1
  6. cortexdb_cli-0.5.1/cortexdb_cli/commands/init.py +0 -129
  7. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/.gitignore +0 -0
  8. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/client.py +0 -0
  9. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/__init__.py +0 -0
  10. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/admin.py +0 -0
  11. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/answer.py +0 -0
  12. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/audit.py +0 -0
  13. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/auth.py +0 -0
  14. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/beliefs.py +0 -0
  15. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/claims.py +0 -0
  16. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/code.py +0 -0
  17. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/compose.py +0 -0
  18. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/config_cmd.py +0 -0
  19. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/conflicts.py +0 -0
  20. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/entities.py +0 -0
  21. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/episodes.py +0 -0
  22. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/experience.py +0 -0
  23. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/export_cmd.py +0 -0
  24. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/facts.py +0 -0
  25. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/forget.py +0 -0
  26. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/import_cmd.py +0 -0
  27. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/interactive.py +0 -0
  28. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/onboard.py +0 -0
  29. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/policy.py +0 -0
  30. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/recall.py +0 -0
  31. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/scopes.py +0 -0
  32. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/search.py +0 -0
  33. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/understanding.py +0 -0
  34. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/commands/wire.py +0 -0
  35. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/config.py +0 -0
  36. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/errors.py +0 -0
  37. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/harness_packs_data.py +0 -0
  38. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/main.py +0 -0
  39. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/onboarding.py +0 -0
  40. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/output.py +0 -0
  41. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.3}/cortexdb_cli/state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cortexdb-cli
3
- Version: 0.5.1
3
+ Version: 0.5.3
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
@@ -62,8 +62,22 @@ cortexdb recall "Q3 revenue?"
62
62
  cortexdb
63
63
  ```
64
64
 
65
- The signup token has a 7-day TTL on the free tier. Re-run `cortexdb init`
66
- to refresh, or pass `--api-key` + `--actor` to use a token from your IdP.
65
+ The signup token has a 7-day TTL on the free tier.
66
+
67
+ **Re-running `cortexdb init` does not refresh it.** `init` reuses an existing
68
+ identity and changes nothing; against an expired one it refuses and exits 3.
69
+ That is deliberate: a new identity is a new *brain*, and memories written
70
+ under the old actor stay where they are, unreachable from the new config.
71
+
72
+ When a token expires you have two safe options:
73
+
74
+ - `cortexdb init --api-key <token> --actor <actor>` — keep the same brain
75
+ with a token you already hold (e.g. from your IdP).
76
+ - `cortexdb init --force` — deliberately start a NEW brain. The previous
77
+ identity is backed up to `state.json.replaced` first.
78
+
79
+ For a second brain alongside the first, isolate instead of replacing:
80
+ `cortexdb --config-dir <dir> init`, or set `CORTEXDB_HOME`.
67
81
 
68
82
  ## Commands
69
83
 
@@ -112,7 +126,9 @@ Force JSON mode any time with `--json`.
112
126
  - The v1 API requires both `Authorization: Bearer <PASETO>` and `X-Cortex-Actor`.
113
127
  The CLI stamps both on every request — you never need to pass them by hand.
114
128
  - 401s show the specific error code (`TOKEN_EXPIRED`, `actor_mismatch`, …)
115
- and the remediation: usually `cortexdb init`.
129
+ and the remediation. Note that `TOKEN_EXPIRED` is **not** fixed by re-running
130
+ `cortexdb init` — see the identity note above; that would mint a different
131
+ brain rather than restore access to yours.
116
132
  - 403s cite the missing capability and the policy tier that denied. If a
117
133
  recall returns `diagnostics.read denied`, pass `--diagnostics none` —
118
134
  free-tier tokens don't carry the `diagnostics.read` capability.
@@ -1,90 +1,106 @@
1
- # cortexdb-cli
2
-
3
- Command-line interface for [CortexDB](https://cortexdb.ai) — the long-term memory layer for AI systems.
4
-
5
- Wraps the v1 HTTP API in a small, fast Click + Rich CLI. Anonymous signup
6
- in one command, no email or card; works against the public SaaS or any
7
- self-hosted CortexDB deployment.
8
-
9
- ## Install
10
-
11
- ```bash
12
- pip install cortexdb-cli
13
- ```
14
-
15
- Requires Python 3.10+.
16
-
17
- ## Quickstart
18
-
19
- ```bash
20
- # 1. One-time setup — anonymous signup, writes ~/.cortexdb/state.json
21
- cortexdb init
22
-
23
- # 2. Store a memory
24
- cortexdb remember "Q3 revenue: $2.4M. We're on track for $10M ARR by year-end."
25
-
26
- # 3. Recall it
27
- cortexdb recall "Q3 revenue?"
28
-
29
- # 4. Or just type — drops into a REPL
30
- cortexdb
31
- ```
32
-
33
- The signup token has a 7-day TTL on the free tier. Re-run `cortexdb init`
34
- to refresh, or pass `--api-key` + `--actor` to use a token from your IdP.
35
-
36
- ## Commands
37
-
38
- ```
39
- cortexdb init anonymous signup; persist token + actor
40
- cortexdb remember "..." store a memory (POST /v1/experience)
41
- cortexdb recall "..." recall a stratified context pack
42
- cortexdb search "..." granular event-level search
43
- cortexdb forget --memory-id ... delete with audit
44
- cortexdb episodes {list,get,delete} episode CRUD
45
- cortexdb entities {list,get,link} entity rollups over the Facts layer
46
- cortexdb admin {health,usage,layers} diagnostics
47
- cortexdb import data.json bulk-load (JSON / JSONL / CSV / TXT)
48
- cortexdb export -o backup.json export memories
49
- cortexdb config {show,set} view / edit ~/.cortexdb/config.toml
50
- cortexdb interactive REPL
51
- ```
52
-
53
- ## Configuration
54
-
55
- Three layers, in precedence order:
56
-
57
- 1. **CLI flags** — `--api-key`, `--endpoint`, `--actor`, `--scope`, `--profile`
58
- 2. **Environment** — `CORTEXDB_API_KEY`, `CORTEXDB_URL`, `CORTEXDB_ACTOR`, `CORTEXDB_SCOPE`
59
- 3. **Persisted state**:
60
- - `~/.cortexdb/config.toml` — endpoint, profile names (human-editable)
61
- - `~/.cortexdb/state.json` — token, actor, scope, expiry (managed by `init`, 0600 on POSIX)
62
-
63
- The state.json format matches the cortexdb-mcp 0.3.0 server's, so you can
64
- copy state across to use both tools from the same anonymous identity.
65
-
66
- ## Pipe-friendly
67
-
68
- Auto-detects when stdout isn't a TTY and emits JSON:
69
-
70
- ```bash
71
- cortexdb recall "Q3 revenue" | jq .context_block
72
- cortexdb entities list | jq '.[].subject'
73
- echo "remember this" | cortexdb remember -
74
- ```
75
-
76
- Force JSON mode any time with `--json`.
77
-
78
- ## Auth notes
79
-
80
- - The v1 API requires both `Authorization: Bearer <PASETO>` and `X-Cortex-Actor`.
81
- The CLI stamps both on every request — you never need to pass them by hand.
82
- - 401s show the specific error code (`TOKEN_EXPIRED`, `actor_mismatch`, …)
83
- and the remediation: usually `cortexdb init`.
84
- - 403s cite the missing capability and the policy tier that denied. If a
85
- recall returns `diagnostics.read denied`, pass `--diagnostics none` —
86
- free-tier tokens don't carry the `diagnostics.read` capability.
87
-
88
- ## License
89
-
90
- Apache-2.0
1
+ # cortexdb-cli
2
+
3
+ Command-line interface for [CortexDB](https://cortexdb.ai) — the long-term memory layer for AI systems.
4
+
5
+ Wraps the v1 HTTP API in a small, fast Click + Rich CLI. Anonymous signup
6
+ in one command, no email or card; works against the public SaaS or any
7
+ self-hosted CortexDB deployment.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pip install cortexdb-cli
13
+ ```
14
+
15
+ Requires Python 3.10+.
16
+
17
+ ## Quickstart
18
+
19
+ ```bash
20
+ # 1. One-time setup — anonymous signup, writes ~/.cortexdb/state.json
21
+ cortexdb init
22
+
23
+ # 2. Store a memory
24
+ cortexdb remember "Q3 revenue: $2.4M. We're on track for $10M ARR by year-end."
25
+
26
+ # 3. Recall it
27
+ cortexdb recall "Q3 revenue?"
28
+
29
+ # 4. Or just type — drops into a REPL
30
+ cortexdb
31
+ ```
32
+
33
+ The signup token has a 7-day TTL on the free tier.
34
+
35
+ **Re-running `cortexdb init` does not refresh it.** `init` reuses an existing
36
+ identity and changes nothing; against an expired one it refuses and exits 3.
37
+ That is deliberate: a new identity is a new *brain*, and memories written
38
+ under the old actor stay where they are, unreachable from the new config.
39
+
40
+ When a token expires you have two safe options:
41
+
42
+ - `cortexdb init --api-key <token> --actor <actor>` — keep the same brain
43
+ with a token you already hold (e.g. from your IdP).
44
+ - `cortexdb init --force` — deliberately start a NEW brain. The previous
45
+ identity is backed up to `state.json.replaced` first.
46
+
47
+ For a second brain alongside the first, isolate instead of replacing:
48
+ `cortexdb --config-dir <dir> init`, or set `CORTEXDB_HOME`.
49
+
50
+ ## Commands
51
+
52
+ ```
53
+ cortexdb init anonymous signup; persist token + actor
54
+ cortexdb remember "..." store a memory (POST /v1/experience)
55
+ cortexdb recall "..." recall a stratified context pack
56
+ cortexdb search "..." granular event-level search
57
+ cortexdb forget --memory-id ... delete with audit
58
+ cortexdb episodes {list,get,delete} episode CRUD
59
+ cortexdb entities {list,get,link} entity rollups over the Facts layer
60
+ cortexdb admin {health,usage,layers} diagnostics
61
+ cortexdb import data.json bulk-load (JSON / JSONL / CSV / TXT)
62
+ cortexdb export -o backup.json export memories
63
+ cortexdb config {show,set} view / edit ~/.cortexdb/config.toml
64
+ cortexdb interactive REPL
65
+ ```
66
+
67
+ ## Configuration
68
+
69
+ Three layers, in precedence order:
70
+
71
+ 1. **CLI flags** — `--api-key`, `--endpoint`, `--actor`, `--scope`, `--profile`
72
+ 2. **Environment** — `CORTEXDB_API_KEY`, `CORTEXDB_URL`, `CORTEXDB_ACTOR`, `CORTEXDB_SCOPE`
73
+ 3. **Persisted state**:
74
+ - `~/.cortexdb/config.toml` — endpoint, profile names (human-editable)
75
+ - `~/.cortexdb/state.json` — token, actor, scope, expiry (managed by `init`, 0600 on POSIX)
76
+
77
+ The state.json format matches the cortexdb-mcp 0.3.0 server's, so you can
78
+ copy state across to use both tools from the same anonymous identity.
79
+
80
+ ## Pipe-friendly
81
+
82
+ Auto-detects when stdout isn't a TTY and emits JSON:
83
+
84
+ ```bash
85
+ cortexdb recall "Q3 revenue" | jq .context_block
86
+ cortexdb entities list | jq '.[].subject'
87
+ echo "remember this" | cortexdb remember -
88
+ ```
89
+
90
+ Force JSON mode any time with `--json`.
91
+
92
+ ## Auth notes
93
+
94
+ - The v1 API requires both `Authorization: Bearer <PASETO>` and `X-Cortex-Actor`.
95
+ The CLI stamps both on every request — you never need to pass them by hand.
96
+ - 401s show the specific error code (`TOKEN_EXPIRED`, `actor_mismatch`, …)
97
+ and the remediation. Note that `TOKEN_EXPIRED` is **not** fixed by re-running
98
+ `cortexdb init` — see the identity note above; that would mint a different
99
+ brain rather than restore access to yours.
100
+ - 403s cite the missing capability and the policy tier that denied. If a
101
+ recall returns `diagnostics.read denied`, pass `--diagnostics none` —
102
+ free-tier tokens don't carry the `diagnostics.read` capability.
103
+
104
+ ## License
105
+
106
+ Apache-2.0
@@ -1,3 +1,3 @@
1
1
  """CortexDB CLI - command-line interface for the v1 memory API."""
2
2
 
3
- __version__ = "0.5.1"
3
+ __version__ = "0.5.3"
@@ -0,0 +1,230 @@
1
+ """cortexdb init - anonymous signup + config persistence."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+ import httpx
7
+
8
+ from datetime import datetime, timezone
9
+
10
+ from cortexdb_cli.client import DEFAULT_ENDPOINT, signup
11
+ from cortexdb_cli.config import config_path, resolve_config_dir, save_config
12
+ from cortexdb_cli.output import console, err_console
13
+ from cortexdb_cli.state import load_state, save_state, state_path
14
+
15
+
16
+ def _inherit_group_flag(
17
+ ctx: click.Context,
18
+ name: str,
19
+ own_value: str | None,
20
+ ) -> str | None:
21
+ """Fall back to a flag given at the GROUP level (`cortexdb --endpoint ...
22
+ init`) when init's own flag wasn't supplied.
23
+
24
+ Users reasonably write global flags before the subcommand; Click parses
25
+ them into the group's params and, before 0.4.1, init silently ignored
26
+ them - `cortexdb --endpoint X --api-key Y init` performed an anonymous
27
+ signup against the DEFAULT endpoint. Only the parent's RAW params are
28
+ consulted (never merged config), so a stale config.toml can't leak into
29
+ a fresh init. If both levels are set and disagree, init's own flag wins
30
+ with a warning.
31
+ """
32
+ parent = ctx.parent
33
+ group_value = parent.params.get(name) if parent is not None else None
34
+ if own_value is not None and group_value is not None and own_value != group_value:
35
+ console.print(
36
+ f"[warning]--{name.replace('_', '-')} given both before and after "
37
+ f"`init` with different values; using {own_value!r}.[/warning]"
38
+ )
39
+ return own_value
40
+ return own_value if own_value is not None else group_value
41
+
42
+
43
+ def _existing_identity() -> tuple[dict, str] | None:
44
+ """Return `(state, status)` for a usable identity already on disk.
45
+
46
+ KAN-145/156: `init` used to mint and overwrite unconditionally. Running
47
+ it twice in one directory replaced a working identity with a brand-new
48
+ anonymous one - and because the actor and scope change, everything
49
+ written under the old identity becomes unreachable from this machine.
50
+ Nothing warned, and nothing had to fail for it to happen: the second
51
+ `init` looked exactly as successful as the first.
52
+
53
+ `status` is "valid" or "expired"; both are treated as "do not silently
54
+ replace this", because an expired token still names the brain the user's
55
+ data is in.
56
+ """
57
+ state = load_state()
58
+ if not state.get("token"):
59
+ return None
60
+ expires = state.get("expires_at")
61
+ status = "valid"
62
+ if expires:
63
+ try:
64
+ when = datetime.fromisoformat(str(expires).replace("Z", "+00:00"))
65
+ if when.tzinfo is None:
66
+ when = when.replace(tzinfo=timezone.utc)
67
+ if when <= datetime.now(timezone.utc):
68
+ status = "expired"
69
+ except (TypeError, ValueError):
70
+ # An unparseable expiry is not evidence of expiry. Prefer the
71
+ # conservative reading: something is here, do not clobber it.
72
+ status = "valid"
73
+ return state, status
74
+
75
+
76
+ def _report_existing(state: dict, status: str) -> None:
77
+ console.print(f" [success]Existing identity reused[/success] ({status})")
78
+ console.print(f" [success]Actor:[/success] {state.get('user_id', '?')}")
79
+ console.print(f" [success]Default scope:[/success] {state.get('scope', '?')}")
80
+ if state.get("expires_at"):
81
+ console.print(f" [dim]Token expires: {state['expires_at']}[/dim]")
82
+ console.print(f"")
83
+ console.print(f"[dim]State directory: {resolve_config_dir()}[/dim]")
84
+
85
+
86
+ @click.command("init")
87
+ @click.option(
88
+ "--endpoint",
89
+ default=None,
90
+ help=f"CortexDB v1 API base URL. [default: {DEFAULT_ENDPOINT}]",
91
+ )
92
+ @click.option(
93
+ "--api-key",
94
+ default=None,
95
+ help="Existing PASETO bearer token. If set, skips anonymous signup.",
96
+ )
97
+ @click.option(
98
+ "--actor",
99
+ default=None,
100
+ help="X-Cortex-Actor value. Required when --api-key is set.",
101
+ )
102
+ @click.option(
103
+ "--scope",
104
+ default=None,
105
+ help="Default scope path. Defaults to the actor.",
106
+ )
107
+ @click.option(
108
+ "--force",
109
+ is_flag=True,
110
+ help="Replace an existing identity. Backs the old one up first. "
111
+ "A new identity is a NEW brain: memories written under the old actor "
112
+ "stay where they are and are no longer reachable from this config.",
113
+ )
114
+ @click.pass_context
115
+ def init_cmd(
116
+ ctx: click.Context,
117
+ endpoint: str | None,
118
+ api_key: str | None,
119
+ actor: str | None,
120
+ scope: str | None,
121
+ force: bool,
122
+ ) -> None:
123
+ """Set up CortexDB CLI.
124
+
125
+ \b
126
+ Without flags: anonymous signup (free tier, 7-day token TTL).
127
+ With --api-key: register an existing token + actor (e.g. from your IdP).
128
+ """
129
+ # Global-flag inheritance: `cortexdb --endpoint X init` == `cortexdb init --endpoint X`.
130
+ endpoint = _inherit_group_flag(ctx, "endpoint", endpoint) or DEFAULT_ENDPOINT
131
+ api_key = _inherit_group_flag(ctx, "api_key", api_key)
132
+ actor = _inherit_group_flag(ctx, "actor", actor)
133
+ scope = _inherit_group_flag(ctx, "scope", scope)
134
+
135
+ console.print("\n[header]CortexDB CLI setup[/header]\n")
136
+
137
+ # Guard BEFORE any signup call or state write. `init` is read-only when a
138
+ # usable identity already exists: reuse and report it, never replace it
139
+ # silently. Reminting is a data-reachability event, not a retry.
140
+ existing = _existing_identity()
141
+ if existing and not force:
142
+ state, status = existing
143
+ if status == "valid":
144
+ _report_existing(state, status)
145
+ console.print(
146
+ "\n[dim]Nothing changed. Use --force to replace this identity, "
147
+ "or --config-dir / CORTEXDB_HOME for a separate one.[/dim]\n"
148
+ )
149
+ return
150
+ # Expired: still refuse. The token names the brain the user's data is
151
+ # in, so a silent remint strands it. Say so and make them choose.
152
+ err_console.print(
153
+ f"[error]The stored token for {state.get('user_id', '?')} has expired "
154
+ f"({state.get('expires_at')}).[/error]"
155
+ )
156
+ err_console.print(
157
+ "Refusing to mint a new identity automatically: a new identity is a "
158
+ "NEW brain, and memories written under the old actor would no longer "
159
+ "be reachable from this config."
160
+ )
161
+ err_console.print(
162
+ " To deliberately start a new brain: cortexdb init --force"
163
+ )
164
+ err_console.print(
165
+ " To use a token you already hold: "
166
+ "cortexdb init --api-key <token> --actor <actor>"
167
+ )
168
+ raise SystemExit(3)
169
+
170
+ if existing and force:
171
+ backup = state_path().with_suffix(".json.replaced")
172
+ try:
173
+ backup.write_text(
174
+ state_path().read_text(encoding="utf-8"), encoding="utf-8", newline="\n"
175
+ )
176
+ console.print(
177
+ f"[warning]Replacing the existing identity; "
178
+ f"previous state saved to {backup}[/warning]"
179
+ )
180
+ except OSError as exc:
181
+ console.print(
182
+ f"[warning]Could not back up existing state ({exc}); continuing[/warning]"
183
+ )
184
+
185
+ if api_key:
186
+ if not actor:
187
+ raise click.UsageError("--actor is required when --api-key is supplied.")
188
+ # Pure config write - no signup call.
189
+ save_config("default", {"endpoint": endpoint})
190
+ save_state(
191
+ {
192
+ "token": api_key,
193
+ "user_id": actor,
194
+ "scope": scope or actor,
195
+ "source": "explicit",
196
+ }
197
+ )
198
+ console.print(f"[success]Saved token + actor to {state_path()}[/success]")
199
+ console.print(f"[dim]Endpoint stored in {config_path()}[/dim]\n")
200
+ return
201
+
202
+ # Anonymous signup path.
203
+ console.print(f" Endpoint: [bold]{endpoint}[/bold]")
204
+ console.print(" No email, no card - free tier, 7-day token TTL.\n")
205
+
206
+ try:
207
+ resp = signup(endpoint)
208
+ except httpx.HTTPError as exc:
209
+ console.print(f"[error]Signup failed: {exc}[/error]")
210
+ raise SystemExit(2) from exc
211
+
212
+ state = {
213
+ "token": resp["token"],
214
+ "user_id": resp["user_id"],
215
+ "scope": resp.get("scope") or resp["user_id"],
216
+ "expires_at": resp.get("expires_at"),
217
+ "tier": resp.get("tier", "free"),
218
+ "source": "signup",
219
+ }
220
+ save_state(state)
221
+ save_config("default", {"endpoint": endpoint})
222
+
223
+ console.print(f" [success]Token minted[/success] (expires {resp.get('expires_at', '?')})")
224
+ console.print(f" [success]Actor:[/success] {resp['user_id']}")
225
+ console.print(f" [success]Default scope:[/success] {state['scope']}")
226
+ console.print(f"\n[dim]State written to {state_path()}[/dim]")
227
+ console.print(f"[dim]Endpoint stored in {config_path()}[/dim]\n")
228
+ console.print("Try it:\n")
229
+ console.print(' [bold]cortexdb remember "Q3 revenue: $2.4M"[/bold]')
230
+ console.print(' [bold]cortexdb recall "Q3 revenue?"[/bold]\n')
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "cortexdb-cli"
7
- version = "0.5.1"
7
+ version = "0.5.3"
8
8
  description = "Command-line interface for CortexDB — the long-term memory layer for AI."
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,129 +0,0 @@
1
- """cortexdb init - anonymous signup + config persistence."""
2
-
3
- from __future__ import annotations
4
-
5
- import click
6
- import httpx
7
-
8
- from cortexdb_cli.client import DEFAULT_ENDPOINT, signup
9
- from cortexdb_cli.config import config_path, save_config
10
- from cortexdb_cli.output import console
11
- from cortexdb_cli.state import save_state, state_path
12
-
13
-
14
- def _inherit_group_flag(
15
- ctx: click.Context,
16
- name: str,
17
- own_value: str | None,
18
- ) -> str | None:
19
- """Fall back to a flag given at the GROUP level (`cortexdb --endpoint ...
20
- init`) when init's own flag wasn't supplied.
21
-
22
- Users reasonably write global flags before the subcommand; Click parses
23
- them into the group's params and, before 0.4.1, init silently ignored
24
- them - `cortexdb --endpoint X --api-key Y init` performed an anonymous
25
- signup against the DEFAULT endpoint. Only the parent's RAW params are
26
- consulted (never merged config), so a stale config.toml can't leak into
27
- a fresh init. If both levels are set and disagree, init's own flag wins
28
- with a warning.
29
- """
30
- parent = ctx.parent
31
- group_value = parent.params.get(name) if parent is not None else None
32
- if own_value is not None and group_value is not None and own_value != group_value:
33
- console.print(
34
- f"[warning]--{name.replace('_', '-')} given both before and after "
35
- f"`init` with different values; using {own_value!r}.[/warning]"
36
- )
37
- return own_value
38
- return own_value if own_value is not None else group_value
39
-
40
-
41
- @click.command("init")
42
- @click.option(
43
- "--endpoint",
44
- default=None,
45
- help=f"CortexDB v1 API base URL. [default: {DEFAULT_ENDPOINT}]",
46
- )
47
- @click.option(
48
- "--api-key",
49
- default=None,
50
- help="Existing PASETO bearer token. If set, skips anonymous signup.",
51
- )
52
- @click.option(
53
- "--actor",
54
- default=None,
55
- help="X-Cortex-Actor value. Required when --api-key is set.",
56
- )
57
- @click.option(
58
- "--scope",
59
- default=None,
60
- help="Default scope path. Defaults to the actor.",
61
- )
62
- @click.pass_context
63
- def init_cmd(
64
- ctx: click.Context,
65
- endpoint: str | None,
66
- api_key: str | None,
67
- actor: str | None,
68
- scope: str | None,
69
- ) -> None:
70
- """Set up CortexDB CLI.
71
-
72
- \b
73
- Without flags: anonymous signup (free tier, 7-day token TTL).
74
- With --api-key: register an existing token + actor (e.g. from your IdP).
75
- """
76
- # Global-flag inheritance: `cortexdb --endpoint X init` == `cortexdb init --endpoint X`.
77
- endpoint = _inherit_group_flag(ctx, "endpoint", endpoint) or DEFAULT_ENDPOINT
78
- api_key = _inherit_group_flag(ctx, "api_key", api_key)
79
- actor = _inherit_group_flag(ctx, "actor", actor)
80
- scope = _inherit_group_flag(ctx, "scope", scope)
81
-
82
- console.print("\n[header]CortexDB CLI setup[/header]\n")
83
-
84
- if api_key:
85
- if not actor:
86
- raise click.UsageError("--actor is required when --api-key is supplied.")
87
- # Pure config write - no signup call.
88
- save_config("default", {"endpoint": endpoint})
89
- save_state(
90
- {
91
- "token": api_key,
92
- "user_id": actor,
93
- "scope": scope or actor,
94
- "source": "explicit",
95
- }
96
- )
97
- console.print(f"[success]Saved token + actor to {state_path()}[/success]")
98
- console.print(f"[dim]Endpoint stored in {config_path()}[/dim]\n")
99
- return
100
-
101
- # Anonymous signup path.
102
- console.print(f" Endpoint: [bold]{endpoint}[/bold]")
103
- console.print(" No email, no card - free tier, 7-day token TTL.\n")
104
-
105
- try:
106
- resp = signup(endpoint)
107
- except httpx.HTTPError as exc:
108
- console.print(f"[error]Signup failed: {exc}[/error]")
109
- raise SystemExit(2) from exc
110
-
111
- state = {
112
- "token": resp["token"],
113
- "user_id": resp["user_id"],
114
- "scope": resp.get("scope") or resp["user_id"],
115
- "expires_at": resp.get("expires_at"),
116
- "tier": resp.get("tier", "free"),
117
- "source": "signup",
118
- }
119
- save_state(state)
120
- save_config("default", {"endpoint": endpoint})
121
-
122
- console.print(f" [success]Token minted[/success] (expires {resp.get('expires_at', '?')})")
123
- console.print(f" [success]Actor:[/success] {resp['user_id']}")
124
- console.print(f" [success]Default scope:[/success] {state['scope']}")
125
- console.print(f"\n[dim]State written to {state_path()}[/dim]")
126
- console.print(f"[dim]Endpoint stored in {config_path()}[/dim]\n")
127
- console.print("Try it:\n")
128
- console.print(' [bold]cortexdb remember "Q3 revenue: $2.4M"[/bold]')
129
- console.print(' [bold]cortexdb recall "Q3 revenue?"[/bold]\n')
File without changes