basic-memory 0.14.2__py3-none-any.whl → 0.14.4__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 (69) hide show
  1. basic_memory/__init__.py +1 -1
  2. basic_memory/alembic/env.py +3 -1
  3. basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py +53 -0
  4. basic_memory/api/app.py +4 -1
  5. basic_memory/api/routers/management_router.py +3 -1
  6. basic_memory/api/routers/project_router.py +21 -13
  7. basic_memory/api/routers/resource_router.py +3 -3
  8. basic_memory/cli/app.py +3 -3
  9. basic_memory/cli/commands/__init__.py +1 -2
  10. basic_memory/cli/commands/db.py +5 -5
  11. basic_memory/cli/commands/import_chatgpt.py +3 -2
  12. basic_memory/cli/commands/import_claude_conversations.py +3 -1
  13. basic_memory/cli/commands/import_claude_projects.py +3 -1
  14. basic_memory/cli/commands/import_memory_json.py +5 -2
  15. basic_memory/cli/commands/mcp.py +3 -15
  16. basic_memory/cli/commands/project.py +46 -6
  17. basic_memory/cli/commands/status.py +4 -1
  18. basic_memory/cli/commands/sync.py +10 -2
  19. basic_memory/cli/main.py +0 -1
  20. basic_memory/config.py +61 -34
  21. basic_memory/db.py +2 -6
  22. basic_memory/deps.py +3 -2
  23. basic_memory/file_utils.py +65 -0
  24. basic_memory/importers/chatgpt_importer.py +20 -10
  25. basic_memory/importers/memory_json_importer.py +22 -7
  26. basic_memory/importers/utils.py +2 -2
  27. basic_memory/markdown/entity_parser.py +2 -2
  28. basic_memory/markdown/markdown_processor.py +2 -2
  29. basic_memory/markdown/plugins.py +42 -26
  30. basic_memory/markdown/utils.py +1 -1
  31. basic_memory/mcp/async_client.py +22 -2
  32. basic_memory/mcp/project_session.py +6 -4
  33. basic_memory/mcp/prompts/__init__.py +0 -2
  34. basic_memory/mcp/server.py +8 -71
  35. basic_memory/mcp/tools/build_context.py +12 -2
  36. basic_memory/mcp/tools/move_note.py +24 -12
  37. basic_memory/mcp/tools/project_management.py +22 -7
  38. basic_memory/mcp/tools/read_content.py +16 -0
  39. basic_memory/mcp/tools/read_note.py +17 -2
  40. basic_memory/mcp/tools/sync_status.py +3 -2
  41. basic_memory/mcp/tools/write_note.py +9 -1
  42. basic_memory/models/knowledge.py +13 -2
  43. basic_memory/models/project.py +3 -3
  44. basic_memory/repository/entity_repository.py +2 -2
  45. basic_memory/repository/project_repository.py +19 -1
  46. basic_memory/repository/search_repository.py +7 -3
  47. basic_memory/schemas/base.py +40 -10
  48. basic_memory/schemas/importer.py +1 -0
  49. basic_memory/schemas/memory.py +23 -11
  50. basic_memory/services/context_service.py +12 -2
  51. basic_memory/services/directory_service.py +7 -0
  52. basic_memory/services/entity_service.py +56 -10
  53. basic_memory/services/initialization.py +0 -75
  54. basic_memory/services/project_service.py +93 -36
  55. basic_memory/sync/background_sync.py +4 -3
  56. basic_memory/sync/sync_service.py +53 -4
  57. basic_memory/sync/watch_service.py +31 -8
  58. basic_memory/utils.py +234 -71
  59. {basic_memory-0.14.2.dist-info → basic_memory-0.14.4.dist-info}/METADATA +21 -92
  60. {basic_memory-0.14.2.dist-info → basic_memory-0.14.4.dist-info}/RECORD +63 -68
  61. basic_memory/cli/commands/auth.py +0 -136
  62. basic_memory/mcp/auth_provider.py +0 -270
  63. basic_memory/mcp/external_auth_provider.py +0 -321
  64. basic_memory/mcp/prompts/sync_status.py +0 -112
  65. basic_memory/mcp/supabase_auth_provider.py +0 -463
  66. basic_memory/services/migration_service.py +0 -168
  67. {basic_memory-0.14.2.dist-info → basic_memory-0.14.4.dist-info}/WHEEL +0 -0
  68. {basic_memory-0.14.2.dist-info → basic_memory-0.14.4.dist-info}/entry_points.txt +0 -0
  69. {basic_memory-0.14.2.dist-info → basic_memory-0.14.4.dist-info}/licenses/LICENSE +0 -0
@@ -1,138 +1,133 @@
1
- basic_memory/__init__.py,sha256=yUZJCrnWSVfSXfN_chOSAkkW-Kge2Dj3Co6TixwmN9s,256
2
- basic_memory/config.py,sha256=YX6pP8aOMlIx9NoCeKLS0b5cgOnegbWhX2ijJzimLQg,11828
3
- basic_memory/db.py,sha256=bFuJHj_PGEhaj5ZgRItIUSW0ujAFCGgYKO7nZsjbYD0,7582
4
- basic_memory/deps.py,sha256=zXOhqXCoSVIa1iIcO8U6uUiofJn5eT4ycwJkH9I2kX4,12102
5
- basic_memory/file_utils.py,sha256=eaxTKLLEbTIy_Mb_Iv_Dmt4IXAJSrZGVi-Knrpyci3E,6700
6
- basic_memory/utils.py,sha256=yG-HHoSqw6RUD9yU0PkrLdhDAi99r6tmE8fWTvBxrgc,7636
1
+ basic_memory/__init__.py,sha256=mDdoiW51bM3-d6fzochHAwurt6I9-U7K-Ar8WifQv4I,256
2
+ basic_memory/config.py,sha256=boVbIb5qgHmvXQo4XlkpaxxqRmhDXWqHUtjbD-_brxE,13046
3
+ basic_memory/db.py,sha256=JeBTsLP56PqCOwX8CZAB7-ijpPbg-Aco5QebpFqkBtg,7428
4
+ basic_memory/deps.py,sha256=vOKM2cpUm4r84LxonMH7LcgM1bkp-FEUqmgruK6CQR0,12144
5
+ basic_memory/file_utils.py,sha256=p1pjbAHNrAR4H3VuMq_Nh4yjJUKkbnwEAygtg5SrMio,8395
6
+ basic_memory/utils.py,sha256=cHcvxLF48lHL2XbekcEFkY6RVJrFwZfJXu28Itepm7o,12704
7
7
  basic_memory/alembic/alembic.ini,sha256=IEZsnF8CbbZnkwBr67LzKKNobHuzTaQNUvM8Psop5xc,3733
8
- basic_memory/alembic/env.py,sha256=gECjMcc--Hhacy3od1WNIAFyHzv6MUi7F_eQG7k3bRQ,2812
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
12
12
  basic_memory/alembic/versions/502b60eaa905_remove_required_from_entity_permalink.py,sha256=k6xYTmYPM9Ros-7CA7BwZBKYwoK_gmVdC-2n8FAjdoE,1840
13
13
  basic_memory/alembic/versions/5fe1ab1ccebe_add_projects_table.py,sha256=2CCY9ayjzbraYMcinqSsJi9Sc0nu2e-ehkUJus-sz34,4379
14
14
  basic_memory/alembic/versions/647e7a75e2cd_project_constraint_fix.py,sha256=YErFkIpZdicvUil4ZtE6uxSpk5BZCTXZ_TfPE-MgSfo,4210
15
+ basic_memory/alembic/versions/a1b2c3d4e5f6_fix_project_foreign_keys.py,sha256=ItF6VEmSMIKBRZSEse9cUHSQ3xTQpQ1TJBrwournz7g,1884
15
16
  basic_memory/alembic/versions/b3c3938bacdb_relation_to_name_unique_index.py,sha256=RsGymQzfRXV1LSNKiyi0lMilTxW1NgwS9jR67ye2apI,1428
16
17
  basic_memory/alembic/versions/cc7172b46608_update_search_index_schema.py,sha256=kDavR9Qqx9wSu5P3qd4SZq_waIsDG1UMTg2SmDoktMU,3679
17
18
  basic_memory/api/__init__.py,sha256=wCpj-21j1D0KzKl9Ql6unLBVFY0K1uGp_FeSZRKtqpk,72
18
- basic_memory/api/app.py,sha256=ucV1epIVQ210JFdZIs6aZ_UWK8TG78p_NhGf6WOrFk0,2809
19
+ basic_memory/api/app.py,sha256=rWc6wsCUEK9YMPav3_qiiwBASA2s1S_wF1C4ssBm6M8,2897
19
20
  basic_memory/api/template_loader.py,sha256=exbTeXyJRgyLFmSjNeroxjT7X2DWFm2X5qUVa3drbYM,8607
20
21
  basic_memory/api/routers/__init__.py,sha256=REO5CKQ96o5vtGWACcsIxIpWybIUSeKXc83RWbWc8BQ,398
21
22
  basic_memory/api/routers/directory_router.py,sha256=rBQHvuwffUOk0iKvcEs2QlWclgvr8ur24f_pH3-sVRQ,2054
22
23
  basic_memory/api/routers/importer_router.py,sha256=xFUCorkPWo8AF0ya0UrcLmXNf8CjPZdAqddQIH8PO-o,4513
23
24
  basic_memory/api/routers/knowledge_router.py,sha256=4dD_tPpcJGWCuoRKEbQXCS3hoXNWe-VbcGC7xV-8VoE,9738
24
- basic_memory/api/routers/management_router.py,sha256=INT5PzUXhfBH2546CTZKqZnRuECYIA4Ypfgdf6JNyu0,2686
25
+ basic_memory/api/routers/management_router.py,sha256=zbzilNzsYUbFbE2uFXRM33cDn9IbI-73y8C1-b-19O4,2730
25
26
  basic_memory/api/routers/memory_router.py,sha256=a9Cnx3XgwSkO-2ABFzI3wM3PoMGxuyfJFFp7NfFZapc,3003
26
- basic_memory/api/routers/project_router.py,sha256=cXGx6VZMg67jdzMi1Xf8SodtueEI04xRwtiv9ygf2Bg,8239
27
+ basic_memory/api/routers/project_router.py,sha256=3qoto8Fm9pWqg6K0ukdd-VmggMENx7kK4JHvJrOhZ_I,8432
27
28
  basic_memory/api/routers/prompt_router.py,sha256=4wxq6-NREgVJM8N9C0YsN1AAUDD8nkTCOzWyzSqTSFw,9948
28
- basic_memory/api/routers/resource_router.py,sha256=WEJEqEaY_yTKj5-U-rW4kXQKUcJflykgwI6_g_R41ck,8058
29
+ basic_memory/api/routers/resource_router.py,sha256=Uko0RLea8DTXl0hPyGjay_YNyYE5852VrBXhlWs8YGc,8097
29
30
  basic_memory/api/routers/search_router.py,sha256=GD62jlCQTiL_VNsdibi-b1f6H40KCWo9SX2Cl7YH4QU,1226
30
31
  basic_memory/api/routers/utils.py,sha256=nmD1faJOHcnWQjbCanojUwA9xhinf764U8SUqjNXpXw,5159
31
32
  basic_memory/cli/__init__.py,sha256=arcKLAWRDhPD7x5t80MlviZeYzwHZ0GZigyy3NKVoGk,33
32
- basic_memory/cli/app.py,sha256=BCIaiJBPV0ipk8KwPRLNiG2ACKBQH9wo1ewKZm7CV7o,2269
33
- basic_memory/cli/main.py,sha256=_0eW-TWYxj8WyCHSS9kFcrrntpeIsqJrA2P0n6LfFvY,475
34
- basic_memory/cli/commands/__init__.py,sha256=jtnGMOIgOBNJCXQ8xpKbLWhFUD3qgpOraRi19YRNaTc,411
35
- basic_memory/cli/commands/auth.py,sha256=mnTlb75eeQWocWYs67LaYbuEysaYvHkqP7gQ0FEXKjE,4858
36
- basic_memory/cli/commands/db.py,sha256=vhuXruyNBj9-9cQzYqKL29aEDUJE7IUQCxodu0wHuio,1481
37
- basic_memory/cli/commands/import_chatgpt.py,sha256=VU_Kfr4B0lVh_Z2_bmsTO3e3U40wrAOvTAvtblgjses,2773
38
- basic_memory/cli/commands/import_claude_conversations.py,sha256=sXnP0hjfwUapwHQDzxE2HEkCDe8FTaE4cogKILsD1EA,2866
39
- basic_memory/cli/commands/import_claude_projects.py,sha256=mWYIeA-mu_Pq23R7OEtY2XHXG5CAh1dMGIBhckB4zRk,2811
40
- basic_memory/cli/commands/import_memory_json.py,sha256=Vz5rt7KCel5B3Dtv57WPEUJTHCMwFUqQlOCm2djwUi8,2867
41
- basic_memory/cli/commands/mcp.py,sha256=NCqyY5dFHDtYnMDrWQiquoGbALUh-AqN4iRjJlFngxY,3123
42
- basic_memory/cli/commands/project.py,sha256=9OJWoV9kSkLePETUPxNs0v5YceXLgPWO2fU45ZinP9g,12252
43
- basic_memory/cli/commands/status.py,sha256=708EK8-iPjyc1iE5MPECzAyZraGYoGpvYjLwTm-BlQs,5719
44
- basic_memory/cli/commands/sync.py,sha256=gOU_onrMj9_IRiIe8FWU_FLEvfjcOt-qhrvvFJuU-ws,8010
33
+ basic_memory/cli/app.py,sha256=9xBnA4DXB8RFitiB96Jbs8XNsrz_WfE583QhJk_o8vg,2268
34
+ basic_memory/cli/main.py,sha256=ekBbEr_5jjGZaFJiOK4VhZ3wNiu6WjhXVi_syke1xw0,465
35
+ basic_memory/cli/commands/__init__.py,sha256=3oojcC-Y-4RPqff9vtwWziT_T4uvBVicL0pSHNilVkU,393
36
+ basic_memory/cli/commands/db.py,sha256=cEoQltgKudEuJH0Cn-YiPpNaDQzu5-YVwCD0anIIKOA,1480
37
+ basic_memory/cli/commands/import_chatgpt.py,sha256=iVfMo6yrY1EzViSlGL3BnZVkh-k9ht0bbCMJ6dWFCuU,2856
38
+ basic_memory/cli/commands/import_claude_conversations.py,sha256=e8l4OHMr8A9PtKgOO6T9-86Jca6FzCrJAsOzo-EQrlc,2946
39
+ basic_memory/cli/commands/import_claude_projects.py,sha256=YyFXcHWAHJmtR6DNwTtao8nKECoFyo8GripRElqMQ7w,2891
40
+ basic_memory/cli/commands/import_memory_json.py,sha256=fqGT9nILJnIpCtFseaFDox2IodVng48Suev5EQp8Azg,3013
41
+ basic_memory/cli/commands/mcp.py,sha256=Phvj0nsY70dDIEt8-KN_vCoakfwWNsRo4IzvzYWXusY,2593
42
+ basic_memory/cli/commands/project.py,sha256=gKnLp4X3AXKunAIQzGib8StFRkO_nWQ6eDEE81Ahxks,13940
43
+ basic_memory/cli/commands/status.py,sha256=ttDqI31xTQVPabJKIFi-unTiJKL6SoClJdYqKqLqLXA,5809
44
+ basic_memory/cli/commands/sync.py,sha256=cDs-c8TDOFDCf29MB6u_XUzl3EP3Br8MQyZLu0X4shM,8213
45
45
  basic_memory/cli/commands/tool.py,sha256=my-kALn3khv1W2Avi736NrHsfkpbyP57mDi5LjHwqe0,9540
46
46
  basic_memory/importers/__init__.py,sha256=BTcBW97P3thcsWa5w9tQsvOu8ynHDgw2-8tPgkCZoh8,795
47
47
  basic_memory/importers/base.py,sha256=awwe_U-CfzSINKoM6iro7ru4QqLlsfXzdHztDvebnxM,2531
48
- basic_memory/importers/chatgpt_importer.py,sha256=36VAsZarI7XE5r_KxNpWeHM1HPfHj6NfTDuH6ExY4hg,7372
48
+ basic_memory/importers/chatgpt_importer.py,sha256=3BJZUOVSX0cg9G6WdMTDQTscMoG6eWuf6E-c9Qhi0v4,7687
49
49
  basic_memory/importers/claude_conversations_importer.py,sha256=p7yehy9Pgc5fef6d0ab9DwCm8CCiyyZkGEqX8U7rHbw,5725
50
50
  basic_memory/importers/claude_projects_importer.py,sha256=pFJnX9m7GOv2TrS9f2nM1-mTtheTEBWjxKtwDWdJOGM,5389
51
- basic_memory/importers/memory_json_importer.py,sha256=u-cNYPuBsiNcyOLKo8YBXKyCp_dOs5PmkTpw5o8SXnw,3936
52
- basic_memory/importers/utils.py,sha256=7n4i_UgSNiOU9i7YlEq-gHOMHjq4gTH69jn0qMz9bIk,1792
51
+ basic_memory/importers/memory_json_importer.py,sha256=vH0EUpnxftmtXOv_exQjJQ7CihETDkegrEjTq4K96vw,4631
52
+ basic_memory/importers/utils.py,sha256=SRcDrjedYH_PeD9Pu1DenlrDcar7L751ZV3qkzJXRQ4,1818
53
53
  basic_memory/markdown/__init__.py,sha256=DdzioCWtDnKaq05BHYLgL_78FawEHLpLXnp-kPSVfIc,501
54
- basic_memory/markdown/entity_parser.py,sha256=Gw0RdzWGRcy63RcLP2J2Dcm9g404x0GXirDaWDYjTWg,4516
55
- basic_memory/markdown/markdown_processor.py,sha256=S5ny69zu2dlqO7tWJoLrpLSzg8emQIDq7Du7olpJUsk,4968
56
- basic_memory/markdown/plugins.py,sha256=gtIzKRjoZsyvBqLpVNnrmzl_cbTZ5ZGn8kcuXxQjRko,6639
54
+ basic_memory/markdown/entity_parser.py,sha256=1zpvVAtWnCV0uXB4DtmMMNzlxGSE9iulWPUpfJyYWvw,4542
55
+ basic_memory/markdown/markdown_processor.py,sha256=imSVYyyfz0rQe9CyDcZhouB4m6V1haMdq6j3EWdo_wc,4984
56
+ basic_memory/markdown/plugins.py,sha256=9wjnfl82CVHuWPMdLH3Wwhml4MC4vh3zrEiH8qY17yY,7478
57
57
  basic_memory/markdown/schemas.py,sha256=eyxYCr1hVyWmImcle0asE5It_DD6ARkqaBZYu1KK5n4,1896
58
- basic_memory/markdown/utils.py,sha256=G3V_DQbmDj6idsCy6kT-GhVqiV4JPB5gfWKG5wK_SuQ,3410
58
+ basic_memory/markdown/utils.py,sha256=cm3h3C1eFz-zklXx5xaNRE-EBv8d-S5tixbTa5WqubQ,3416
59
59
  basic_memory/mcp/__init__.py,sha256=dsDOhKqjYeIbCULbHIxfcItTbqudEuEg1Np86eq0GEQ,35
60
- basic_memory/mcp/async_client.py,sha256=Eo345wANiBRSM4u3j_Vd6Ax4YtMg7qbWd9PIoFfj61I,236
61
- basic_memory/mcp/auth_provider.py,sha256=NnnxpOUMI1sZhKkzHh4Hhoufo4FuiKByx34TdfPoBX8,10050
62
- basic_memory/mcp/external_auth_provider.py,sha256=Z1GDbr6P4C-flZVHMWkIqAu30kcfeHv2iSp0EYbFuxo,11483
63
- basic_memory/mcp/project_session.py,sha256=KfObBqUFUKNGlcApCfQcsqMYsmtWs72OdIcQ79ZSWhk,4142
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
60
+ basic_memory/mcp/async_client.py,sha256=ZTH0OH8wowoyIZf_UdbqyuSyWljGSAlZMbPRFF0dowM,925
61
+ basic_memory/mcp/project_session.py,sha256=TaQD7JPeyQY-64sFJG41AXQNFizHegZ9POVLpD1pCBk,4203
62
+ basic_memory/mcp/server.py,sha256=CpEMrifWHqkj6Q--LUtcfKzzsp0-FW8Lf3eHKrlzx2E,1248
63
+ basic_memory/mcp/prompts/__init__.py,sha256=-Bl9Dgj2TD9PULjzggPqXuvPEjWCRy7S9Yg03h2-U7A,615
67
64
  basic_memory/mcp/prompts/ai_assistant_guide.py,sha256=8TI5xObiRVcwv6w9by1xQHlX0whvyE7-LGsiqDMRTFg,821
68
65
  basic_memory/mcp/prompts/continue_conversation.py,sha256=rsmlC2V7e7G6DAK0K825vFsPKgsRQ702HFzn6lkHaDM,1998
69
66
  basic_memory/mcp/prompts/recent_activity.py,sha256=0v1c3b2SdDDxXVuF8eOjNooYy04uRYel0pdJ0rnggw4,3311
70
67
  basic_memory/mcp/prompts/search.py,sha256=nb88MZy9tdW_MmCLUVItiukrLdb3xEHWLv0JVLUlc4o,1692
71
- basic_memory/mcp/prompts/sync_status.py,sha256=0F6YowgqIbAFmGE3vFFJ-D-q1SrTqzGLKYWECgNWaxw,4495
72
68
  basic_memory/mcp/prompts/utils.py,sha256=VacrbqwYtySpIlYIrKHo5s6jtoTMscYJqrFRH3zpC6Q,5431
73
69
  basic_memory/mcp/resources/ai_assistant_guide.md,sha256=qnYWDkYlb-JmKuOoZ5llmRas_t4dWDXB_i8LE277Lgs,14777
74
70
  basic_memory/mcp/resources/project_info.py,sha256=LcUkTx4iXBfU6Lp4TVch78OqLopbOy4ljyKnfr4VXso,1906
75
71
  basic_memory/mcp/tools/__init__.py,sha256=hyt3HdUuw7djZForr24Qpw8EnOMQaDCm0_BTs-CaX-Y,1619
76
- basic_memory/mcp/tools/build_context.py,sha256=ckKAt3uPXz5hzT_e68PuZuK8_tquo2OOai4uM_yxl44,4611
72
+ basic_memory/mcp/tools/build_context.py,sha256=41JGBL0_Ne0J4q8HXbDPmIZFRNyrwD5NT4EhWE_SlEQ,4975
77
73
  basic_memory/mcp/tools/canvas.py,sha256=22F9G9gfPb-l8i1B5ra4Ja_h9zYY83rPY9mDA5C5gkY,3738
78
74
  basic_memory/mcp/tools/delete_note.py,sha256=tSyRc_VgBmLyVeenClwX1Sk--LKcGahAMzTX2mK2XIs,7346
79
75
  basic_memory/mcp/tools/edit_note.py,sha256=q4x-f7-j_l-wzm17-AVFT1_WGCo0Cq4lI3seYSe21aY,13570
80
76
  basic_memory/mcp/tools/list_directory.py,sha256=-FxDsCru5YD02M4qkQDAurEJWyRaC7YI4YR6zg0atR8,5236
81
- basic_memory/mcp/tools/move_note.py,sha256=jAsCFXrcWXPoBWlWcW8y3Tli5MkKwCQK-n6IwUZoOK8,17357
82
- basic_memory/mcp/tools/project_management.py,sha256=zaxzbWUSn2iahca4L44EO8hKMWV-rXqDMXcRce6qhg8,12944
83
- basic_memory/mcp/tools/read_content.py,sha256=4FTw13B8UjVVhR78NJB9HKeJb_nA6-BGT1WdGtekN5Q,8596
84
- basic_memory/mcp/tools/read_note.py,sha256=V08NdBqWY8Y0Q4zuwK--zN3VK7fmuCH1mOYZKwL1IT4,7614
77
+ basic_memory/mcp/tools/move_note.py,sha256=XvJ9m6rrwsVlb8ScR5ik5OQKcLbNnR52HbyuAhutVYw,17541
78
+ basic_memory/mcp/tools/project_management.py,sha256=daaa7t9xrCLno3VLMBpda3adMw7cumIO-_R_ncBjMsc,13624
79
+ basic_memory/mcp/tools/read_content.py,sha256=lqE63axQf9h0OU36Lh5e1xr164nQhywdaMrPnKw_hyQ,9125
80
+ basic_memory/mcp/tools/read_note.py,sha256=_IbyJLCajJoI2ztFhyv_n9rpN-P64BPuw4QGXbR7D4w,8170
85
81
  basic_memory/mcp/tools/recent_activity.py,sha256=XVjNJAJnmxvzx9_Ls1A-QOd2yTR7pJlSTTuRxSivmN4,4833
86
82
  basic_memory/mcp/tools/search.py,sha256=hRmwBXRoxEUOtUOi9WG80NfLluHOG5XpSOArMJumt8o,15883
87
- basic_memory/mcp/tools/sync_status.py,sha256=mt0DdcaAlyiKW4NK4gy6psajSqcez0bOm_4MzG1NOdg,10486
83
+ basic_memory/mcp/tools/sync_status.py,sha256=c91zsYbq7IuC5rF-OaZ8JwgtJOBlPcTks1cSvYO4CiI,10517
88
84
  basic_memory/mcp/tools/utils.py,sha256=qVAEkR4naCLrqIo_7xXFubqGGxypouz-DB4_svTvARY,20892
89
85
  basic_memory/mcp/tools/view_note.py,sha256=ddNXxyETsdA5SYflIaQVj_Cbd7I7CLVs3atRRDMbGmg,2499
90
- basic_memory/mcp/tools/write_note.py,sha256=GFmX_VLJvcqK29-ADTCDnPgBaweAq_9IBGCs99mwFTw,6178
86
+ basic_memory/mcp/tools/write_note.py,sha256=dxOW-0nTl_619-NjIdTSM8IsTRtTQUkSyEmVfG6W_IM,6612
91
87
  basic_memory/models/__init__.py,sha256=j0C4dtFi-FOEaQKR8dQWEG-dJtdQ15NBTiJg4nbIXNU,333
92
88
  basic_memory/models/base.py,sha256=4hAXJ8CE1RnjKhb23lPd-QM7G_FXIdTowMJ9bRixspU,225
93
- basic_memory/models/knowledge.py,sha256=AFxfKS8fRa43Kq3EjJCAufpte4VNC7fs9YfshDrB4o0,7087
94
- basic_memory/models/project.py,sha256=oUrQaUOu7_muSl-i38Dh0HzmCFrMAtwgxALDUTt9k5c,2773
89
+ basic_memory/models/knowledge.py,sha256=t493m_jYw_80xtqYRauyJ5pDaa1RlvpMduHw7JCNWuA,7702
90
+ basic_memory/models/project.py,sha256=b1ujg-mM-XqLtKw29RVUtLWMgZb9qOllfHmA5ZS1WuQ,2838
95
91
  basic_memory/models/search.py,sha256=PhQ8w4taApSvjh1DpPhB4cH9GTt2E2po-DFZzhnoZkY,1300
96
92
  basic_memory/repository/__init__.py,sha256=MWK-o8QikqzOpe5SyPbKQ2ioB5BWA0Upz65tgg-E0DU,327
97
- basic_memory/repository/entity_repository.py,sha256=4qjR66bI1kvGHXFo3w_owppnCFi_na6sRkoPRAJz-uA,10405
93
+ basic_memory/repository/entity_repository.py,sha256=QaWYJf9kx55illiLXFCAOA7-fXJX5IVf4m8FspdPzY0,10429
98
94
  basic_memory/repository/observation_repository.py,sha256=qhMvHLSjaoT3Fa_cQOKsT5jYPj66GXSytEBMwLAgygQ,2943
99
95
  basic_memory/repository/project_info_repository.py,sha256=8XLVAYKkBWQ6GbKj1iqA9OK0FGPHdTlOs7ZtfeUf9t8,338
100
- basic_memory/repository/project_repository.py,sha256=sgdKxKTSiiOZTzABwUNqli7K5mbXiPiQEAc5r0RD_jQ,3159
96
+ basic_memory/repository/project_repository.py,sha256=Fvo_pCylz8okFiv3FD3nv6PNQknLuC7f3S11fJNQkic,3795
101
97
  basic_memory/repository/relation_repository.py,sha256=z7Oo5Zz_J-Bj6RvQDpSWR73ZLk2fxG7e7jrMbeFeJvQ,3179
102
98
  basic_memory/repository/repository.py,sha256=MJb-cb8QZQbL-Grq_iqv4Kq75aX2yQohLIqh5T4fFxw,15224
103
- basic_memory/repository/search_repository.py,sha256=qXL3PRtx2sV3Do6zeTxsmsROTnkvnatSj4xObGqAvKo,21936
99
+ basic_memory/repository/search_repository.py,sha256=VQfxYZqYupT9-HiWz3_UvhpIRNzpjb4aDrbIdQj8x6E,22171
104
100
  basic_memory/schemas/__init__.py,sha256=mEgIFcdTeb-v4y0gkOh_pA5zyqGbZk-9XbXqlSi6WMs,1674
105
- basic_memory/schemas/base.py,sha256=Fx97DEqzOr7y9zeeseO9qVBYbOft_4OQf9EiVfhOJn4,6738
101
+ basic_memory/schemas/base.py,sha256=XKrEJyGP8QpFuDOeoAicF8iOh4V-jF1XCdLIs1Byp6k,8270
106
102
  basic_memory/schemas/delete.py,sha256=UAR2JK99WMj3gP-yoGWlHD3eZEkvlTSRf8QoYIE-Wfw,1180
107
103
  basic_memory/schemas/directory.py,sha256=F9_LrJqRqb_kO08GDKJzXLb2nhbYG2PdVUo5eDD_Kf4,881
108
- basic_memory/schemas/importer.py,sha256=FAh-RGxuhFW2rz3HFxwLzENJOiGgbTR2hUeXZZpM3OA,663
109
- basic_memory/schemas/memory.py,sha256=rLSpU6VT_spnLEiVeYp9lI7FH5IvdbZt19VXFuO-vtM,5833
104
+ basic_memory/schemas/importer.py,sha256=rDPfQjyjKyjOe26pwp1UH4eDqGwMKfeNs1Fjv5PxOc0,693
105
+ basic_memory/schemas/memory.py,sha256=K9RyK4MS0nzuV9JhRINLKSsO5BpmACI8ldNzBPs4f_s,6388
110
106
  basic_memory/schemas/project_info.py,sha256=fcNjUpe25_5uMmKy142ib3p5qEakzs1WJPLkgol5zyw,7047
111
107
  basic_memory/schemas/prompt.py,sha256=SpIVfZprQT8E5uP40j3CpBc2nHKflwOo3iZD7BFPIHE,3648
112
108
  basic_memory/schemas/request.py,sha256=Mv5EvrLZlFIiPr8dOjo_4QXvkseYhQI7cd_X2zDsxQM,3760
113
109
  basic_memory/schemas/response.py,sha256=XupGYKKr5I2D7Qg9HCSD_c-0A-C1BPA8FNIvHK6Gars,6450
114
110
  basic_memory/schemas/search.py,sha256=ywMsDGAQK2sO2TT5lc-da_k67OKW1x1TenXormHHWv4,3657
115
111
  basic_memory/services/__init__.py,sha256=XGt8WX3fX_0K9L37Msy8HF8nlMZYIG3uQ6mUX6_iJtg,259
116
- basic_memory/services/context_service.py,sha256=4ReLAF5qifA9ayOePGsVKusw1TWj8oBzRECjrsFiKPI,14462
117
- basic_memory/services/directory_service.py,sha256=_YOPXseQM4knd7PIFAho9LV_E-FljVE5WVJKQ0uflZs,6017
118
- basic_memory/services/entity_service.py,sha256=fNUWPsprigdy6DjIyGnkeBZnY81qLXRbC5qlwlpluu4,30440
112
+ basic_memory/services/context_service.py,sha256=DaxfDEC2mOFyfqmOMoKHAwa-GS6cQub8tfwJDaiHJdI,15103
113
+ basic_memory/services/directory_service.py,sha256=_FGX9yunAfmeKCfGG4wKC5oN9eBj2R12v0fwH3GcNxo,6273
114
+ basic_memory/services/entity_service.py,sha256=eYwJYwAG7xGzCeVU5eeLmKJP-oKDqvjPUN0d3L1VbJs,32253
119
115
  basic_memory/services/exceptions.py,sha256=oVjQr50XQqnFq1-MNKBilI2ShtHDxypavyDk1UeyHhw,390
120
116
  basic_memory/services/file_service.py,sha256=jCrmnEkTQ4t9HF7L_M6BL7tdDqjjzty9hpTo9AzwhvM,10059
121
- basic_memory/services/initialization.py,sha256=HN1NhFTEPHjpzBwabVkvFbJ_ldXJXuNaww4ugh7MJos,9717
117
+ basic_memory/services/initialization.py,sha256=Td5Yt5nPSskGMeWZSbVbM1WpO9-Z3w2cJAjAzqZ-EMQ,6715
122
118
  basic_memory/services/link_resolver.py,sha256=1-_VFsvqdT5rVBHe8Jrq63U59XQ0hxGezxY8c24Tiow,4594
123
- basic_memory/services/migration_service.py,sha256=pFJCSD7UgHLx1CHvtN4Df1CzDEp-CZ9Vqx4XYn1m1M0,6096
124
- basic_memory/services/project_service.py,sha256=uLIrQB6T1DY3BXrEsLdB2ZlcKnPgjubyn-g6V9vMBzA,27928
119
+ basic_memory/services/project_service.py,sha256=M9tukO5Ph847a7ZWrimcB8b-yNcQz3LKRvhvc7aU8GI,30175
125
120
  basic_memory/services/search_service.py,sha256=c5Ky0ufz7YPFgHhVzNRQ4OecF_JUrt7nALzpMjobW4M,12782
126
121
  basic_memory/services/service.py,sha256=V-d_8gOV07zGIQDpL-Ksqs3ZN9l3qf3HZOK1f_YNTag,336
127
122
  basic_memory/services/sync_status_service.py,sha256=CgJdaJ6OFvFjKHIQSVIQX8kEU389Mrz_WS6x8dx2-7c,7504
128
123
  basic_memory/sync/__init__.py,sha256=CVHguYH457h2u2xoM8KvOilJC71XJlZ-qUh8lHcjYj4,156
129
- basic_memory/sync/background_sync.py,sha256=4CEx8oP6-qD33uCeowhpzhA8wivmWxaCmSBP37h3Fs8,714
130
- basic_memory/sync/sync_service.py,sha256=AxC5J1YTcPWTmA0HdzvOZBthi4-_LZ44kNF0KQoDRPw,23387
131
- basic_memory/sync/watch_service.py,sha256=JAumrHUjV1lF9NtEK32jgg0myWBfLXotNXxONeIV9SM,15316
124
+ basic_memory/sync/background_sync.py,sha256=VJr2SukRKLdsbfB-9Re4LehcpK15a-RLXAFB-sAdRRM,726
125
+ basic_memory/sync/sync_service.py,sha256=-cN3fV1VqO_LtnfcJlbsNAMSmM_tcUNVdUAMlh11XVo,26581
126
+ basic_memory/sync/watch_service.py,sha256=1KsgZn7B_izo5ECe_mhwJm_ewi0hYjjVQckhU8EqhX0,16877
132
127
  basic_memory/templates/prompts/continue_conversation.hbs,sha256=trrDHSXA5S0JCbInMoUJL04xvCGRB_ku1RHNQHtl6ZI,3076
133
128
  basic_memory/templates/prompts/search.hbs,sha256=H1cCIsHKp4VC1GrH2KeUB8pGe5vXFPqb2VPotypmeCA,3098
134
- basic_memory-0.14.2.dist-info/METADATA,sha256=h7tyu5dCVDkJYGeV4XPL2RbK7hL3CrKM9n5UlrUapWc,17639
135
- basic_memory-0.14.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
136
- basic_memory-0.14.2.dist-info/entry_points.txt,sha256=wvE2mRF6-Pg4weIYcfQ-86NOLZD4WJg7F7TIsRVFLb8,90
137
- basic_memory-0.14.2.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
138
- basic_memory-0.14.2.dist-info/RECORD,,
129
+ basic_memory-0.14.4.dist-info/METADATA,sha256=zoI_IydCEe0CWVkDlOQ8X_DyNhqexppXE6RIWE3lokY,14329
130
+ basic_memory-0.14.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
131
+ basic_memory-0.14.4.dist-info/entry_points.txt,sha256=wvE2mRF6-Pg4weIYcfQ-86NOLZD4WJg7F7TIsRVFLb8,90
132
+ basic_memory-0.14.4.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
133
+ basic_memory-0.14.4.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()
@@ -1,270 +0,0 @@
1
- """OAuth authentication provider for Basic Memory MCP server."""
2
-
3
- import secrets
4
- from datetime import datetime, timedelta, timezone
5
- from typing import Dict, Optional
6
-
7
- import jwt
8
- from mcp.server.auth.provider import (
9
- OAuthAuthorizationServerProvider,
10
- AuthorizationParams,
11
- AuthorizationCode,
12
- RefreshToken,
13
- AccessToken,
14
- )
15
- from mcp.shared.auth import OAuthClientInformationFull, OAuthToken
16
- from loguru import logger
17
-
18
-
19
- class BasicMemoryAuthorizationCode(AuthorizationCode):
20
- """Extended authorization code with additional metadata."""
21
-
22
- issuer_state: Optional[str] = None
23
-
24
-
25
- class BasicMemoryRefreshToken(RefreshToken):
26
- """Extended refresh token with additional metadata."""
27
-
28
- pass
29
-
30
-
31
- class BasicMemoryAccessToken(AccessToken):
32
- """Extended access token with additional metadata."""
33
-
34
- pass
35
-
36
-
37
- class BasicMemoryOAuthProvider(
38
- OAuthAuthorizationServerProvider[
39
- BasicMemoryAuthorizationCode, BasicMemoryRefreshToken, BasicMemoryAccessToken
40
- ]
41
- ):
42
- """OAuth provider for Basic Memory MCP server.
43
-
44
- This is a simple in-memory implementation that can be extended
45
- to integrate with external OAuth providers or use persistent storage.
46
- """
47
-
48
- def __init__(self, issuer_url: str = "http://localhost:8000", secret_key: Optional[str] = None):
49
- self.issuer_url = issuer_url
50
- # Use environment variable for secret key if available, otherwise generate
51
- import os
52
-
53
- self.secret_key = (
54
- secret_key or os.getenv("FASTMCP_AUTH_SECRET_KEY") or secrets.token_urlsafe(32)
55
- )
56
-
57
- # In-memory storage - in production, use a proper database
58
- self.clients: Dict[str, OAuthClientInformationFull] = {}
59
- self.authorization_codes: Dict[str, BasicMemoryAuthorizationCode] = {}
60
- self.refresh_tokens: Dict[str, BasicMemoryRefreshToken] = {}
61
- self.access_tokens: Dict[str, BasicMemoryAccessToken] = {}
62
-
63
- async def get_client(self, client_id: str) -> Optional[OAuthClientInformationFull]:
64
- """Get a client by ID."""
65
- return self.clients.get(client_id)
66
-
67
- async def register_client(self, client_info: OAuthClientInformationFull) -> None:
68
- """Register a new OAuth client."""
69
- # Generate client ID if not provided
70
- if not client_info.client_id:
71
- client_info.client_id = secrets.token_urlsafe(16)
72
-
73
- # Generate client secret if not provided
74
- if not client_info.client_secret:
75
- client_info.client_secret = secrets.token_urlsafe(32)
76
-
77
- self.clients[client_info.client_id] = client_info
78
- logger.info(f"Registered OAuth client: {client_info.client_id}")
79
-
80
- async def authorize(
81
- self, client: OAuthClientInformationFull, params: AuthorizationParams
82
- ) -> str:
83
- """Create an authorization URL for the OAuth flow.
84
-
85
- For basic-memory, we'll implement a simple authorization flow.
86
- In production, this might redirect to an external provider.
87
- """
88
- # Generate authorization code
89
- auth_code = secrets.token_urlsafe(32)
90
-
91
- # Store authorization code with metadata
92
- self.authorization_codes[auth_code] = BasicMemoryAuthorizationCode(
93
- code=auth_code,
94
- scopes=params.scopes or [],
95
- expires_at=(datetime.now(timezone.utc) + timedelta(minutes=10)).timestamp(),
96
- client_id=client.client_id,
97
- code_challenge=params.code_challenge,
98
- redirect_uri=params.redirect_uri,
99
- redirect_uri_provided_explicitly=params.redirect_uri_provided_explicitly,
100
- issuer_state=params.state,
101
- )
102
-
103
- # In a real implementation, we'd redirect to an authorization page
104
- # For now, we'll just return the redirect URL with the code
105
- redirect_uri = str(params.redirect_uri)
106
- separator = "&" if "?" in redirect_uri else "?"
107
-
108
- auth_url = f"{redirect_uri}{separator}code={auth_code}"
109
- if params.state:
110
- auth_url += f"&state={params.state}"
111
-
112
- return auth_url
113
-
114
- async def load_authorization_code(
115
- self, client: OAuthClientInformationFull, authorization_code: str
116
- ) -> Optional[BasicMemoryAuthorizationCode]:
117
- """Load an authorization code."""
118
- code = self.authorization_codes.get(authorization_code)
119
-
120
- if code and code.client_id == client.client_id:
121
- # Check if expired
122
- if datetime.now(timezone.utc).timestamp() > code.expires_at:
123
- del self.authorization_codes[authorization_code]
124
- return None
125
- return code
126
-
127
- return None
128
-
129
- async def exchange_authorization_code(
130
- self, client: OAuthClientInformationFull, authorization_code: BasicMemoryAuthorizationCode
131
- ) -> OAuthToken:
132
- """Exchange an authorization code for tokens."""
133
- # Generate tokens
134
- access_token = self._generate_access_token(client.client_id, authorization_code.scopes)
135
- refresh_token = secrets.token_urlsafe(32)
136
-
137
- # Store tokens
138
- expires_at = (datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()
139
-
140
- self.access_tokens[access_token] = BasicMemoryAccessToken(
141
- token=access_token,
142
- client_id=client.client_id,
143
- scopes=authorization_code.scopes,
144
- expires_at=int(expires_at),
145
- )
146
-
147
- self.refresh_tokens[refresh_token] = BasicMemoryRefreshToken(
148
- token=refresh_token,
149
- client_id=client.client_id,
150
- scopes=authorization_code.scopes,
151
- )
152
-
153
- # Remove used authorization code
154
- del self.authorization_codes[authorization_code.code]
155
-
156
- return OAuthToken(
157
- access_token=access_token,
158
- token_type="bearer",
159
- expires_in=3600, # 1 hour
160
- refresh_token=refresh_token,
161
- scope=" ".join(authorization_code.scopes) if authorization_code.scopes else None,
162
- )
163
-
164
- async def load_refresh_token(
165
- self, client: OAuthClientInformationFull, refresh_token: str
166
- ) -> Optional[BasicMemoryRefreshToken]:
167
- """Load a refresh token."""
168
- token = self.refresh_tokens.get(refresh_token)
169
-
170
- if token and token.client_id == client.client_id:
171
- return token
172
-
173
- return None
174
-
175
- async def exchange_refresh_token(
176
- self,
177
- client: OAuthClientInformationFull,
178
- refresh_token: BasicMemoryRefreshToken,
179
- scopes: list[str],
180
- ) -> OAuthToken:
181
- """Exchange a refresh token for new tokens."""
182
- # Use requested scopes or original scopes
183
- token_scopes = scopes if scopes else refresh_token.scopes
184
-
185
- # Generate new tokens
186
- new_access_token = self._generate_access_token(client.client_id, token_scopes)
187
- new_refresh_token = secrets.token_urlsafe(32)
188
-
189
- # Store new tokens
190
- expires_at = (datetime.now(timezone.utc) + timedelta(hours=1)).timestamp()
191
-
192
- self.access_tokens[new_access_token] = BasicMemoryAccessToken(
193
- token=new_access_token,
194
- client_id=client.client_id,
195
- scopes=token_scopes,
196
- expires_at=int(expires_at),
197
- )
198
-
199
- self.refresh_tokens[new_refresh_token] = BasicMemoryRefreshToken(
200
- token=new_refresh_token,
201
- client_id=client.client_id,
202
- scopes=token_scopes,
203
- )
204
-
205
- # Remove old tokens
206
- del self.refresh_tokens[refresh_token.token]
207
-
208
- return OAuthToken(
209
- access_token=new_access_token,
210
- token_type="bearer",
211
- expires_in=3600, # 1 hour
212
- refresh_token=new_refresh_token,
213
- scope=" ".join(token_scopes) if token_scopes else None,
214
- )
215
-
216
- async def load_access_token(self, token: str) -> Optional[BasicMemoryAccessToken]:
217
- """Load and validate an access token."""
218
- logger.debug("Loading access token, checking in-memory store first")
219
- access_token = self.access_tokens.get(token)
220
-
221
- if access_token:
222
- # Check if expired
223
- if access_token.expires_at and datetime.now(timezone.utc).timestamp() > access_token.expires_at:
224
- logger.debug("Token found in memory but expired, removing")
225
- del self.access_tokens[token]
226
- return None
227
- logger.debug("Token found in memory and valid")
228
- return access_token
229
-
230
- # Try to decode as JWT
231
- logger.debug("Token not in memory, attempting JWT decode with secret key")
232
- try:
233
- # Decode with audience verification - PyJWT expects the audience to match
234
- payload = jwt.decode(
235
- token,
236
- self.secret_key,
237
- algorithms=["HS256"],
238
- audience="basic-memory", # Expecting this audience
239
- issuer=self.issuer_url, # And this issuer
240
- )
241
- logger.debug(f"JWT decoded successfully: {payload}")
242
- return BasicMemoryAccessToken(
243
- token=token,
244
- client_id=payload.get("sub", ""),
245
- scopes=payload.get("scopes", []),
246
- expires_at=payload.get("exp"),
247
- )
248
- except jwt.InvalidTokenError as e:
249
- logger.error(f"JWT decode failed: {e}")
250
- return None
251
-
252
- async def revoke_token(self, token: BasicMemoryAccessToken | BasicMemoryRefreshToken) -> None:
253
- """Revoke an access or refresh token."""
254
- if isinstance(token, BasicMemoryAccessToken):
255
- self.access_tokens.pop(token.token, None)
256
- else:
257
- self.refresh_tokens.pop(token.token, None)
258
-
259
- def _generate_access_token(self, client_id: str, scopes: list[str]) -> str:
260
- """Generate a JWT access token."""
261
- payload = {
262
- "iss": self.issuer_url,
263
- "sub": client_id,
264
- "aud": "basic-memory",
265
- "exp": datetime.now(timezone.utc) + timedelta(hours=1),
266
- "iat": datetime.now(timezone.utc),
267
- "scopes": scopes,
268
- }
269
-
270
- return jwt.encode(payload, self.secret_key, algorithm="HS256")