cortexdb-cli 0.5.1__py3-none-any.whl → 0.5.2__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 +1 -1
- cortexdb_cli/commands/init.py +104 -3
- {cortexdb_cli-0.5.1.dist-info → cortexdb_cli-0.5.2.dist-info}/METADATA +1 -1
- {cortexdb_cli-0.5.1.dist-info → cortexdb_cli-0.5.2.dist-info}/RECORD +6 -6
- {cortexdb_cli-0.5.1.dist-info → cortexdb_cli-0.5.2.dist-info}/WHEEL +0 -0
- {cortexdb_cli-0.5.1.dist-info → cortexdb_cli-0.5.2.dist-info}/entry_points.txt +0 -0
cortexdb_cli/__init__.py
CHANGED
cortexdb_cli/commands/init.py
CHANGED
|
@@ -5,10 +5,12 @@ from __future__ import annotations
|
|
|
5
5
|
import click
|
|
6
6
|
import httpx
|
|
7
7
|
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
|
|
8
10
|
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
|
|
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
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
def _inherit_group_flag(
|
|
@@ -38,6 +40,49 @@ def _inherit_group_flag(
|
|
|
38
40
|
return own_value if own_value is not None else group_value
|
|
39
41
|
|
|
40
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
|
+
|
|
41
86
|
@click.command("init")
|
|
42
87
|
@click.option(
|
|
43
88
|
"--endpoint",
|
|
@@ -59,6 +104,13 @@ def _inherit_group_flag(
|
|
|
59
104
|
default=None,
|
|
60
105
|
help="Default scope path. Defaults to the actor.",
|
|
61
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
|
+
)
|
|
62
114
|
@click.pass_context
|
|
63
115
|
def init_cmd(
|
|
64
116
|
ctx: click.Context,
|
|
@@ -66,6 +118,7 @@ def init_cmd(
|
|
|
66
118
|
api_key: str | None,
|
|
67
119
|
actor: str | None,
|
|
68
120
|
scope: str | None,
|
|
121
|
+
force: bool,
|
|
69
122
|
) -> None:
|
|
70
123
|
"""Set up CortexDB CLI.
|
|
71
124
|
|
|
@@ -81,6 +134,54 @@ def init_cmd(
|
|
|
81
134
|
|
|
82
135
|
console.print("\n[header]CortexDB CLI setup[/header]\n")
|
|
83
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
|
+
|
|
84
185
|
if api_key:
|
|
85
186
|
if not actor:
|
|
86
187
|
raise click.UsageError("--actor is required when --api-key is supplied.")
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
cortexdb_cli/__init__.py,sha256=
|
|
1
|
+
cortexdb_cli/__init__.py,sha256=E9HyqRe9juxBYu8xwyA9tk6TUvi3rTeVOfrdIvcJFPA,90
|
|
2
2
|
cortexdb_cli/client.py,sha256=CQwGo4Gn7cHDnfLUlJBZMsZ9-AyGVz8W6bJKxNMwlRA,2862
|
|
3
3
|
cortexdb_cli/config.py,sha256=ee1qmOfqMwilL81ujaqjf6TjjlrFG7WZAHRIgeyif9U,5690
|
|
4
4
|
cortexdb_cli/errors.py,sha256=xwxAWYY7KtNa-e7dyLQECnsKcciVcKC41CXFFF_-0VU,3924
|
|
@@ -25,7 +25,7 @@ cortexdb_cli/commands/export_cmd.py,sha256=IFkALD-zu2fVrpt1R_L4yvOgCEzZ9gPwcsQdF
|
|
|
25
25
|
cortexdb_cli/commands/facts.py,sha256=R21T2x6fyKn8Mvg-tskoURBBiXtsIoiooT9WXRSrGzc,5401
|
|
26
26
|
cortexdb_cli/commands/forget.py,sha256=fPGXqw-KetuuemirHfV7FUmKPihSAX8VEVjrVs3CgP8,5060
|
|
27
27
|
cortexdb_cli/commands/import_cmd.py,sha256=wnV1bsf54z8W8alQOBj_ExfdV1nUYzl_Ghxv-atBRHA,8386
|
|
28
|
-
cortexdb_cli/commands/init.py,sha256=
|
|
28
|
+
cortexdb_cli/commands/init.py,sha256=4hIaqqzMzddutBiOo8zJB8bhd00bsfGmgN8fszn-Sw0,8905
|
|
29
29
|
cortexdb_cli/commands/interactive.py,sha256=_xDPMKQ_VnpLUMJn6pZL4aJ3VpY7ti5XDqKGC657zSI,9514
|
|
30
30
|
cortexdb_cli/commands/onboard.py,sha256=Mz_pQUH0WC8IKcVm6B3CVQNuez0KqZlPvezOE4uOvIU,27414
|
|
31
31
|
cortexdb_cli/commands/policy.py,sha256=FFBiVKNZJcUjTRL07yYQ8zsIWasGeF_jhM124hIKWeI,3354
|
|
@@ -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.
|
|
38
|
-
cortexdb_cli-0.5.
|
|
39
|
-
cortexdb_cli-0.5.
|
|
40
|
-
cortexdb_cli-0.5.
|
|
37
|
+
cortexdb_cli-0.5.2.dist-info/METADATA,sha256=CIgu0dksEWHPqVq3WHPmOq2kQRxkMf3U00ZlltMO620,4429
|
|
38
|
+
cortexdb_cli-0.5.2.dist-info/WHEEL,sha256=zOwg4jB6zX2kU910N-cMawjivD6tO8NEWvE12je1bVk,87
|
|
39
|
+
cortexdb_cli-0.5.2.dist-info/entry_points.txt,sha256=h3sEbdqqvtv8LBbRLUgybYPo9N2lpU85fQUSh5ToEvs,51
|
|
40
|
+
cortexdb_cli-0.5.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|