ai-memory-cli 0.1.6__tar.gz → 0.1.8__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.
- {ai_memory_cli-0.1.6/src/ai_memory_cli.egg-info → ai_memory_cli-0.1.8}/PKG-INFO +5 -1
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/README.md +4 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/pyproject.toml +1 -1
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/src/ai_memory_cli/__init__.py +1 -1
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/src/ai_memory_cli/cli.py +79 -21
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8/src/ai_memory_cli.egg-info}/PKG-INFO +5 -1
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/LICENSE +0 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/bin/watch.cmd +0 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/setup.cfg +0 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/src/ai_memory_cli/__main__.py +0 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/src/ai_memory_cli.egg-info/SOURCES.txt +0 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/src/ai_memory_cli.egg-info/dependency_links.txt +0 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/src/ai_memory_cli.egg-info/entry_points.txt +0 -0
- {ai_memory_cli-0.1.6 → ai_memory_cli-0.1.8}/src/ai_memory_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-memory-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Python CLI for AI Memory terminal capture and offline sync.
|
|
5
5
|
Author: AI Memory
|
|
6
6
|
License-Expression: MIT
|
|
@@ -57,6 +57,9 @@ The `auth` command verifies the website-issued token with FastAPI before it is s
|
|
|
57
57
|
the CLI stores a SHA-512 user hash for this computer, binds the token to that hash on the server, and starts the
|
|
58
58
|
background sync agent once on Windows.
|
|
59
59
|
|
|
60
|
+
If you run `watch` before auth, it will prompt for the website CLI token and FastAPI URL, then continue into
|
|
61
|
+
terminal capture after verification.
|
|
62
|
+
|
|
60
63
|
`watch` is a shortcut for `python -m ai_memory_cli watch`. If Windows Device Guard blocks the generated launcher, keep using `python -m ai_memory_cli watch`.
|
|
61
64
|
On Windows the shortcut is installed as `watch.cmd`; the Python Scripts folder must be on `PATH` for bare `watch` to resolve.
|
|
62
65
|
|
|
@@ -65,6 +68,7 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
|
|
|
65
68
|
On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids PATH issues and Device Guard policies that can block pip's generated `ai-memory.exe` launcher. Also avoid angle bracket placeholders in CMD because they are treated as file redirection.
|
|
66
69
|
|
|
67
70
|
Inside `watch`, type the real command you want to capture, for example `python --version`. Do not type `python -m ai_memory_cli run -- ...` inside `watch`, or you will capture the nested CLI command too.
|
|
71
|
+
Use `cls` on Windows or `clear` on Unix shells to clear the watch screen; those control commands are not stored or synced.
|
|
68
72
|
|
|
69
73
|
## Background agent
|
|
70
74
|
|
|
@@ -35,6 +35,9 @@ The `auth` command verifies the website-issued token with FastAPI before it is s
|
|
|
35
35
|
the CLI stores a SHA-512 user hash for this computer, binds the token to that hash on the server, and starts the
|
|
36
36
|
background sync agent once on Windows.
|
|
37
37
|
|
|
38
|
+
If you run `watch` before auth, it will prompt for the website CLI token and FastAPI URL, then continue into
|
|
39
|
+
terminal capture after verification.
|
|
40
|
+
|
|
38
41
|
`watch` is a shortcut for `python -m ai_memory_cli watch`. If Windows Device Guard blocks the generated launcher, keep using `python -m ai_memory_cli watch`.
|
|
39
42
|
On Windows the shortcut is installed as `watch.cmd`; the Python Scripts folder must be on `PATH` for bare `watch` to resolve.
|
|
40
43
|
|
|
@@ -43,6 +46,7 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
|
|
|
43
46
|
On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids PATH issues and Device Guard policies that can block pip's generated `ai-memory.exe` launcher. Also avoid angle bracket placeholders in CMD because they are treated as file redirection.
|
|
44
47
|
|
|
45
48
|
Inside `watch`, type the real command you want to capture, for example `python --version`. Do not type `python -m ai_memory_cli run -- ...` inside `watch`, or you will capture the nested CLI command too.
|
|
49
|
+
Use `cls` on Windows or `clear` on Unix shells to clear the watch screen; those control commands are not stored or synced.
|
|
46
50
|
|
|
47
51
|
## Background agent
|
|
48
52
|
|
|
@@ -42,6 +42,7 @@ SHELL_NOT_FOUND_PATTERNS = [
|
|
|
42
42
|
"no such file or directory",
|
|
43
43
|
"command not found",
|
|
44
44
|
]
|
|
45
|
+
CLEAR_COMMANDS = {"cls", "clear"}
|
|
45
46
|
|
|
46
47
|
|
|
47
48
|
def utc_now() -> str:
|
|
@@ -174,6 +175,10 @@ def client_identity(home: Path, config: dict[str, Any]) -> dict[str, Any]:
|
|
|
174
175
|
}
|
|
175
176
|
|
|
176
177
|
|
|
178
|
+
def has_verified_auth(config: dict[str, Any]) -> bool:
|
|
179
|
+
return bool(config.get("token") and config.get("auth_verified_at") and config.get("user_hash") and config.get("local_user_hash"))
|
|
180
|
+
|
|
181
|
+
|
|
177
182
|
def agent_state_path(home: Path) -> Path:
|
|
178
183
|
return home / "agent.json"
|
|
179
184
|
|
|
@@ -317,6 +322,18 @@ def clean_watch_command(command: str) -> str:
|
|
|
317
322
|
return cleaned
|
|
318
323
|
|
|
319
324
|
|
|
325
|
+
def is_clear_command(command: str) -> bool:
|
|
326
|
+
return normalize_command(command).lower() in CLEAR_COMMANDS
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def clear_console() -> None:
|
|
330
|
+
command = "cls" if os.name == "nt" else "clear"
|
|
331
|
+
try:
|
|
332
|
+
subprocess.call(command, shell=True)
|
|
333
|
+
except Exception:
|
|
334
|
+
print("\033[2J\033[H", end="")
|
|
335
|
+
|
|
336
|
+
|
|
320
337
|
def normalize_output(stdout: str, stderr: str) -> str:
|
|
321
338
|
combined = f"stdout:\n{stdout}\nstderr:\n{stderr}"
|
|
322
339
|
lines = [line.rstrip() for line in combined.replace("\r\n", "\n").replace("\r", "\n").split("\n")]
|
|
@@ -425,9 +442,56 @@ def verify_cli_auth(home: Path, config: dict[str, Any], token: str) -> dict[str,
|
|
|
425
442
|
return response
|
|
426
443
|
|
|
427
444
|
|
|
445
|
+
def finish_auth(home: Path, config: dict[str, Any], token: str, start_agent: bool = True) -> dict[str, Any]:
|
|
446
|
+
config["pending_token_hash"] = sha256_text(token)
|
|
447
|
+
response = verify_cli_auth(home, config, token)
|
|
448
|
+
config["authed_at"] = utc_now()
|
|
449
|
+
save_config(home, config)
|
|
450
|
+
|
|
451
|
+
print(f"Saved CLI auth in {config_path(home)}")
|
|
452
|
+
print(f"GitHub account: {response.get('github_user') or config.get('github_user') or '-'}")
|
|
453
|
+
print(f"Local user hash: {str(config.get('user_hash') or '')[:24]}...")
|
|
454
|
+
if response.get("account_storage_dir"):
|
|
455
|
+
print(f"Server account storage: {response['account_storage_dir']}")
|
|
456
|
+
|
|
457
|
+
if start_agent:
|
|
458
|
+
ensure_agent_started_once(home, config)
|
|
459
|
+
|
|
460
|
+
try:
|
|
461
|
+
synced = sync_events(home, config, quiet=True)
|
|
462
|
+
if synced:
|
|
463
|
+
print(f"Synced {synced} queued terminal event(s).")
|
|
464
|
+
except Exception as exc:
|
|
465
|
+
print(f"Auth saved. Sync will retry later: {exc}", file=sys.stderr)
|
|
466
|
+
|
|
467
|
+
return response
|
|
468
|
+
|
|
469
|
+
|
|
470
|
+
def prompt_for_auth(home: Path, config: dict[str, Any]) -> dict[str, Any]:
|
|
471
|
+
print("AI Memory needs website auth before watch can capture and sync.")
|
|
472
|
+
print("Generate a CLI token from the website Integrations page, then paste it here.")
|
|
473
|
+
token = getpass.getpass("Website CLI token: ").strip()
|
|
474
|
+
if not token:
|
|
475
|
+
raise SystemExit("No token entered. Generate a CLI token from the website and run watch again.")
|
|
476
|
+
|
|
477
|
+
current_api_url = api_url(config)
|
|
478
|
+
entered_api_url = input(f"FastAPI URL [{current_api_url}]: ").strip()
|
|
479
|
+
if entered_api_url:
|
|
480
|
+
config["api_url"] = entered_api_url.rstrip("/")
|
|
481
|
+
|
|
482
|
+
try:
|
|
483
|
+
return finish_auth(home, config, token, start_agent=True)
|
|
484
|
+
except urllib.error.HTTPError as exc:
|
|
485
|
+
save_config(home, config)
|
|
486
|
+
raise SystemExit(f"CLI auth failed: {describe_http_error(exc)}") from exc
|
|
487
|
+
except Exception as exc:
|
|
488
|
+
save_config(home, config)
|
|
489
|
+
raise SystemExit(f"CLI auth failed. Keep the local FastAPI server running and generate a fresh website token: {exc}") from exc
|
|
490
|
+
|
|
491
|
+
|
|
428
492
|
def require_verified_auth(home: Path, config: dict[str, Any]) -> str:
|
|
429
493
|
token = require_token(config)
|
|
430
|
-
if
|
|
494
|
+
if has_verified_auth(config):
|
|
431
495
|
return token
|
|
432
496
|
|
|
433
497
|
try:
|
|
@@ -545,7 +609,7 @@ def sync_events(home: Path, config: dict[str, Any], limit: int = 50, quiet: bool
|
|
|
545
609
|
if not quiet:
|
|
546
610
|
print("No CLI token saved. Events remain queued until python -m ai_memory_cli auth is configured.")
|
|
547
611
|
return 0
|
|
548
|
-
if not (config
|
|
612
|
+
if not has_verified_auth(config):
|
|
549
613
|
try:
|
|
550
614
|
verify_cli_auth(home, config, token)
|
|
551
615
|
save_config(home, config)
|
|
@@ -581,6 +645,10 @@ def sync_events(home: Path, config: dict[str, Any], limit: int = 50, quiet: bool
|
|
|
581
645
|
|
|
582
646
|
|
|
583
647
|
def capture_command(home: Path, config: dict[str, Any], command: str, include_excluded: bool, source: str) -> int:
|
|
648
|
+
if is_clear_command(command):
|
|
649
|
+
clear_console()
|
|
650
|
+
return 0
|
|
651
|
+
|
|
584
652
|
require_verified_auth(home, config)
|
|
585
653
|
workspace = Path(str(config.get("workspace_path") or ".")).expanduser()
|
|
586
654
|
cwd = workspace if workspace.exists() else Path.cwd()
|
|
@@ -661,31 +729,14 @@ def command_auth(args: argparse.Namespace) -> int:
|
|
|
661
729
|
config["api_url"] = args.api_url.rstrip("/")
|
|
662
730
|
|
|
663
731
|
token = args.token.strip()
|
|
664
|
-
config["pending_token_hash"] = sha256_text(token)
|
|
665
732
|
try:
|
|
666
|
-
|
|
733
|
+
finish_auth(home, config, token, start_agent=not args.no_agent)
|
|
667
734
|
except urllib.error.HTTPError as exc:
|
|
668
735
|
save_config(home, config)
|
|
669
736
|
raise SystemExit(f"CLI auth failed: {describe_http_error(exc)}") from exc
|
|
670
737
|
except Exception as exc:
|
|
671
738
|
save_config(home, config)
|
|
672
739
|
raise SystemExit(f"CLI auth failed. Keep the local FastAPI server running and generate a fresh website token: {exc}") from exc
|
|
673
|
-
|
|
674
|
-
config["authed_at"] = utc_now()
|
|
675
|
-
save_config(home, config)
|
|
676
|
-
print(f"Saved CLI auth in {config_path(home)}")
|
|
677
|
-
print(f"GitHub account: {response.get('github_user') or config.get('github_user') or '-'}")
|
|
678
|
-
print(f"Local user hash: {str(config.get('user_hash') or '')[:24]}...")
|
|
679
|
-
if response.get("account_storage_dir"):
|
|
680
|
-
print(f"Server account storage: {response['account_storage_dir']}")
|
|
681
|
-
|
|
682
|
-
if not args.no_agent:
|
|
683
|
-
ensure_agent_started_once(home, config)
|
|
684
|
-
|
|
685
|
-
try:
|
|
686
|
-
sync_events(home, config, quiet=True)
|
|
687
|
-
except Exception as exc:
|
|
688
|
-
print(f"Auth saved. Sync will retry later: {exc}", file=sys.stderr)
|
|
689
740
|
return 0
|
|
690
741
|
|
|
691
742
|
|
|
@@ -812,6 +863,10 @@ def command_run(args: argparse.Namespace) -> int:
|
|
|
812
863
|
def command_watch(args: argparse.Namespace) -> int:
|
|
813
864
|
home = cli_home()
|
|
814
865
|
config = load_config(home)
|
|
866
|
+
if not has_verified_auth(config):
|
|
867
|
+
prompt_for_auth(home, config)
|
|
868
|
+
config = load_config(home)
|
|
869
|
+
|
|
815
870
|
print("AI Memory watch mode. Type commands to run and capture. Type exit to stop.")
|
|
816
871
|
while True:
|
|
817
872
|
try:
|
|
@@ -830,6 +885,9 @@ def command_watch(args: argparse.Namespace) -> int:
|
|
|
830
885
|
command = cleaned_command
|
|
831
886
|
if command.lower() in {"exit", "quit"}:
|
|
832
887
|
break
|
|
888
|
+
if is_clear_command(command):
|
|
889
|
+
clear_console()
|
|
890
|
+
continue
|
|
833
891
|
capture_command(home, config, command, args.include_excluded, "watch")
|
|
834
892
|
return 0
|
|
835
893
|
|
|
@@ -1120,7 +1178,7 @@ def command_status(_: argparse.Namespace) -> int:
|
|
|
1120
1178
|
print(f"Project: {config.get('project') or '-'}")
|
|
1121
1179
|
print(f"Repository: {config.get('repository') or '-'}")
|
|
1122
1180
|
print(f"Workspace: {config.get('workspace_path') or '.'}")
|
|
1123
|
-
print(f"Token: {'verified' if
|
|
1181
|
+
print(f"Token: {'verified' if has_verified_auth(config) else 'saved' if config.get('token') else 'missing'}")
|
|
1124
1182
|
print(f"GitHub account: {config.get('github_user') or '-'}")
|
|
1125
1183
|
print(f"User hash: {str(config.get('user_hash') or '-')[:24]}{'...' if config.get('user_hash') else ''}")
|
|
1126
1184
|
print(f"Events: {event_count} total, {outbox_count} queued, {sent_count} synced receipts")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-memory-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
4
4
|
Summary: Python CLI for AI Memory terminal capture and offline sync.
|
|
5
5
|
Author: AI Memory
|
|
6
6
|
License-Expression: MIT
|
|
@@ -57,6 +57,9 @@ The `auth` command verifies the website-issued token with FastAPI before it is s
|
|
|
57
57
|
the CLI stores a SHA-512 user hash for this computer, binds the token to that hash on the server, and starts the
|
|
58
58
|
background sync agent once on Windows.
|
|
59
59
|
|
|
60
|
+
If you run `watch` before auth, it will prompt for the website CLI token and FastAPI URL, then continue into
|
|
61
|
+
terminal capture after verification.
|
|
62
|
+
|
|
60
63
|
`watch` is a shortcut for `python -m ai_memory_cli watch`. If Windows Device Guard blocks the generated launcher, keep using `python -m ai_memory_cli watch`.
|
|
61
64
|
On Windows the shortcut is installed as `watch.cmd`; the Python Scripts folder must be on `PATH` for bare `watch` to resolve.
|
|
62
65
|
|
|
@@ -65,6 +68,7 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
|
|
|
65
68
|
On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids PATH issues and Device Guard policies that can block pip's generated `ai-memory.exe` launcher. Also avoid angle bracket placeholders in CMD because they are treated as file redirection.
|
|
66
69
|
|
|
67
70
|
Inside `watch`, type the real command you want to capture, for example `python --version`. Do not type `python -m ai_memory_cli run -- ...` inside `watch`, or you will capture the nested CLI command too.
|
|
71
|
+
Use `cls` on Windows or `clear` on Unix shells to clear the watch screen; those control commands are not stored or synced.
|
|
68
72
|
|
|
69
73
|
## Background agent
|
|
70
74
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|