basic-memory 0.14.4__py3-none-any.whl → 0.15.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.

Potentially problematic release.


This version of basic-memory might be problematic. Click here for more details.

Files changed (82) hide show
  1. basic_memory/__init__.py +1 -1
  2. basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +5 -9
  3. basic_memory/api/app.py +10 -4
  4. basic_memory/api/routers/knowledge_router.py +25 -8
  5. basic_memory/api/routers/project_router.py +99 -4
  6. basic_memory/cli/app.py +9 -28
  7. basic_memory/cli/auth.py +277 -0
  8. basic_memory/cli/commands/cloud/__init__.py +5 -0
  9. basic_memory/cli/commands/cloud/api_client.py +112 -0
  10. basic_memory/cli/commands/cloud/bisync_commands.py +818 -0
  11. basic_memory/cli/commands/cloud/core_commands.py +288 -0
  12. basic_memory/cli/commands/cloud/mount_commands.py +295 -0
  13. basic_memory/cli/commands/cloud/rclone_config.py +288 -0
  14. basic_memory/cli/commands/cloud/rclone_installer.py +198 -0
  15. basic_memory/cli/commands/command_utils.py +60 -0
  16. basic_memory/cli/commands/import_memory_json.py +0 -4
  17. basic_memory/cli/commands/mcp.py +16 -4
  18. basic_memory/cli/commands/project.py +139 -142
  19. basic_memory/cli/commands/status.py +34 -22
  20. basic_memory/cli/commands/sync.py +45 -228
  21. basic_memory/cli/commands/tool.py +87 -16
  22. basic_memory/cli/main.py +1 -0
  23. basic_memory/config.py +76 -12
  24. basic_memory/db.py +104 -3
  25. basic_memory/deps.py +20 -3
  26. basic_memory/file_utils.py +37 -13
  27. basic_memory/ignore_utils.py +295 -0
  28. basic_memory/markdown/plugins.py +9 -7
  29. basic_memory/mcp/async_client.py +22 -10
  30. basic_memory/mcp/project_context.py +141 -0
  31. basic_memory/mcp/prompts/ai_assistant_guide.py +49 -4
  32. basic_memory/mcp/prompts/continue_conversation.py +1 -1
  33. basic_memory/mcp/prompts/recent_activity.py +116 -32
  34. basic_memory/mcp/prompts/search.py +1 -1
  35. basic_memory/mcp/prompts/utils.py +11 -4
  36. basic_memory/mcp/resources/ai_assistant_guide.md +179 -41
  37. basic_memory/mcp/resources/project_info.py +20 -6
  38. basic_memory/mcp/server.py +0 -37
  39. basic_memory/mcp/tools/__init__.py +5 -6
  40. basic_memory/mcp/tools/build_context.py +29 -19
  41. basic_memory/mcp/tools/canvas.py +19 -8
  42. basic_memory/mcp/tools/chatgpt_tools.py +178 -0
  43. basic_memory/mcp/tools/delete_note.py +67 -34
  44. basic_memory/mcp/tools/edit_note.py +55 -39
  45. basic_memory/mcp/tools/headers.py +44 -0
  46. basic_memory/mcp/tools/list_directory.py +18 -8
  47. basic_memory/mcp/tools/move_note.py +119 -41
  48. basic_memory/mcp/tools/project_management.py +61 -228
  49. basic_memory/mcp/tools/read_content.py +28 -12
  50. basic_memory/mcp/tools/read_note.py +83 -46
  51. basic_memory/mcp/tools/recent_activity.py +441 -42
  52. basic_memory/mcp/tools/search.py +82 -70
  53. basic_memory/mcp/tools/sync_status.py +5 -4
  54. basic_memory/mcp/tools/utils.py +19 -0
  55. basic_memory/mcp/tools/view_note.py +31 -6
  56. basic_memory/mcp/tools/write_note.py +65 -14
  57. basic_memory/models/knowledge.py +12 -6
  58. basic_memory/models/project.py +6 -2
  59. basic_memory/repository/entity_repository.py +29 -82
  60. basic_memory/repository/relation_repository.py +13 -0
  61. basic_memory/repository/repository.py +2 -2
  62. basic_memory/repository/search_repository.py +4 -2
  63. basic_memory/schemas/__init__.py +6 -0
  64. basic_memory/schemas/base.py +39 -11
  65. basic_memory/schemas/cloud.py +46 -0
  66. basic_memory/schemas/memory.py +90 -21
  67. basic_memory/schemas/project_info.py +9 -10
  68. basic_memory/schemas/sync_report.py +48 -0
  69. basic_memory/services/context_service.py +25 -11
  70. basic_memory/services/entity_service.py +75 -45
  71. basic_memory/services/initialization.py +30 -11
  72. basic_memory/services/project_service.py +13 -23
  73. basic_memory/sync/sync_service.py +145 -21
  74. basic_memory/sync/watch_service.py +101 -40
  75. basic_memory/utils.py +14 -4
  76. {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/METADATA +7 -6
  77. basic_memory-0.15.0.dist-info/RECORD +147 -0
  78. basic_memory/mcp/project_session.py +0 -120
  79. basic_memory-0.14.4.dist-info/RECORD +0 -133
  80. {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/WHEEL +0 -0
  81. {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/entry_points.txt +0 -0
  82. {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,112 @@
1
+ """Cloud API client utilities."""
2
+
3
+ from typing import Optional
4
+
5
+ import httpx
6
+ import typer
7
+ from rich.console import Console
8
+
9
+ from basic_memory.cli.auth import CLIAuth
10
+ from basic_memory.config import ConfigManager
11
+
12
+ console = Console()
13
+
14
+
15
+ class CloudAPIError(Exception):
16
+ """Exception raised for cloud API errors."""
17
+
18
+ def __init__(
19
+ self, message: str, status_code: Optional[int] = None, detail: Optional[dict] = None
20
+ ):
21
+ super().__init__(message)
22
+ self.status_code = status_code
23
+ self.detail = detail or {}
24
+
25
+
26
+ class SubscriptionRequiredError(CloudAPIError):
27
+ """Exception raised when user needs an active subscription."""
28
+
29
+ def __init__(self, message: str, subscribe_url: str):
30
+ super().__init__(message, status_code=403, detail={"error": "subscription_required"})
31
+ self.subscribe_url = subscribe_url
32
+
33
+
34
+ def get_cloud_config() -> tuple[str, str, str]:
35
+ """Get cloud OAuth configuration from config."""
36
+ config_manager = ConfigManager()
37
+ config = config_manager.config
38
+ return config.cloud_client_id, config.cloud_domain, config.cloud_host
39
+
40
+
41
+ async def get_authenticated_headers() -> dict[str, str]:
42
+ """
43
+ Get authentication headers with JWT token.
44
+ handles jwt refresh if needed.
45
+ """
46
+ client_id, domain, _ = get_cloud_config()
47
+ auth = CLIAuth(client_id=client_id, authkit_domain=domain)
48
+ token = await auth.get_valid_token()
49
+ if not token:
50
+ console.print("[red]Not authenticated. Please run 'basic-memory cloud login' first.[/red]")
51
+ raise typer.Exit(1)
52
+
53
+ return {"Authorization": f"Bearer {token}"}
54
+
55
+
56
+ async def make_api_request(
57
+ method: str,
58
+ url: str,
59
+ headers: Optional[dict] = None,
60
+ json_data: Optional[dict] = None,
61
+ timeout: float = 30.0,
62
+ ) -> httpx.Response:
63
+ """Make an API request to the cloud service."""
64
+ headers = headers or {}
65
+ auth_headers = await get_authenticated_headers()
66
+ headers.update(auth_headers)
67
+ # Add debug headers to help with compression issues
68
+ headers.setdefault("Accept-Encoding", "identity") # Disable compression for debugging
69
+
70
+ async with httpx.AsyncClient(timeout=timeout) as client:
71
+ try:
72
+ response = await client.request(method=method, url=url, headers=headers, json=json_data)
73
+ response.raise_for_status()
74
+ return response
75
+ except httpx.HTTPError as e:
76
+ # Check if this is a response error with response details
77
+ if hasattr(e, "response") and e.response is not None: # pyright: ignore [reportAttributeAccessIssue]
78
+ response = e.response # type: ignore
79
+
80
+ # Try to parse error detail from response
81
+ error_detail = None
82
+ try:
83
+ error_detail = response.json()
84
+ except Exception:
85
+ # If JSON parsing fails, we'll handle it as a generic error
86
+ pass
87
+
88
+ # Check for subscription_required error (403)
89
+ if response.status_code == 403 and isinstance(error_detail, dict):
90
+ # Handle both FastAPI HTTPException format (nested under "detail")
91
+ # and direct format
92
+ detail_obj = error_detail.get("detail", error_detail)
93
+ if (
94
+ isinstance(detail_obj, dict)
95
+ and detail_obj.get("error") == "subscription_required"
96
+ ):
97
+ message = detail_obj.get("message", "Active subscription required")
98
+ subscribe_url = detail_obj.get(
99
+ "subscribe_url", "https://basicmemory.com/subscribe"
100
+ )
101
+ raise SubscriptionRequiredError(
102
+ message=message, subscribe_url=subscribe_url
103
+ ) from e
104
+
105
+ # Raise generic CloudAPIError with status code and detail
106
+ raise CloudAPIError(
107
+ f"API request failed: {e}",
108
+ status_code=response.status_code,
109
+ detail=error_detail if isinstance(error_detail, dict) else {},
110
+ ) from e
111
+
112
+ raise CloudAPIError(f"API request failed: {e}") from e