cortexdb-cli 0.7.0__py3-none-any.whl → 0.8.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 +1 -1
- cortexdb_cli/commands/admin.py +1 -1
- cortexdb_cli/commands/admin_access.py +14 -1
- cortexdb_cli/commands/beliefs.py +65 -8
- cortexdb_cli/commands/changes.py +308 -45
- cortexdb_cli/commands/conflicts.py +6 -0
- cortexdb_cli/commands/corrections.py +514 -0
- cortexdb_cli/commands/derivation.py +1667 -0
- cortexdb_cli/commands/derivation_jobs.py +377 -0
- cortexdb_cli/commands/episodes.py +315 -11
- cortexdb_cli/commands/experience.py +96 -14
- cortexdb_cli/commands/facts.py +39 -7
- cortexdb_cli/commands/import_cmd.py +5 -1
- cortexdb_cli/commands/publish.py +1 -1
- cortexdb_cli/commands/review.py +238 -0
- cortexdb_cli/commands/rollups.py +1 -1
- cortexdb_cli/commands/tokens.py +4 -2
- cortexdb_cli/common.py +118 -3
- cortexdb_cli/errors.py +517 -7
- cortexdb_cli/harness_packs_data.py +6 -6
- cortexdb_cli/main.py +14 -1
- cortexdb_cli/output.py +107 -5
- {cortexdb_cli-0.7.0.dist-info → cortexdb_cli-0.8.0.dist-info}/METADATA +233 -9
- {cortexdb_cli-0.7.0.dist-info → cortexdb_cli-0.8.0.dist-info}/RECORD +26 -22
- {cortexdb_cli-0.7.0.dist-info → cortexdb_cli-0.8.0.dist-info}/WHEEL +0 -0
- {cortexdb_cli-0.7.0.dist-info → cortexdb_cli-0.8.0.dist-info}/entry_points.txt +0 -0
cortexdb_cli/__init__.py
CHANGED
cortexdb_cli/commands/admin.py
CHANGED
|
@@ -95,7 +95,7 @@ def admin_layers(ctx: click.Context) -> None:
|
|
|
95
95
|
|
|
96
96
|
# -- retention (unified WAL) -------------------------------------------------
|
|
97
97
|
#
|
|
98
|
-
# The operator surface over the
|
|
98
|
+
# The operator surface over the retention plane. Every write here is
|
|
99
99
|
# irreversible in effect - an applied sweep removes expired captured events,
|
|
100
100
|
# everything about them under an `erase` rule, their raw data under a
|
|
101
101
|
# `keep_learned` one - so reporting is the default on both mutating commands
|
|
@@ -151,7 +151,11 @@ def tenant_settings(ctx, root, mode, readiness_ticket, clear_readiness):
|
|
|
151
151
|
def tenant_binding(ctx, root, from_tenant, apply_):
|
|
152
152
|
"""Bind a root's records and tombstones to the tenant it names (POST /v1/admin/access/tenant-binding).
|
|
153
153
|
|
|
154
|
-
A DRY RUN unless --apply; running it again is safe.
|
|
154
|
+
A DRY RUN unless --apply; running it again is safe. The dry run says how
|
|
155
|
+
the operator plane decides the real run (operator_plane: whether it
|
|
156
|
+
registers the root, is in-band, the tenant's mode): when it says
|
|
157
|
+
break_glass_required, pass --break-glass REASON --break-glass-ticket
|
|
158
|
+
TICKET with --apply.
|
|
155
159
|
"""
|
|
156
160
|
cfg = ctx.obj
|
|
157
161
|
scope = root_or_die(cfg, root)
|
|
@@ -169,6 +173,15 @@ def tenant_binding(ctx, root, from_tenant, apply_):
|
|
|
169
173
|
+ (", root registered" if d.get("root_registered") else ""))
|
|
170
174
|
if d.get("consistency_token"):
|
|
171
175
|
console.print(f"[dim] consistency token: {escape(str(d['consistency_token']))}[/dim]")
|
|
176
|
+
plane = d.get("operator_plane")
|
|
177
|
+
if isinstance(plane, dict):
|
|
178
|
+
# 0.14.1 (p6a C): how the operator plane decides the run.
|
|
179
|
+
console.print(f" operator plane: mode {escape(str(plane.get('mode')))}"
|
|
180
|
+
+ (", the run registers the root" if plane.get("registers_root") else "")
|
|
181
|
+
+ (", an in-band path for the operator key" if plane.get("in_band") else ""))
|
|
182
|
+
if plane.get("break_glass_required"):
|
|
183
|
+
console.print("[warning] The real run needs break-glass: add --break-glass REASON "
|
|
184
|
+
"--break-glass-ticket TICKET to --apply (else 403 BREAK_GLASS_REQUIRED).[/warning]")
|
|
172
185
|
if d.get("dry_run"):
|
|
173
186
|
console.print("[dim]Nothing was changed. Run again with --apply.[/dim]")
|
|
174
187
|
|
cortexdb_cli/commands/beliefs.py
CHANGED
|
@@ -5,12 +5,20 @@ from __future__ import annotations
|
|
|
5
5
|
import click
|
|
6
6
|
|
|
7
7
|
from cortexdb_cli.client import get_client
|
|
8
|
-
from cortexdb_cli.common import
|
|
8
|
+
from cortexdb_cli.common import (
|
|
9
|
+
api_options,
|
|
10
|
+
at_least_option,
|
|
11
|
+
derived_by_option,
|
|
12
|
+
include_option,
|
|
13
|
+
include_param,
|
|
14
|
+
)
|
|
9
15
|
from cortexdb_cli.errors import handle_errors
|
|
10
16
|
from cortexdb_cli.output import (
|
|
11
17
|
console,
|
|
18
|
+
derivation_summary,
|
|
12
19
|
escape,
|
|
13
20
|
is_json_mode,
|
|
21
|
+
note_unreported_derivation,
|
|
14
22
|
print_json,
|
|
15
23
|
spinner,
|
|
16
24
|
)
|
|
@@ -40,15 +48,30 @@ def _scope_or_die(cfg: dict, scope: str | None) -> str:
|
|
|
40
48
|
@beliefs_group.command("list")
|
|
41
49
|
@click.option("--scope", "-S", help="Scope path (defaults to your default scope)")
|
|
42
50
|
@click.option("--limit", "-l", default=50, show_default=True)
|
|
51
|
+
@include_option
|
|
43
52
|
@at_least_option
|
|
44
53
|
@api_options
|
|
45
54
|
@click.pass_context
|
|
46
55
|
@handle_errors
|
|
47
|
-
def beliefs_list(
|
|
48
|
-
|
|
56
|
+
def beliefs_list(
|
|
57
|
+
ctx: click.Context,
|
|
58
|
+
scope: str | None,
|
|
59
|
+
limit: int,
|
|
60
|
+
include: tuple[str, ...],
|
|
61
|
+
at_least: str | None,
|
|
62
|
+
) -> None:
|
|
63
|
+
"""List active beliefs in a scope.
|
|
64
|
+
|
|
65
|
+
With --include derivation each belief also says how it was made,
|
|
66
|
+
computed from its supporting facts (the latest one's; unsynced when any
|
|
67
|
+
of them is). A belief with no fact support, a declared one, has no stamp.
|
|
68
|
+
"""
|
|
49
69
|
cfg = ctx.obj
|
|
50
70
|
s = _scope_or_die(cfg, scope)
|
|
51
71
|
params: dict[str, str | int] = {"scope": s, "limit": limit}
|
|
72
|
+
included = include_param(include)
|
|
73
|
+
if included:
|
|
74
|
+
params["include"] = included
|
|
52
75
|
if at_least:
|
|
53
76
|
params["at_least"] = at_least
|
|
54
77
|
with get_client(cfg) as client:
|
|
@@ -79,19 +102,26 @@ def beliefs_list(ctx: click.Context, scope: str | None, limit: int, at_least: st
|
|
|
79
102
|
table.add_column("Object", style="bold")
|
|
80
103
|
table.add_column("Conf", justify="right", width=6)
|
|
81
104
|
table.add_column("Support", justify="right", width=8)
|
|
105
|
+
if included:
|
|
106
|
+
table.add_column("Why", style="dim", max_width=48)
|
|
82
107
|
for b in items:
|
|
83
108
|
# v1 beliefs carry their triple under `claim` ({subject, predicate,
|
|
84
109
|
# object}, each term {type, value|id}); the columns used to read
|
|
85
110
|
# top-level `predicate`/`object`, which no belief has.
|
|
86
111
|
claim = b.get("claim") if isinstance(b.get("claim"), dict) else b
|
|
87
|
-
|
|
112
|
+
row = [
|
|
88
113
|
escape(str(b.get("belief_id") or b.get("id", ""))[:16]),
|
|
89
114
|
escape(_term(claim.get("predicate"))),
|
|
90
115
|
escape(_term(claim.get("object"))),
|
|
91
116
|
f"{(b.get('confidence') or 0):.0%}",
|
|
92
117
|
str(b.get("support_count") or len(b.get("supports") or [])),
|
|
93
|
-
|
|
118
|
+
]
|
|
119
|
+
if included:
|
|
120
|
+
row.append(escape(derivation_summary(b.get("derivation"))))
|
|
121
|
+
table.add_row(*row)
|
|
94
122
|
console.print(table)
|
|
123
|
+
if included:
|
|
124
|
+
note_unreported_derivation(items)
|
|
95
125
|
if isinstance(data, dict) and data.get("rollups"):
|
|
96
126
|
console.print(
|
|
97
127
|
f"[dim]{len(data['rollups'])} rollup(s) on this page; "
|
|
@@ -108,19 +138,30 @@ def beliefs_list(ctx: click.Context, scope: str | None, limit: int, at_least: st
|
|
|
108
138
|
help="Return the belief only if it lies at or under this scope (?scope=; else 404). It "
|
|
109
139
|
"routes the read on a cluster, and a break-glass read of a private region must name it.",
|
|
110
140
|
)
|
|
141
|
+
@include_option
|
|
111
142
|
@api_options
|
|
112
143
|
@click.pass_context
|
|
113
144
|
@handle_errors
|
|
114
|
-
def beliefs_why(
|
|
145
|
+
def beliefs_why(
|
|
146
|
+
ctx: click.Context, belief_id: str, scope: str | None, include: tuple[str, ...]
|
|
147
|
+
) -> None:
|
|
115
148
|
"""Show the facts + events that ground a belief.
|
|
116
149
|
|
|
150
|
+
\b
|
|
117
151
|
cortexdb beliefs why belief_01HX...
|
|
118
152
|
cortexdb beliefs why belief_01HX... --scope org:acme/team:eng
|
|
153
|
+
cortexdb beliefs why belief_01HX... --include derivation
|
|
154
|
+
|
|
155
|
+
With --include derivation the belief also carries its derivation, and
|
|
156
|
+
the terminal says it on one line under the answer.
|
|
119
157
|
"""
|
|
120
158
|
cfg = ctx.obj
|
|
121
159
|
params = {"belief_id": belief_id}
|
|
122
160
|
if scope:
|
|
123
161
|
params["scope"] = scope
|
|
162
|
+
included = include_param(include)
|
|
163
|
+
if included:
|
|
164
|
+
params["include"] = included
|
|
124
165
|
with get_client(cfg) as client:
|
|
125
166
|
if not is_json_mode(cfg):
|
|
126
167
|
with spinner("Tracing provenance..."):
|
|
@@ -128,7 +169,13 @@ def beliefs_why(ctx: click.Context, belief_id: str, scope: str | None) -> None:
|
|
|
128
169
|
else:
|
|
129
170
|
resp = client.get("/v1/beliefs/why", params=params)
|
|
130
171
|
resp.raise_for_status()
|
|
131
|
-
|
|
172
|
+
data = resp.json()
|
|
173
|
+
print_json(data)
|
|
174
|
+
if included and not is_json_mode(cfg):
|
|
175
|
+
belief = data.get("belief") if isinstance(data, dict) else None
|
|
176
|
+
if isinstance(belief, dict):
|
|
177
|
+
console.print(f"[dim]why: {escape(derivation_summary(belief.get('derivation')))}[/dim]")
|
|
178
|
+
note_unreported_derivation([belief])
|
|
132
179
|
|
|
133
180
|
|
|
134
181
|
@beliefs_group.command("create")
|
|
@@ -165,6 +212,10 @@ def beliefs_why(ctx: click.Context, belief_id: str, scope: str | None) -> None:
|
|
|
165
212
|
"key is 409 IDEMPOTENCY_CONFLICT).",
|
|
166
213
|
)
|
|
167
214
|
@click.option("--scope", "-S", help="Scope path (defaults to your default scope)")
|
|
215
|
+
@derived_by_option(
|
|
216
|
+
"A declared belief carries it in its record (below apply format 10 the server answers 503 "
|
|
217
|
+
"FORMAT_UPGRADE_PENDING), and include=derivation reads it back."
|
|
218
|
+
)
|
|
168
219
|
@api_options
|
|
169
220
|
@click.pass_context
|
|
170
221
|
@handle_errors
|
|
@@ -179,16 +230,20 @@ def beliefs_create(
|
|
|
179
230
|
provenance: tuple[str, ...],
|
|
180
231
|
idempotency_key: str | None,
|
|
181
232
|
scope: str | None,
|
|
233
|
+
derived_by: dict[str, str] | None,
|
|
182
234
|
) -> None:
|
|
183
235
|
"""Declare a belief directly (POST /v1/beliefs).
|
|
184
236
|
|
|
185
237
|
\b
|
|
186
238
|
cortexdb beliefs create "Acme is on the enterprise plan" \\
|
|
187
239
|
--confidence 0.9 --support evt_01HX... --subject Acme --predicate plan
|
|
240
|
+
cortexdb beliefs create "Bob reads at level B2" --confidence 0.8 \\
|
|
241
|
+
--subject Bob --predicate level --derived-by acme-level@1.1.0
|
|
188
242
|
|
|
189
243
|
Declaring a newer belief over the same (subject, predicate) closes the
|
|
190
244
|
previous one. Pass --idempotency-key to retry safely after an outcome
|
|
191
|
-
you did not learn (WAL_OUTCOME_UNKNOWN, a lost connection)
|
|
245
|
+
you did not learn (WAL_OUTCOME_UNKNOWN, a lost connection); the same key
|
|
246
|
+
with another --derived-by stamp, or none, is 409 IDEMPOTENCY_CONFLICT.
|
|
192
247
|
"""
|
|
193
248
|
cfg = ctx.obj
|
|
194
249
|
s = _scope_or_die(cfg, scope)
|
|
@@ -210,6 +265,8 @@ def beliefs_create(
|
|
|
210
265
|
if not idempotency_key.strip() or len(idempotency_key.encode("utf-8")) > 256:
|
|
211
266
|
raise click.UsageError("--idempotency-key is 1 to 256 bytes, not blank")
|
|
212
267
|
body["idempotency_key"] = idempotency_key
|
|
268
|
+
if derived_by:
|
|
269
|
+
body["derived_by"] = derived_by
|
|
213
270
|
with get_client(cfg) as client:
|
|
214
271
|
resp = client.post("/v1/beliefs", json=body)
|
|
215
272
|
resp.raise_for_status()
|