basic-memory 0.14.2__py3-none-any.whl → 0.14.3__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/env.py +3 -1
- basic_memory/api/app.py +4 -1
- basic_memory/api/routers/management_router.py +3 -1
- basic_memory/api/routers/project_router.py +21 -13
- basic_memory/cli/app.py +3 -3
- basic_memory/cli/commands/__init__.py +1 -2
- basic_memory/cli/commands/db.py +5 -5
- basic_memory/cli/commands/import_chatgpt.py +3 -2
- basic_memory/cli/commands/import_claude_conversations.py +3 -1
- basic_memory/cli/commands/import_claude_projects.py +3 -1
- basic_memory/cli/commands/import_memory_json.py +5 -2
- basic_memory/cli/commands/mcp.py +3 -15
- basic_memory/cli/commands/project.py +41 -0
- basic_memory/cli/commands/status.py +4 -1
- basic_memory/cli/commands/sync.py +10 -2
- basic_memory/cli/main.py +0 -1
- basic_memory/config.py +46 -31
- basic_memory/db.py +2 -6
- basic_memory/deps.py +3 -2
- basic_memory/importers/chatgpt_importer.py +19 -9
- basic_memory/importers/memory_json_importer.py +22 -7
- basic_memory/mcp/async_client.py +22 -2
- basic_memory/mcp/project_session.py +6 -4
- basic_memory/mcp/prompts/__init__.py +0 -2
- basic_memory/mcp/server.py +8 -71
- basic_memory/mcp/tools/move_note.py +24 -12
- basic_memory/mcp/tools/read_content.py +16 -0
- basic_memory/mcp/tools/read_note.py +12 -0
- basic_memory/mcp/tools/sync_status.py +3 -2
- basic_memory/mcp/tools/write_note.py +9 -1
- basic_memory/models/project.py +3 -3
- basic_memory/repository/project_repository.py +18 -0
- basic_memory/schemas/importer.py +1 -0
- basic_memory/services/entity_service.py +49 -3
- basic_memory/services/initialization.py +0 -75
- basic_memory/services/project_service.py +85 -28
- basic_memory/sync/background_sync.py +4 -3
- basic_memory/sync/sync_service.py +50 -1
- basic_memory/utils.py +105 -4
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/METADATA +2 -2
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/RECORD +45 -51
- basic_memory/cli/commands/auth.py +0 -136
- basic_memory/mcp/auth_provider.py +0 -270
- basic_memory/mcp/external_auth_provider.py +0 -321
- basic_memory/mcp/prompts/sync_status.py +0 -112
- basic_memory/mcp/supabase_auth_provider.py +0 -463
- basic_memory/services/migration_service.py +0 -168
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/WHEEL +0 -0
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/entry_points.txt +0 -0
- {basic_memory-0.14.2.dist-info → basic_memory-0.14.3.dist-info}/licenses/LICENSE +0 -0
basic_memory/utils.py
CHANGED
|
@@ -4,7 +4,6 @@ import os
|
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
6
|
import re
|
|
7
|
-
import sys
|
|
8
7
|
import unicodedata
|
|
9
8
|
from pathlib import Path
|
|
10
9
|
from typing import Optional, Protocol, Union, runtime_checkable, List, Any
|
|
@@ -144,7 +143,7 @@ def setup_logging(
|
|
|
144
143
|
console: Whether to log to the console
|
|
145
144
|
"""
|
|
146
145
|
# Remove default handler and any existing handlers
|
|
147
|
-
logger.remove()
|
|
146
|
+
# logger.remove()
|
|
148
147
|
|
|
149
148
|
# Add file handler if we are not running tests and a log file is specified
|
|
150
149
|
if log_file and env != "test":
|
|
@@ -162,8 +161,8 @@ def setup_logging(
|
|
|
162
161
|
)
|
|
163
162
|
|
|
164
163
|
# Add console logger if requested or in test mode
|
|
165
|
-
if env == "test" or console:
|
|
166
|
-
|
|
164
|
+
# if env == "test" or console:
|
|
165
|
+
# logger.add(sys.stderr, level=log_level, backtrace=True, diagnose=True, colorize=True)
|
|
167
166
|
|
|
168
167
|
logger.info(f"ENV: '{env}' Log level: '{log_level}' Logging to {log_file}")
|
|
169
168
|
|
|
@@ -182,6 +181,79 @@ def setup_logging(
|
|
|
182
181
|
logging.getLogger(logger_name).setLevel(level)
|
|
183
182
|
|
|
184
183
|
|
|
184
|
+
def normalize_file_path_for_comparison(file_path: str) -> str:
|
|
185
|
+
"""Normalize a file path for conflict detection.
|
|
186
|
+
|
|
187
|
+
This function normalizes file paths to help detect potential conflicts:
|
|
188
|
+
- Converts to lowercase for case-insensitive comparison
|
|
189
|
+
- Normalizes Unicode characters
|
|
190
|
+
- Handles path separators consistently
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
file_path: The file path to normalize
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Normalized file path for comparison purposes
|
|
197
|
+
"""
|
|
198
|
+
import unicodedata
|
|
199
|
+
|
|
200
|
+
# Convert to lowercase for case-insensitive comparison
|
|
201
|
+
normalized = file_path.lower()
|
|
202
|
+
|
|
203
|
+
# Normalize Unicode characters (NFD normalization)
|
|
204
|
+
normalized = unicodedata.normalize("NFD", normalized)
|
|
205
|
+
|
|
206
|
+
# Replace path separators with forward slashes
|
|
207
|
+
normalized = normalized.replace("\\", "/")
|
|
208
|
+
|
|
209
|
+
# Remove multiple slashes
|
|
210
|
+
normalized = re.sub(r"/+", "/", normalized)
|
|
211
|
+
|
|
212
|
+
return normalized
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def detect_potential_file_conflicts(file_path: str, existing_paths: List[str]) -> List[str]:
|
|
216
|
+
"""Detect potential conflicts between a file path and existing paths.
|
|
217
|
+
|
|
218
|
+
This function checks for various types of conflicts:
|
|
219
|
+
- Case sensitivity differences
|
|
220
|
+
- Unicode normalization differences
|
|
221
|
+
- Path separator differences
|
|
222
|
+
- Permalink generation conflicts
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
file_path: The file path to check
|
|
226
|
+
existing_paths: List of existing file paths to check against
|
|
227
|
+
|
|
228
|
+
Returns:
|
|
229
|
+
List of existing paths that might conflict with the given file path
|
|
230
|
+
"""
|
|
231
|
+
conflicts = []
|
|
232
|
+
|
|
233
|
+
# Normalize the input file path
|
|
234
|
+
normalized_input = normalize_file_path_for_comparison(file_path)
|
|
235
|
+
input_permalink = generate_permalink(file_path)
|
|
236
|
+
|
|
237
|
+
for existing_path in existing_paths:
|
|
238
|
+
# Skip identical paths
|
|
239
|
+
if existing_path == file_path:
|
|
240
|
+
continue
|
|
241
|
+
|
|
242
|
+
# Check for case-insensitive path conflicts
|
|
243
|
+
normalized_existing = normalize_file_path_for_comparison(existing_path)
|
|
244
|
+
if normalized_input == normalized_existing:
|
|
245
|
+
conflicts.append(existing_path)
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
# Check for permalink conflicts
|
|
249
|
+
existing_permalink = generate_permalink(existing_path)
|
|
250
|
+
if input_permalink == existing_permalink:
|
|
251
|
+
conflicts.append(existing_path)
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
return conflicts
|
|
255
|
+
|
|
256
|
+
|
|
185
257
|
def parse_tags(tags: Union[List[str], str, None]) -> List[str]:
|
|
186
258
|
"""Parse tags from various input formats into a consistent list.
|
|
187
259
|
|
|
@@ -214,3 +286,32 @@ def parse_tags(tags: Union[List[str], str, None]) -> List[str]:
|
|
|
214
286
|
except (ValueError, TypeError): # pragma: no cover
|
|
215
287
|
logger.warning(f"Couldn't parse tags from input of type {type(tags)}: {tags}")
|
|
216
288
|
return []
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def validate_project_path(path: str, project_path: Path) -> bool:
|
|
292
|
+
"""Ensure path stays within project boundaries."""
|
|
293
|
+
# Allow empty strings as they resolve to the project root
|
|
294
|
+
if not path:
|
|
295
|
+
return True
|
|
296
|
+
|
|
297
|
+
# Check for obvious path traversal patterns first
|
|
298
|
+
if ".." in path or "~" in path:
|
|
299
|
+
return False
|
|
300
|
+
|
|
301
|
+
# Check for Windows-style path traversal (even on Unix systems)
|
|
302
|
+
if "\\.." in path or path.startswith("\\"):
|
|
303
|
+
return False
|
|
304
|
+
|
|
305
|
+
# Block absolute paths (Unix-style starting with / or Windows-style with drive letters)
|
|
306
|
+
if path.startswith("/") or (len(path) >= 2 and path[1] == ":"):
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
# Block paths with control characters (but allow whitespace that will be stripped)
|
|
310
|
+
if path.strip() and any(ord(c) < 32 and c not in [" ", "\t"] for c in path):
|
|
311
|
+
return False
|
|
312
|
+
|
|
313
|
+
try:
|
|
314
|
+
resolved = (project_path / path).resolve()
|
|
315
|
+
return resolved.is_relative_to(project_path.resolve())
|
|
316
|
+
except (ValueError, OSError):
|
|
317
|
+
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: basic-memory
|
|
3
|
-
Version: 0.14.
|
|
3
|
+
Version: 0.14.3
|
|
4
4
|
Summary: Local-first knowledge management combining Zettelkasten with knowledge graphs
|
|
5
5
|
Project-URL: Homepage, https://github.com/basicmachines-co/basic-memory
|
|
6
6
|
Project-URL: Repository, https://github.com/basicmachines-co/basic-memory
|
|
@@ -13,7 +13,7 @@ Requires-Dist: aiosqlite>=0.20.0
|
|
|
13
13
|
Requires-Dist: alembic>=1.14.1
|
|
14
14
|
Requires-Dist: dateparser>=1.2.0
|
|
15
15
|
Requires-Dist: fastapi[standard]>=0.115.8
|
|
16
|
-
Requires-Dist: fastmcp
|
|
16
|
+
Requires-Dist: fastmcp==2.10.2
|
|
17
17
|
Requires-Dist: greenlet>=3.1.1
|
|
18
18
|
Requires-Dist: icecream>=2.1.3
|
|
19
19
|
Requires-Dist: loguru>=0.7.3
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
basic_memory/__init__.py,sha256=
|
|
2
|
-
basic_memory/config.py,sha256=
|
|
3
|
-
basic_memory/db.py,sha256=
|
|
4
|
-
basic_memory/deps.py,sha256=
|
|
1
|
+
basic_memory/__init__.py,sha256=8waxNnFQNDHI9IsCihBom8GtgzjgHwDI4NDWWe_3Jxo,256
|
|
2
|
+
basic_memory/config.py,sha256=fGhHf0Eouc_97vOZ10E4poVeD2xWwoRGbZUP_7RWVPI,12480
|
|
3
|
+
basic_memory/db.py,sha256=JeBTsLP56PqCOwX8CZAB7-ijpPbg-Aco5QebpFqkBtg,7428
|
|
4
|
+
basic_memory/deps.py,sha256=vOKM2cpUm4r84LxonMH7LcgM1bkp-FEUqmgruK6CQR0,12144
|
|
5
5
|
basic_memory/file_utils.py,sha256=eaxTKLLEbTIy_Mb_Iv_Dmt4IXAJSrZGVi-Knrpyci3E,6700
|
|
6
|
-
basic_memory/utils.py,sha256=
|
|
6
|
+
basic_memory/utils.py,sha256=PZvF_bV-Te6-gvRHk2ZDi3XTaMh6rwO8NNDYWfDNfqg,10946
|
|
7
7
|
basic_memory/alembic/alembic.ini,sha256=IEZsnF8CbbZnkwBr67LzKKNobHuzTaQNUvM8Psop5xc,3733
|
|
8
|
-
basic_memory/alembic/env.py,sha256=
|
|
8
|
+
basic_memory/alembic/env.py,sha256=4kHZh-rfzVARy9ndvsuDPTBt6Hk3dZ2BwI030EppBrA,2838
|
|
9
9
|
basic_memory/alembic/migrations.py,sha256=lriHPXDdBLSNXEW3QTpU0SJKuVd1V-8NrVkpN3qfsUQ,718
|
|
10
10
|
basic_memory/alembic/script.py.mako,sha256=MEqL-2qATlST9TAOeYgscMn1uy6HUS9NFvDgl93dMj8,635
|
|
11
11
|
basic_memory/alembic/versions/3dae7c7b1564_initial_schema.py,sha256=lTbWlAnd1es7xU99DoJgfaRe1_Kte8TL98riqeKGV80,4363
|
|
@@ -15,40 +15,39 @@ basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py,sha256=YErF
|
|
|
15
15
|
basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py,sha256=RsGymQzfRXV1LSNKiyi0lMilTxW1NgwS9jR67ye2apI,1428
|
|
16
16
|
basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py,sha256=kDavR9Qqx9wSu5P3qd4SZq_waIsDG1UMTg2SmDoktMU,3679
|
|
17
17
|
basic_memory/api/__init__.py,sha256=wCpj-21j1D0KzKl9Ql6unLBVFY0K1uGp_FeSZRKtqpk,72
|
|
18
|
-
basic_memory/api/app.py,sha256=
|
|
18
|
+
basic_memory/api/app.py,sha256=rWc6wsCUEK9YMPav3_qiiwBASA2s1S_wF1C4ssBm6M8,2897
|
|
19
19
|
basic_memory/api/template_loader.py,sha256=exbTeXyJRgyLFmSjNeroxjT7X2DWFm2X5qUVa3drbYM,8607
|
|
20
20
|
basic_memory/api/routers/__init__.py,sha256=REO5CKQ96o5vtGWACcsIxIpWybIUSeKXc83RWbWc8BQ,398
|
|
21
21
|
basic_memory/api/routers/directory_router.py,sha256=rBQHvuwffUOk0iKvcEs2QlWclgvr8ur24f_pH3-sVRQ,2054
|
|
22
22
|
basic_memory/api/routers/importer_router.py,sha256=xFUCorkPWo8AF0ya0UrcLmXNf8CjPZdAqddQIH8PO-o,4513
|
|
23
23
|
basic_memory/api/routers/knowledge_router.py,sha256=4dD_tPpcJGWCuoRKEbQXCS3hoXNWe-VbcGC7xV-8VoE,9738
|
|
24
|
-
basic_memory/api/routers/management_router.py,sha256=
|
|
24
|
+
basic_memory/api/routers/management_router.py,sha256=zbzilNzsYUbFbE2uFXRM33cDn9IbI-73y8C1-b-19O4,2730
|
|
25
25
|
basic_memory/api/routers/memory_router.py,sha256=a9Cnx3XgwSkO-2ABFzI3wM3PoMGxuyfJFFp7NfFZapc,3003
|
|
26
|
-
basic_memory/api/routers/project_router.py,sha256=
|
|
26
|
+
basic_memory/api/routers/project_router.py,sha256=3qoto8Fm9pWqg6K0ukdd-VmggMENx7kK4JHvJrOhZ_I,8432
|
|
27
27
|
basic_memory/api/routers/prompt_router.py,sha256=4wxq6-NREgVJM8N9C0YsN1AAUDD8nkTCOzWyzSqTSFw,9948
|
|
28
28
|
basic_memory/api/routers/resource_router.py,sha256=WEJEqEaY_yTKj5-U-rW4kXQKUcJflykgwI6_g_R41ck,8058
|
|
29
29
|
basic_memory/api/routers/search_router.py,sha256=GD62jlCQTiL_VNsdibi-b1f6H40KCWo9SX2Cl7YH4QU,1226
|
|
30
30
|
basic_memory/api/routers/utils.py,sha256=nmD1faJOHcnWQjbCanojUwA9xhinf764U8SUqjNXpXw,5159
|
|
31
31
|
basic_memory/cli/__init__.py,sha256=arcKLAWRDhPD7x5t80MlviZeYzwHZ0GZigyy3NKVoGk,33
|
|
32
|
-
basic_memory/cli/app.py,sha256=
|
|
33
|
-
basic_memory/cli/main.py,sha256=
|
|
34
|
-
basic_memory/cli/commands/__init__.py,sha256=
|
|
35
|
-
basic_memory/cli/commands/
|
|
36
|
-
basic_memory/cli/commands/
|
|
37
|
-
basic_memory/cli/commands/
|
|
38
|
-
basic_memory/cli/commands/
|
|
39
|
-
basic_memory/cli/commands/
|
|
40
|
-
basic_memory/cli/commands/
|
|
41
|
-
basic_memory/cli/commands/
|
|
42
|
-
basic_memory/cli/commands/
|
|
43
|
-
basic_memory/cli/commands/
|
|
44
|
-
basic_memory/cli/commands/sync.py,sha256=gOU_onrMj9_IRiIe8FWU_FLEvfjcOt-qhrvvFJuU-ws,8010
|
|
32
|
+
basic_memory/cli/app.py,sha256=9xBnA4DXB8RFitiB96Jbs8XNsrz_WfE583QhJk_o8vg,2268
|
|
33
|
+
basic_memory/cli/main.py,sha256=ekBbEr_5jjGZaFJiOK4VhZ3wNiu6WjhXVi_syke1xw0,465
|
|
34
|
+
basic_memory/cli/commands/__init__.py,sha256=3oojcC-Y-4RPqff9vtwWziT_T4uvBVicL0pSHNilVkU,393
|
|
35
|
+
basic_memory/cli/commands/db.py,sha256=cEoQltgKudEuJH0Cn-YiPpNaDQzu5-YVwCD0anIIKOA,1480
|
|
36
|
+
basic_memory/cli/commands/import_chatgpt.py,sha256=iVfMo6yrY1EzViSlGL3BnZVkh-k9ht0bbCMJ6dWFCuU,2856
|
|
37
|
+
basic_memory/cli/commands/import_claude_conversations.py,sha256=e8l4OHMr8A9PtKgOO6T9-86Jca6FzCrJAsOzo-EQrlc,2946
|
|
38
|
+
basic_memory/cli/commands/import_claude_projects.py,sha256=YyFXcHWAHJmtR6DNwTtao8nKECoFyo8GripRElqMQ7w,2891
|
|
39
|
+
basic_memory/cli/commands/import_memory_json.py,sha256=fqGT9nILJnIpCtFseaFDox2IodVng48Suev5EQp8Azg,3013
|
|
40
|
+
basic_memory/cli/commands/mcp.py,sha256=Phvj0nsY70dDIEt8-KN_vCoakfwWNsRo4IzvzYWXusY,2593
|
|
41
|
+
basic_memory/cli/commands/project.py,sha256=JaT_j-zckshHrNls15GSnLHV6_ZTqD7GVG69N44j_C8,13877
|
|
42
|
+
basic_memory/cli/commands/status.py,sha256=ttDqI31xTQVPabJKIFi-unTiJKL6SoClJdYqKqLqLXA,5809
|
|
43
|
+
basic_memory/cli/commands/sync.py,sha256=cDs-c8TDOFDCf29MB6u_XUzl3EP3Br8MQyZLu0X4shM,8213
|
|
45
44
|
basic_memory/cli/commands/tool.py,sha256=my-kALn3khv1W2Avi736NrHsfkpbyP57mDi5LjHwqe0,9540
|
|
46
45
|
basic_memory/importers/__init__.py,sha256=BTcBW97P3thcsWa5w9tQsvOu8ynHDgw2-8tPgkCZoh8,795
|
|
47
46
|
basic_memory/importers/base.py,sha256=awwe_U-CfzSINKoM6iro7ru4QqLlsfXzdHztDvebnxM,2531
|
|
48
|
-
basic_memory/importers/chatgpt_importer.py,sha256=
|
|
47
|
+
basic_memory/importers/chatgpt_importer.py,sha256=UqhmWNm7YCfyMRCR53zP_xgtx-UzoY_eOR6Cb8gw6m8,7674
|
|
49
48
|
basic_memory/importers/claude_conversations_importer.py,sha256=p7yehy9Pgc5fef6d0ab9DwCm8CCiyyZkGEqX8U7rHbw,5725
|
|
50
49
|
basic_memory/importers/claude_projects_importer.py,sha256=pFJnX9m7GOv2TrS9f2nM1-mTtheTEBWjxKtwDWdJOGM,5389
|
|
51
|
-
basic_memory/importers/memory_json_importer.py,sha256=
|
|
50
|
+
basic_memory/importers/memory_json_importer.py,sha256=vH0EUpnxftmtXOv_exQjJQ7CihETDkegrEjTq4K96vw,4631
|
|
52
51
|
basic_memory/importers/utils.py,sha256=7n4i_UgSNiOU9i7YlEq-gHOMHjq4gTH69jn0qMz9bIk,1792
|
|
53
52
|
basic_memory/markdown/__init__.py,sha256=DdzioCWtDnKaq05BHYLgL_78FawEHLpLXnp-kPSVfIc,501
|
|
54
53
|
basic_memory/markdown/entity_parser.py,sha256=Gw0RdzWGRcy63RcLP2J2Dcm9g404x0GXirDaWDYjTWg,4516
|
|
@@ -57,18 +56,14 @@ basic_memory/markdown/plugins.py,sha256=gtIzKRjoZsyvBqLpVNnrmzl_cbTZ5ZGn8kcuXxQj
|
|
|
57
56
|
basic_memory/markdown/schemas.py,sha256=eyxYCr1hVyWmImcle0asE5It_DD6ARkqaBZYu1KK5n4,1896
|
|
58
57
|
basic_memory/markdown/utils.py,sha256=G3V_DQbmDj6idsCy6kT-GhVqiV4JPB5gfWKG5wK_SuQ,3410
|
|
59
58
|
basic_memory/mcp/__init__.py,sha256=dsDOhKqjYeIbCULbHIxfcItTbqudEuEg1Np86eq0GEQ,35
|
|
60
|
-
basic_memory/mcp/async_client.py,sha256=
|
|
61
|
-
basic_memory/mcp/
|
|
62
|
-
basic_memory/mcp/
|
|
63
|
-
basic_memory/mcp/
|
|
64
|
-
basic_memory/mcp/server.py,sha256=T8utX0fTA12rAC_TjtWgsfB1z-Q6pdTWJH4HISw73vg,3764
|
|
65
|
-
basic_memory/mcp/supabase_auth_provider.py,sha256=R_E4jzXSDOyPomoHiIqPVjx-VUhPqJSIUbg84mE2YaQ,16518
|
|
66
|
-
basic_memory/mcp/prompts/__init__.py,sha256=UvaIw5KA8PaXj3Wz1Dr-VjlkEq6T5D8AGtYFVwaHqnA,683
|
|
59
|
+
basic_memory/mcp/async_client.py,sha256=ZTH0OH8wowoyIZf_UdbqyuSyWljGSAlZMbPRFF0dowM,925
|
|
60
|
+
basic_memory/mcp/project_session.py,sha256=TaQD7JPeyQY-64sFJG41AXQNFizHegZ9POVLpD1pCBk,4203
|
|
61
|
+
basic_memory/mcp/server.py,sha256=CpEMrifWHqkj6Q--LUtcfKzzsp0-FW8Lf3eHKrlzx2E,1248
|
|
62
|
+
basic_memory/mcp/prompts/__init__.py,sha256=-Bl9Dgj2TD9PULjzggPqXuvPEjWCRy7S9Yg03h2-U7A,615
|
|
67
63
|
basic_memory/mcp/prompts/ai_assistant_guide.py,sha256=8TI5xObiRVcwv6w9by1xQHlX0whvyE7-LGsiqDMRTFg,821
|
|
68
64
|
basic_memory/mcp/prompts/continue_conversation.py,sha256=rsmlC2V7e7G6DAK0K825vFsPKgsRQ702HFzn6lkHaDM,1998
|
|
69
65
|
basic_memory/mcp/prompts/recent_activity.py,sha256=0v1c3b2SdDDxXVuF8eOjNooYy04uRYel0pdJ0rnggw4,3311
|
|
70
66
|
basic_memory/mcp/prompts/search.py,sha256=nb88MZy9tdW_MmCLUVItiukrLdb3xEHWLv0JVLUlc4o,1692
|
|
71
|
-
basic_memory/mcp/prompts/sync_status.py,sha256=0F6YowgqIbAFmGE3vFFJ-D-q1SrTqzGLKYWECgNWaxw,4495
|
|
72
67
|
basic_memory/mcp/prompts/utils.py,sha256=VacrbqwYtySpIlYIrKHo5s6jtoTMscYJqrFRH3zpC6Q,5431
|
|
73
68
|
basic_memory/mcp/resources/ai_assistant_guide.md,sha256=qnYWDkYlb-JmKuOoZ5llmRas_t4dWDXB_i8LE277Lgs,14777
|
|
74
69
|
basic_memory/mcp/resources/project_info.py,sha256=LcUkTx4iXBfU6Lp4TVch78OqLopbOy4ljyKnfr4VXso,1906
|
|
@@ -78,26 +73,26 @@ basic_memory/mcp/tools/canvas.py,sha256=22F9G9gfPb-l8i1B5ra4Ja_h9zYY83rPY9mDA5C5
|
|
|
78
73
|
basic_memory/mcp/tools/delete_note.py,sha256=tSyRc_VgBmLyVeenClwX1Sk--LKcGahAMzTX2mK2XIs,7346
|
|
79
74
|
basic_memory/mcp/tools/edit_note.py,sha256=q4x-f7-j_l-wzm17-AVFT1_WGCo0Cq4lI3seYSe21aY,13570
|
|
80
75
|
basic_memory/mcp/tools/list_directory.py,sha256=-FxDsCru5YD02M4qkQDAurEJWyRaC7YI4YR6zg0atR8,5236
|
|
81
|
-
basic_memory/mcp/tools/move_note.py,sha256=
|
|
76
|
+
basic_memory/mcp/tools/move_note.py,sha256=XvJ9m6rrwsVlb8ScR5ik5OQKcLbNnR52HbyuAhutVYw,17541
|
|
82
77
|
basic_memory/mcp/tools/project_management.py,sha256=zaxzbWUSn2iahca4L44EO8hKMWV-rXqDMXcRce6qhg8,12944
|
|
83
|
-
basic_memory/mcp/tools/read_content.py,sha256=
|
|
84
|
-
basic_memory/mcp/tools/read_note.py,sha256=
|
|
78
|
+
basic_memory/mcp/tools/read_content.py,sha256=lqE63axQf9h0OU36Lh5e1xr164nQhywdaMrPnKw_hyQ,9125
|
|
79
|
+
basic_memory/mcp/tools/read_note.py,sha256=3_U84CsW2cX8oxWz8Ju3CGeonWFLDvPwVI3Oh5eASO4,8132
|
|
85
80
|
basic_memory/mcp/tools/recent_activity.py,sha256=XVjNJAJnmxvzx9_Ls1A-QOd2yTR7pJlSTTuRxSivmN4,4833
|
|
86
81
|
basic_memory/mcp/tools/search.py,sha256=hRmwBXRoxEUOtUOi9WG80NfLluHOG5XpSOArMJumt8o,15883
|
|
87
|
-
basic_memory/mcp/tools/sync_status.py,sha256=
|
|
82
|
+
basic_memory/mcp/tools/sync_status.py,sha256=c91zsYbq7IuC5rF-OaZ8JwgtJOBlPcTks1cSvYO4CiI,10517
|
|
88
83
|
basic_memory/mcp/tools/utils.py,sha256=qVAEkR4naCLrqIo_7xXFubqGGxypouz-DB4_svTvARY,20892
|
|
89
84
|
basic_memory/mcp/tools/view_note.py,sha256=ddNXxyETsdA5SYflIaQVj_Cbd7I7CLVs3atRRDMbGmg,2499
|
|
90
|
-
basic_memory/mcp/tools/write_note.py,sha256=
|
|
85
|
+
basic_memory/mcp/tools/write_note.py,sha256=dxOW-0nTl_619-NjIdTSM8IsTRtTQUkSyEmVfG6W_IM,6612
|
|
91
86
|
basic_memory/models/__init__.py,sha256=j0C4dtFi-FOEaQKR8dQWEG-dJtdQ15NBTiJg4nbIXNU,333
|
|
92
87
|
basic_memory/models/base.py,sha256=4hAXJ8CE1RnjKhb23lPd-QM7G_FXIdTowMJ9bRixspU,225
|
|
93
88
|
basic_memory/models/knowledge.py,sha256=AFxfKS8fRa43Kq3EjJCAufpte4VNC7fs9YfshDrB4o0,7087
|
|
94
|
-
basic_memory/models/project.py,sha256=
|
|
89
|
+
basic_memory/models/project.py,sha256=wQ0zrG0noh1c1kLwpaL6_apKib2EUoIWmuk4KDgScdM,2808
|
|
95
90
|
basic_memory/models/search.py,sha256=PhQ8w4taApSvjh1DpPhB4cH9GTt2E2po-DFZzhnoZkY,1300
|
|
96
91
|
basic_memory/repository/__init__.py,sha256=MWK-o8QikqzOpe5SyPbKQ2ioB5BWA0Upz65tgg-E0DU,327
|
|
97
92
|
basic_memory/repository/entity_repository.py,sha256=4qjR66bI1kvGHXFo3w_owppnCFi_na6sRkoPRAJz-uA,10405
|
|
98
93
|
basic_memory/repository/observation_repository.py,sha256=qhMvHLSjaoT3Fa_cQOKsT5jYPj66GXSytEBMwLAgygQ,2943
|
|
99
94
|
basic_memory/repository/project_info_repository.py,sha256=8XLVAYKkBWQ6GbKj1iqA9OK0FGPHdTlOs7ZtfeUf9t8,338
|
|
100
|
-
basic_memory/repository/project_repository.py,sha256=
|
|
95
|
+
basic_memory/repository/project_repository.py,sha256=e-qi9CPBzmCz-fS4AQFDgg4MVZ3Zqec6fxVjnP_fGaI,3783
|
|
101
96
|
basic_memory/repository/relation_repository.py,sha256=z7Oo5Zz_J-Bj6RvQDpSWR73ZLk2fxG7e7jrMbeFeJvQ,3179
|
|
102
97
|
basic_memory/repository/repository.py,sha256=MJb-cb8QZQbL-Grq_iqv4Kq75aX2yQohLIqh5T4fFxw,15224
|
|
103
98
|
basic_memory/repository/search_repository.py,sha256=qXL3PRtx2sV3Do6zeTxsmsROTnkvnatSj4xObGqAvKo,21936
|
|
@@ -105,7 +100,7 @@ basic_memory/schemas/__init__.py,sha256=mEgIFcdTeb-v4y0gkOh_pA5zyqGbZk-9XbXqlSi6
|
|
|
105
100
|
basic_memory/schemas/base.py,sha256=Fx97DEqzOr7y9zeeseO9qVBYbOft_4OQf9EiVfhOJn4,6738
|
|
106
101
|
basic_memory/schemas/delete.py,sha256=UAR2JK99WMj3gP-yoGWlHD3eZEkvlTSRf8QoYIE-Wfw,1180
|
|
107
102
|
basic_memory/schemas/directory.py,sha256=F9_LrJqRqb_kO08GDKJzXLb2nhbYG2PdVUo5eDD_Kf4,881
|
|
108
|
-
basic_memory/schemas/importer.py,sha256=
|
|
103
|
+
basic_memory/schemas/importer.py,sha256=rDPfQjyjKyjOe26pwp1UH4eDqGwMKfeNs1Fjv5PxOc0,693
|
|
109
104
|
basic_memory/schemas/memory.py,sha256=rLSpU6VT_spnLEiVeYp9lI7FH5IvdbZt19VXFuO-vtM,5833
|
|
110
105
|
basic_memory/schemas/project_info.py,sha256=fcNjUpe25_5uMmKy142ib3p5qEakzs1WJPLkgol5zyw,7047
|
|
111
106
|
basic_memory/schemas/prompt.py,sha256=SpIVfZprQT8E5uP40j3CpBc2nHKflwOo3iZD7BFPIHE,3648
|
|
@@ -115,24 +110,23 @@ basic_memory/schemas/search.py,sha256=ywMsDGAQK2sO2TT5lc-da_k67OKW1x1TenXormHHWv
|
|
|
115
110
|
basic_memory/services/__init__.py,sha256=XGt8WX3fX_0K9L37Msy8HF8nlMZYIG3uQ6mUX6_iJtg,259
|
|
116
111
|
basic_memory/services/context_service.py,sha256=4ReLAF5qifA9ayOePGsVKusw1TWj8oBzRECjrsFiKPI,14462
|
|
117
112
|
basic_memory/services/directory_service.py,sha256=_YOPXseQM4knd7PIFAho9LV_E-FljVE5WVJKQ0uflZs,6017
|
|
118
|
-
basic_memory/services/entity_service.py,sha256=
|
|
113
|
+
basic_memory/services/entity_service.py,sha256=b2HnzWqZOG12bVhzb8iOQ1_94T37-3J7-F9w8f5kGvM,32237
|
|
119
114
|
basic_memory/services/exceptions.py,sha256=oVjQr50XQqnFq1-MNKBilI2ShtHDxypavyDk1UeyHhw,390
|
|
120
115
|
basic_memory/services/file_service.py,sha256=jCrmnEkTQ4t9HF7L_M6BL7tdDqjjzty9hpTo9AzwhvM,10059
|
|
121
|
-
basic_memory/services/initialization.py,sha256=
|
|
116
|
+
basic_memory/services/initialization.py,sha256=Td5Yt5nPSskGMeWZSbVbM1WpO9-Z3w2cJAjAzqZ-EMQ,6715
|
|
122
117
|
basic_memory/services/link_resolver.py,sha256=1-_VFsvqdT5rVBHe8Jrq63U59XQ0hxGezxY8c24Tiow,4594
|
|
123
|
-
basic_memory/services/
|
|
124
|
-
basic_memory/services/project_service.py,sha256=uLIrQB6T1DY3BXrEsLdB2ZlcKnPgjubyn-g6V9vMBzA,27928
|
|
118
|
+
basic_memory/services/project_service.py,sha256=1qsCEMQN5tUxfXxfXQD_NboIRrW3_xac0ztDW60q9kc,30065
|
|
125
119
|
basic_memory/services/search_service.py,sha256=c5Ky0ufz7YPFgHhVzNRQ4OecF_JUrt7nALzpMjobW4M,12782
|
|
126
120
|
basic_memory/services/service.py,sha256=V-d_8gOV07zGIQDpL-Ksqs3ZN9l3qf3HZOK1f_YNTag,336
|
|
127
121
|
basic_memory/services/sync_status_service.py,sha256=CgJdaJ6OFvFjKHIQSVIQX8kEU389Mrz_WS6x8dx2-7c,7504
|
|
128
122
|
basic_memory/sync/__init__.py,sha256=CVHguYH457h2u2xoM8KvOilJC71XJlZ-qUh8lHcjYj4,156
|
|
129
|
-
basic_memory/sync/background_sync.py,sha256=
|
|
130
|
-
basic_memory/sync/sync_service.py,sha256=
|
|
123
|
+
basic_memory/sync/background_sync.py,sha256=VJr2SukRKLdsbfB-9Re4LehcpK15a-RLXAFB-sAdRRM,726
|
|
124
|
+
basic_memory/sync/sync_service.py,sha256=GSpefl6M_TV2lQElWwnogmlERZFmhgZaIwIxWjvnc8I,26549
|
|
131
125
|
basic_memory/sync/watch_service.py,sha256=JAumrHUjV1lF9NtEK32jgg0myWBfLXotNXxONeIV9SM,15316
|
|
132
126
|
basic_memory/templates/prompts/continue_conversation.hbs,sha256=trrDHSXA5S0JCbInMoUJL04xvCGRB_ku1RHNQHtl6ZI,3076
|
|
133
127
|
basic_memory/templates/prompts/search.hbs,sha256=H1cCIsHKp4VC1GrH2KeUB8pGe5vXFPqb2VPotypmeCA,3098
|
|
134
|
-
basic_memory-0.14.
|
|
135
|
-
basic_memory-0.14.
|
|
136
|
-
basic_memory-0.14.
|
|
137
|
-
basic_memory-0.14.
|
|
138
|
-
basic_memory-0.14.
|
|
128
|
+
basic_memory-0.14.3.dist-info/METADATA,sha256=_LzguWWrEH3FfrxDesJjCT7__tnmKqk8J4CSfwt5M6c,17632
|
|
129
|
+
basic_memory-0.14.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
130
|
+
basic_memory-0.14.3.dist-info/entry_points.txt,sha256=wvE2mRF6-Pg4weIYcfQ-86NOLZD4WJg7F7TIsRVFLb8,90
|
|
131
|
+
basic_memory-0.14.3.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
|
|
132
|
+
basic_memory-0.14.3.dist-info/RECORD,,
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
"""OAuth management commands."""
|
|
2
|
-
|
|
3
|
-
import typer
|
|
4
|
-
from typing import Optional
|
|
5
|
-
from pydantic import AnyHttpUrl
|
|
6
|
-
|
|
7
|
-
from basic_memory.cli.app import app
|
|
8
|
-
from basic_memory.mcp.auth_provider import BasicMemoryOAuthProvider
|
|
9
|
-
from mcp.shared.auth import OAuthClientInformationFull
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
auth_app = typer.Typer(help="OAuth client management commands")
|
|
13
|
-
app.add_typer(auth_app, name="auth")
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@auth_app.command()
|
|
17
|
-
def register_client(
|
|
18
|
-
client_id: Optional[str] = typer.Option(
|
|
19
|
-
None, help="Client ID (auto-generated if not provided)"
|
|
20
|
-
),
|
|
21
|
-
client_secret: Optional[str] = typer.Option(
|
|
22
|
-
None, help="Client secret (auto-generated if not provided)"
|
|
23
|
-
),
|
|
24
|
-
issuer_url: str = typer.Option("http://localhost:8000", help="OAuth issuer URL"),
|
|
25
|
-
):
|
|
26
|
-
"""Register a new OAuth client for Basic Memory MCP server."""
|
|
27
|
-
|
|
28
|
-
# Create provider instance
|
|
29
|
-
provider = BasicMemoryOAuthProvider(issuer_url=issuer_url)
|
|
30
|
-
|
|
31
|
-
# Create client info with required redirect_uris
|
|
32
|
-
client_info = OAuthClientInformationFull(
|
|
33
|
-
client_id=client_id or "", # Provider will generate if empty
|
|
34
|
-
client_secret=client_secret or "", # Provider will generate if empty
|
|
35
|
-
redirect_uris=[AnyHttpUrl("http://localhost:8000/callback")], # Default redirect URI
|
|
36
|
-
client_name="Basic Memory OAuth Client",
|
|
37
|
-
grant_types=["authorization_code", "refresh_token"],
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
# Register the client
|
|
41
|
-
import asyncio
|
|
42
|
-
|
|
43
|
-
asyncio.run(provider.register_client(client_info))
|
|
44
|
-
|
|
45
|
-
typer.echo("Client registered successfully!")
|
|
46
|
-
typer.echo(f"Client ID: {client_info.client_id}")
|
|
47
|
-
typer.echo(f"Client Secret: {client_info.client_secret}")
|
|
48
|
-
typer.echo("\nSave these credentials securely - the client secret cannot be retrieved later.")
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
@auth_app.command()
|
|
52
|
-
def test_auth(
|
|
53
|
-
issuer_url: str = typer.Option("http://localhost:8000", help="OAuth issuer URL"),
|
|
54
|
-
):
|
|
55
|
-
"""Test OAuth authentication flow.
|
|
56
|
-
|
|
57
|
-
IMPORTANT: Use the same FASTMCP_AUTH_SECRET_KEY environment variable
|
|
58
|
-
as your MCP server for tokens to validate correctly.
|
|
59
|
-
"""
|
|
60
|
-
|
|
61
|
-
import asyncio
|
|
62
|
-
import secrets
|
|
63
|
-
from mcp.server.auth.provider import AuthorizationParams
|
|
64
|
-
from pydantic import AnyHttpUrl
|
|
65
|
-
|
|
66
|
-
async def test_flow():
|
|
67
|
-
# Create provider with same secret key as server
|
|
68
|
-
provider = BasicMemoryOAuthProvider(issuer_url=issuer_url)
|
|
69
|
-
|
|
70
|
-
# Register a test client
|
|
71
|
-
client_info = OAuthClientInformationFull(
|
|
72
|
-
client_id=secrets.token_urlsafe(16),
|
|
73
|
-
client_secret=secrets.token_urlsafe(32),
|
|
74
|
-
redirect_uris=[AnyHttpUrl("http://localhost:8000/callback")],
|
|
75
|
-
client_name="Test OAuth Client",
|
|
76
|
-
grant_types=["authorization_code", "refresh_token"],
|
|
77
|
-
)
|
|
78
|
-
await provider.register_client(client_info)
|
|
79
|
-
typer.echo(f"Registered test client: {client_info.client_id}")
|
|
80
|
-
|
|
81
|
-
# Get the client
|
|
82
|
-
client = await provider.get_client(client_info.client_id)
|
|
83
|
-
if not client:
|
|
84
|
-
typer.echo("Error: Client not found after registration", err=True)
|
|
85
|
-
return
|
|
86
|
-
|
|
87
|
-
# Create authorization request
|
|
88
|
-
auth_params = AuthorizationParams(
|
|
89
|
-
state="test-state",
|
|
90
|
-
scopes=["read", "write"],
|
|
91
|
-
code_challenge="test-challenge",
|
|
92
|
-
redirect_uri=AnyHttpUrl("http://localhost:8000/callback"),
|
|
93
|
-
redirect_uri_provided_explicitly=True,
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
# Get authorization URL
|
|
97
|
-
auth_url = await provider.authorize(client, auth_params)
|
|
98
|
-
typer.echo(f"Authorization URL: {auth_url}")
|
|
99
|
-
|
|
100
|
-
# Extract auth code from URL
|
|
101
|
-
from urllib.parse import urlparse, parse_qs
|
|
102
|
-
|
|
103
|
-
parsed = urlparse(auth_url)
|
|
104
|
-
params = parse_qs(parsed.query)
|
|
105
|
-
auth_code = params.get("code", [None])[0]
|
|
106
|
-
|
|
107
|
-
if not auth_code:
|
|
108
|
-
typer.echo("Error: No authorization code in URL", err=True)
|
|
109
|
-
return
|
|
110
|
-
|
|
111
|
-
# Load the authorization code
|
|
112
|
-
code_obj = await provider.load_authorization_code(client, auth_code)
|
|
113
|
-
if not code_obj:
|
|
114
|
-
typer.echo("Error: Invalid authorization code", err=True)
|
|
115
|
-
return
|
|
116
|
-
|
|
117
|
-
# Exchange for tokens
|
|
118
|
-
token = await provider.exchange_authorization_code(client, code_obj)
|
|
119
|
-
typer.echo(f"Access token: {token.access_token}")
|
|
120
|
-
typer.echo(f"Refresh token: {token.refresh_token}")
|
|
121
|
-
typer.echo(f"Expires in: {token.expires_in} seconds")
|
|
122
|
-
|
|
123
|
-
# Validate access token
|
|
124
|
-
access_token_obj = await provider.load_access_token(token.access_token)
|
|
125
|
-
if access_token_obj:
|
|
126
|
-
typer.echo("Access token validated successfully!")
|
|
127
|
-
typer.echo(f"Client ID: {access_token_obj.client_id}")
|
|
128
|
-
typer.echo(f"Scopes: {access_token_obj.scopes}")
|
|
129
|
-
else:
|
|
130
|
-
typer.echo("Error: Invalid access token", err=True)
|
|
131
|
-
|
|
132
|
-
asyncio.run(test_flow())
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if __name__ == "__main__":
|
|
136
|
-
auth_app()
|