Skip to content

Commit 99a35a7

Browse files
phernandezclaude
andauthored
feat: Cloud CLI cloud sync via rclone bisync (#322)
Signed-off-by: phernandez <paul@basicmachines.co> Co-authored-by: Claude <noreply@anthropic.com>
1 parent ea2e93d commit 99a35a7

47 files changed

Lines changed: 4981 additions & 2756 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

docs/cloud-cli.md

Lines changed: 448 additions & 254 deletions
Large diffs are not rendered by default.

specs/SPEC-8 TigrisFS Integration.md

Lines changed: 886 additions & 0 deletions
Large diffs are not rendered by default.

specs/SPEC-9 Multi-Project Bidirectional Sync Architecture.md

Lines changed: 1114 additions & 0 deletions
Large diffs are not rendered by default.

src/basic_memory/api/app.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
resource,
2020
search,
2121
prompt_router,
22-
webdav,
2322
)
2423
from basic_memory.config import ConfigManager
2524
from basic_memory.services.initialization import initialize_file_sync, initialize_app
@@ -77,7 +76,6 @@ async def lifespan(app: FastAPI): # pragma: no cover
7776
app.include_router(directory_router.router, prefix="/{project}")
7877
app.include_router(prompt_router.router, prefix="/{project}")
7978
app.include_router(importer_router.router, prefix="/{project}")
80-
app.include_router(webdav.router, prefix="/{project}")
8179

8280
# Project resource router works accross projects
8381
app.include_router(project.project_resource_router)

src/basic_memory/api/routers/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@
77
from . import resource_router as resource
88
from . import search_router as search
99
from . import prompt_router as prompt
10-
from . import webdav_router as webdav
1110

12-
__all__ = ["knowledge", "management", "memory", "project", "resource", "search", "prompt", "webdav"]
11+
__all__ = ["knowledge", "management", "memory", "project", "resource", "search", "prompt"]

src/basic_memory/api/routers/project_router.py

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
"""Router for project management."""
22

33
import os
4-
from fastapi import APIRouter, HTTPException, Path, Body
4+
from fastapi import APIRouter, HTTPException, Path, Body, BackgroundTasks
55
from typing import Optional
6+
from loguru import logger
67

7-
from basic_memory.deps import ProjectServiceDep, ProjectPathDep
8-
from basic_memory.schemas import ProjectInfoResponse
8+
from basic_memory.deps import (
9+
ProjectConfigDep,
10+
ProjectServiceDep,
11+
ProjectPathDep,
12+
SyncServiceDep,
13+
)
14+
from basic_memory.schemas import ProjectInfoResponse, SyncReportResponse
915
from basic_memory.schemas.project_info import (
1016
ProjectList,
1117
ProjectItem,
@@ -97,6 +103,54 @@ async def update_project(
97103
raise HTTPException(status_code=400, detail=str(e))
98104

99105

106+
# Sync project filesystem
107+
@project_router.post("/sync")
108+
async def sync_project(
109+
background_tasks: BackgroundTasks,
110+
sync_service: SyncServiceDep,
111+
project_config: ProjectConfigDep,
112+
):
113+
"""Force project filesystem sync to database.
114+
115+
Scans the project directory and updates the database with any new or modified files.
116+
117+
Args:
118+
background_tasks: FastAPI background tasks
119+
sync_service: Sync service for this project
120+
project_config: Project configuration
121+
122+
Returns:
123+
Response confirming sync was initiated
124+
"""
125+
background_tasks.add_task(sync_service.sync, project_config.home, project_config.name)
126+
logger.info(f"Filesystem sync initiated for project: {project_config.name}")
127+
128+
return {
129+
"status": "sync_started",
130+
"message": f"Filesystem sync initiated for project '{project_config.name}'",
131+
}
132+
133+
134+
@project_router.post("/status", response_model=SyncReportResponse)
135+
async def project_sync_status(
136+
sync_service: SyncServiceDep,
137+
project_config: ProjectConfigDep,
138+
) -> SyncReportResponse:
139+
"""Scan directory for changes compared to database state.
140+
141+
Args:
142+
sync_service: Sync service for this project
143+
project_config: Project configuration
144+
145+
Returns:
146+
Scan report with details on files that need syncing
147+
"""
148+
logger.info(f"Scanning filesystem for project: {project_config.name}")
149+
sync_report = await sync_service.scan(project_config.home)
150+
151+
return SyncReportResponse.from_sync_report(sync_report)
152+
153+
100154
# List all available projects
101155
@project_resource_router.get("/projects", response_model=ProjectList)
102156
async def list_projects(
@@ -259,7 +313,7 @@ async def get_default_project(
259313

260314

261315
# Synchronize projects between config and database
262-
@project_resource_router.post("/sync", response_model=ProjectStatusResponse)
316+
@project_resource_router.post("/config/sync", response_model=ProjectStatusResponse)
263317
async def synchronize_projects(
264318
project_service: ProjectServiceDep,
265319
) -> ProjectStatusResponse:

0 commit comments

Comments
 (0)