cortexdb-cli 0.5.1__tar.gz → 0.5.2__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.2}/PKG-INFO +1 -1
  2. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/__init__.py +1 -1
  3. cortexdb_cli-0.5.2/cortexdb_cli/commands/init.py +230 -0
  4. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/pyproject.toml +1 -1
  5. cortexdb_cli-0.5.1/cortexdb_cli/commands/init.py +0 -129
  6. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/.gitignore +0 -0
  7. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/README.md +0 -0
  8. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/client.py +0 -0
  9. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/__init__.py +0 -0
  10. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/admin.py +0 -0
  11. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/answer.py +0 -0
  12. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/audit.py +0 -0
  13. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/auth.py +0 -0
  14. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/beliefs.py +0 -0
  15. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/claims.py +0 -0
  16. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/code.py +0 -0
  17. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/compose.py +0 -0
  18. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/config_cmd.py +0 -0
  19. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/conflicts.py +0 -0
  20. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/entities.py +0 -0
  21. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/episodes.py +0 -0
  22. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/experience.py +0 -0
  23. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/export_cmd.py +0 -0
  24. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/facts.py +0 -0
  25. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/forget.py +0 -0
  26. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/import_cmd.py +0 -0
  27. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/interactive.py +0 -0
  28. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/onboard.py +0 -0
  29. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/policy.py +0 -0
  30. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/recall.py +0 -0
  31. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/scopes.py +0 -0
  32. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/search.py +0 -0
  33. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/understanding.py +0 -0
  34. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/commands/wire.py +0 -0
  35. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/config.py +0 -0
  36. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/errors.py +0 -0
  37. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/harness_packs_data.py +0 -0
  38. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/main.py +0 -0
  39. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/onboarding.py +0 -0
  40. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/cortexdb_cli/output.py +0 -0
  41. {cortexdb_cli-0.5.1 → cortexdb_cli-0.5.2}/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.2
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,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.2"
@@ -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.2"
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
File without changes