memorysync-cli 1.1.3__tar.gz → 1.1.4__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.
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/PKG-INFO +1 -1
- memorysync_cli-1.1.4/src/memorysync_cli/_version.py +1 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/commands/admin.py +2 -2
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/commands/init.py +18 -1
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/commands/memory.py +6 -3
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/main.py +21 -18
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/output.py +27 -2
- memorysync_cli-1.1.3/src/memorysync_cli/_version.py +0 -1
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/.gitignore +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/LICENSE +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/README.md +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/pyproject.toml +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/__init__.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/__main__.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/args.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/commands/__init__.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/commands/source.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/commands/tooling.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/completions.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/config.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/credentials.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/errors.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/evaluation.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/http.py +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/registry.json +0 -0
- {memorysync_cli-1.1.3 → memorysync_cli-1.1.4}/src/memorysync_cli/registry.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.1.4"
|
|
@@ -24,7 +24,7 @@ from .. import credentials
|
|
|
24
24
|
from .._version import __version__
|
|
25
25
|
from ..errors import usage_error
|
|
26
26
|
from ..evaluation import is_evaluation_key
|
|
27
|
-
from ..output import render_table, style
|
|
27
|
+
from ..output import dumps, render_table, style
|
|
28
28
|
|
|
29
29
|
#: Keys ``config set`` / ``config unset`` accept. Identical to the Node CLI's
|
|
30
30
|
#: ``SETTABLE``; ``output`` was previously missing here, so `config set output table`
|
|
@@ -457,7 +457,7 @@ def config_command(ctx: dict) -> dict:
|
|
|
457
457
|
f"{style.dim('active profile ')} {data.get('current_profile')}",
|
|
458
458
|
f"{style.dim('key storage ')} {credentials.describe_storage()}",
|
|
459
459
|
"",
|
|
460
|
-
|
|
460
|
+
dumps(data.get("profiles", {})),
|
|
461
461
|
]
|
|
462
462
|
),
|
|
463
463
|
}
|
|
@@ -147,10 +147,27 @@ def _init_by_claiming(ctx: dict, profile_name: str) -> dict:
|
|
|
147
147
|
"Your existing key keeps working and no longer expires. "
|
|
148
148
|
"Everything stored during the evaluation is still there."
|
|
149
149
|
),
|
|
150
|
+
"",
|
|
151
|
+
# Owning the account and being able to open the dashboard are
|
|
152
|
+
# two different things, and this used to stop before saying
|
|
153
|
+
# so. Without ``--password`` there is no credential yet, and
|
|
154
|
+
# the only way in is a link labelled "Forgot password" —
|
|
155
|
+
# which nobody should have to guess at for a password they
|
|
156
|
+
# never had. The server says which case this is; print its
|
|
157
|
+
# sentence rather than deciding here, so the two CLIs and the
|
|
158
|
+
# API cannot drift on the instruction.
|
|
159
|
+
style.yellow(claimed["next_step"])
|
|
160
|
+
if claimed.get("next_step")
|
|
161
|
+
else style.dim(
|
|
162
|
+
"Sign in at https://app.memorysync.io/login with this address."
|
|
163
|
+
),
|
|
150
164
|
]
|
|
151
165
|
)
|
|
152
166
|
|
|
153
|
-
|
|
167
|
+
# The profile now records that the account is owned, so the reminder must
|
|
168
|
+
# be re-evaluated from disk rather than from the settings resolved before
|
|
169
|
+
# this ran.
|
|
170
|
+
return {"data": payload, "text": render_complete, "rescope": True}
|
|
154
171
|
|
|
155
172
|
# The value ``main`` already resolved from --api-key, the environment, then the
|
|
156
173
|
# stored profile. Resolving it again here would be a second precedence order
|
|
@@ -22,7 +22,7 @@ from typing import Any
|
|
|
22
22
|
|
|
23
23
|
from ..errors import quota_error, usage_error
|
|
24
24
|
from ..http import ApiClient
|
|
25
|
-
from ..output import render_table, style
|
|
25
|
+
from ..output import dumps, render_table, style
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
def _require_user(ctx: dict) -> str:
|
|
@@ -608,12 +608,15 @@ def export_memories(ctx: dict) -> dict:
|
|
|
608
608
|
|
|
609
609
|
|
|
610
610
|
def _serialize_export(memories: list[dict], fmt: str) -> str:
|
|
611
|
+
# Through the shared helper: an export is a file a customer keeps, and
|
|
612
|
+
# escaping every accent to ``\\uXXXX`` made this CLI's export a different
|
|
613
|
+
# document from the Node CLI's for the same memories.
|
|
611
614
|
if fmt == "json":
|
|
612
|
-
return
|
|
615
|
+
return dumps(memories) + "\n"
|
|
613
616
|
if fmt == "jsonl":
|
|
614
617
|
if not memories:
|
|
615
618
|
return ""
|
|
616
|
-
return "\n".join(
|
|
619
|
+
return "\n".join(dumps(item, indent=None) for item in memories) + "\n"
|
|
617
620
|
|
|
618
621
|
# CSV, quoted by hand rather than through the csv module so the output is
|
|
619
622
|
# identical to the Node CLI's, which also writes it directly.
|
|
@@ -24,6 +24,7 @@ from .errors import CliError, Exit, auth_error, usage_error
|
|
|
24
24
|
from .evaluation import claim_reminder
|
|
25
25
|
from .http import ApiClient
|
|
26
26
|
from .output import (
|
|
27
|
+
dumps,
|
|
27
28
|
emit,
|
|
28
29
|
error_envelope,
|
|
29
30
|
set_colour_enabled,
|
|
@@ -163,7 +164,7 @@ def _fail(*, command: str, error: CliError, agent_mode: bool, started: float, se
|
|
|
163
164
|
duration_ms = int((time.monotonic() - started) * 1000)
|
|
164
165
|
if agent_mode:
|
|
165
166
|
write(
|
|
166
|
-
|
|
167
|
+
dumps(
|
|
167
168
|
error_envelope(
|
|
168
169
|
command=command,
|
|
169
170
|
error=error,
|
|
@@ -277,37 +278,39 @@ def dispatch(argv: list[str]) -> int:
|
|
|
277
278
|
result = handler(ctx)
|
|
278
279
|
duration_ms = int((time.monotonic() - started) * 1000)
|
|
279
280
|
|
|
281
|
+
# A command that rewrote the profile has invalidated the settings resolved
|
|
282
|
+
# before it ran, so re-read them once and use them for everything below.
|
|
283
|
+
#
|
|
284
|
+
# Two things depend on this. `init --agent` stores a new default end-user
|
|
285
|
+
# id, and reporting the previous one in `scope` told an agent to keep
|
|
286
|
+
# writing under the identity it had just replaced. And
|
|
287
|
+
# `init --email ... --code` records that the account is now owned, without
|
|
288
|
+
# which the CLI printed "This is an unclaimed evaluation key and expires"
|
|
289
|
+
# immediately after a successful claim — nagging about the exact thing the
|
|
290
|
+
# user had just done.
|
|
291
|
+
effective = _resolve_settings(flags) if result.get("rescope") else settings
|
|
292
|
+
|
|
280
293
|
# `help` is a discovery contract, not a data result, so its JSON is the
|
|
281
294
|
# tree itself rather than an envelope around it. An agent bootstrapping
|
|
282
295
|
# from `help --json` should not have to reach into `.data[0]` for the
|
|
283
296
|
# command list.
|
|
284
297
|
if command_name == "help" and (agent_mode or fmt == "json"):
|
|
285
|
-
write(
|
|
286
|
-
_note_unclaimed_key(api_key,
|
|
298
|
+
write(dumps(result["data"]))
|
|
299
|
+
_note_unclaimed_key(api_key, effective)
|
|
287
300
|
return Exit.OK
|
|
288
301
|
|
|
289
302
|
if agent_mode:
|
|
290
303
|
write(
|
|
291
|
-
|
|
304
|
+
dumps(
|
|
292
305
|
success_envelope(
|
|
293
306
|
command=command_name,
|
|
294
307
|
data=result["data"],
|
|
295
308
|
duration_ms=duration_ms,
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
# case that matters: it mints an account and stores a new
|
|
299
|
-
# default end-user id, and reporting the previous one told
|
|
300
|
-
# an agent to keep writing under the identity it just
|
|
301
|
-
# replaced. Memories then split across two ids, which is the
|
|
302
|
-
# one failure this whole flow exists to avoid.
|
|
303
|
-
scope=_scope_of(
|
|
304
|
-
_resolve_settings(flags) if result.get("rescope") else settings
|
|
305
|
-
),
|
|
306
|
-
),
|
|
307
|
-
indent=2,
|
|
309
|
+
scope=_scope_of(effective),
|
|
310
|
+
)
|
|
308
311
|
)
|
|
309
312
|
)
|
|
310
|
-
_note_unclaimed_key(api_key,
|
|
313
|
+
_note_unclaimed_key(api_key, effective)
|
|
311
314
|
return result.get("exit_code", Exit.OK)
|
|
312
315
|
|
|
313
316
|
# `completion` and `mcp` emit text that must not be decorated or reshaped.
|
|
@@ -321,7 +324,7 @@ def dispatch(argv: list[str]) -> int:
|
|
|
321
324
|
return result.get("exit_code", Exit.OK)
|
|
322
325
|
|
|
323
326
|
emit(fmt=fmt, data=result["data"], text=result["text"])
|
|
324
|
-
_note_unclaimed_key(api_key,
|
|
327
|
+
_note_unclaimed_key(api_key, effective)
|
|
325
328
|
return result.get("exit_code", Exit.OK)
|
|
326
329
|
|
|
327
330
|
except CliError as error:
|
|
@@ -204,7 +204,9 @@ def _yaml_scalar(value: Any) -> str:
|
|
|
204
204
|
# Sexagesimal: `12:30` is a number to a 1.1 parser.
|
|
205
205
|
or _SEXAGESIMAL.match(text) is not None
|
|
206
206
|
)
|
|
207
|
-
|
|
207
|
+
# Quoted through the shared helper so a value needing quotes in YAML is not
|
|
208
|
+
# also ASCII-escaped, which the Node CLI does not do.
|
|
209
|
+
return dumps(text, indent=None) if ambiguous else text
|
|
208
210
|
|
|
209
211
|
|
|
210
212
|
def to_yaml(value: Any, indent: int = 0) -> str:
|
|
@@ -316,13 +318,36 @@ def write(text: str) -> None:
|
|
|
316
318
|
sys.stdout.write(f"{text}\n")
|
|
317
319
|
|
|
318
320
|
|
|
321
|
+
def dumps(data: Any, *, indent: int | None = 2) -> str:
|
|
322
|
+
"""JSON exactly as the Node CLI's ``JSON.stringify`` writes it.
|
|
323
|
+
|
|
324
|
+
``ensure_ascii=False`` is the whole point. Python escapes non-ASCII by
|
|
325
|
+
default, so an em-dash came out as ``\\u2014`` here and as the character
|
|
326
|
+
itself there — the same document in different bytes, from two CLIs published
|
|
327
|
+
as interchangeable. It showed up on a claim response whose sentence contains
|
|
328
|
+
one, and it would have applied to every ``--json`` payload carrying any
|
|
329
|
+
non-ASCII text, which for a memory store is most of them: any customer
|
|
330
|
+
memory containing an accent, a dash or an emoji.
|
|
331
|
+
|
|
332
|
+
Separators are pinned too, because Python's defaults leave a trailing space
|
|
333
|
+
after ``,`` when no indent is given and JavaScript does not.
|
|
334
|
+
|
|
335
|
+
Every user-facing JSON write goes through here so the two cannot drift again.
|
|
336
|
+
Request bodies deliberately do not: those are read by our own API, which
|
|
337
|
+
decodes both spellings to the same string, and no user ever sees them.
|
|
338
|
+
"""
|
|
339
|
+
if indent is None:
|
|
340
|
+
return json.dumps(data, ensure_ascii=False, separators=(",", ":"))
|
|
341
|
+
return json.dumps(data, indent=indent, ensure_ascii=False)
|
|
342
|
+
|
|
343
|
+
|
|
319
344
|
def emit(*, fmt: str, data: Any, text: Callable[[], str]) -> None:
|
|
320
345
|
"""Render one result.
|
|
321
346
|
|
|
322
347
|
``text`` stays lazy so a JSON caller never pays to build a table it discards.
|
|
323
348
|
"""
|
|
324
349
|
if fmt == "json":
|
|
325
|
-
write(
|
|
350
|
+
write(dumps(data))
|
|
326
351
|
return
|
|
327
352
|
if fmt == "yaml":
|
|
328
353
|
write(to_yaml(data))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.1.3"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|