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.
- basic_memory/__init__.py +1 -1
- basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +5 -9
- basic_memory/api/app.py +10 -4
- basic_memory/api/routers/knowledge_router.py +25 -8
- basic_memory/api/routers/project_router.py +99 -4
- basic_memory/cli/app.py +9 -28
- basic_memory/cli/auth.py +277 -0
- basic_memory/cli/commands/cloud/__init__.py +5 -0
- basic_memory/cli/commands/cloud/api_client.py +112 -0
- basic_memory/cli/commands/cloud/bisync_commands.py +818 -0
- basic_memory/cli/commands/cloud/core_commands.py +288 -0
- basic_memory/cli/commands/cloud/mount_commands.py +295 -0
- basic_memory/cli/commands/cloud/rclone_config.py +288 -0
- basic_memory/cli/commands/cloud/rclone_installer.py +198 -0
- basic_memory/cli/commands/command_utils.py +60 -0
- basic_memory/cli/commands/import_memory_json.py +0 -4
- basic_memory/cli/commands/mcp.py +16 -4
- basic_memory/cli/commands/project.py +139 -142
- basic_memory/cli/commands/status.py +34 -22
- basic_memory/cli/commands/sync.py +45 -228
- basic_memory/cli/commands/tool.py +87 -16
- basic_memory/cli/main.py +1 -0
- basic_memory/config.py +76 -12
- basic_memory/db.py +104 -3
- basic_memory/deps.py +20 -3
- basic_memory/file_utils.py +37 -13
- basic_memory/ignore_utils.py +295 -0
- basic_memory/markdown/plugins.py +9 -7
- basic_memory/mcp/async_client.py +22 -10
- basic_memory/mcp/project_context.py +141 -0
- basic_memory/mcp/prompts/ai_assistant_guide.py +49 -4
- basic_memory/mcp/prompts/continue_conversation.py +1 -1
- basic_memory/mcp/prompts/recent_activity.py +116 -32
- basic_memory/mcp/prompts/search.py +1 -1
- basic_memory/mcp/prompts/utils.py +11 -4
- basic_memory/mcp/resources/ai_assistant_guide.md +179 -41
- basic_memory/mcp/resources/project_info.py +20 -6
- basic_memory/mcp/server.py +0 -37
- basic_memory/mcp/tools/__init__.py +5 -6
- basic_memory/mcp/tools/build_context.py +29 -19
- basic_memory/mcp/tools/canvas.py +19 -8
- basic_memory/mcp/tools/chatgpt_tools.py +178 -0
- basic_memory/mcp/tools/delete_note.py +67 -34
- basic_memory/mcp/tools/edit_note.py +55 -39
- basic_memory/mcp/tools/headers.py +44 -0
- basic_memory/mcp/tools/list_directory.py +18 -8
- basic_memory/mcp/tools/move_note.py +119 -41
- basic_memory/mcp/tools/project_management.py +61 -228
- basic_memory/mcp/tools/read_content.py +28 -12
- basic_memory/mcp/tools/read_note.py +83 -46
- basic_memory/mcp/tools/recent_activity.py +441 -42
- basic_memory/mcp/tools/search.py +82 -70
- basic_memory/mcp/tools/sync_status.py +5 -4
- basic_memory/mcp/tools/utils.py +19 -0
- basic_memory/mcp/tools/view_note.py +31 -6
- basic_memory/mcp/tools/write_note.py +65 -14
- basic_memory/models/knowledge.py +12 -6
- basic_memory/models/project.py +6 -2
- basic_memory/repository/entity_repository.py +29 -82
- basic_memory/repository/relation_repository.py +13 -0
- basic_memory/repository/repository.py +2 -2
- basic_memory/repository/search_repository.py +4 -2
- basic_memory/schemas/__init__.py +6 -0
- basic_memory/schemas/base.py +39 -11
- basic_memory/schemas/cloud.py +46 -0
- basic_memory/schemas/memory.py +90 -21
- basic_memory/schemas/project_info.py +9 -10
- basic_memory/schemas/sync_report.py +48 -0
- basic_memory/services/context_service.py +25 -11
- basic_memory/services/entity_service.py +75 -45
- basic_memory/services/initialization.py +30 -11
- basic_memory/services/project_service.py +13 -23
- basic_memory/sync/sync_service.py +145 -21
- basic_memory/sync/watch_service.py +101 -40
- basic_memory/utils.py +14 -4
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/METADATA +7 -6
- basic_memory-0.15.0.dist-info/RECORD +147 -0
- basic_memory/mcp/project_session.py +0 -120
- basic_memory-0.14.4.dist-info/RECORD +0 -133
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/WHEEL +0 -0
- {basic_memory-0.14.4.dist-info → basic_memory-0.15.0.dist-info}/entry_points.txt +0 -0
- {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
|