Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e594b94
feat(backup): add user asset backup & migration module
Mar 28, 2026
2e35406
style(backup): fix pylint warnings in test files
Mar 28, 2026
2985784
style(backup): fix remaining pylint warnings from CI
Mar 28, 2026
65e12c0
style(backup): fix pylint warnings in importer test files
Mar 28, 2026
e02939f
style(backup): fix all remaining CI pylint warnings
Mar 28, 2026
c44ddfb
style(backup): fix all CI pre-commit failures
Mar 28, 2026
e06f83e
style(backup): fix pylint disable comment placement for CI
Mar 28, 2026
d5e0d42
Merge branch 'main' into feat/user-asset-backup-migration
Apr 8, 2026
375427d
Merge branch 'main' into feat/user-asset-backup-migration
Apr 8, 2026
d31dc50
feat: add user asset backup & migration feature
Apr 10, 2026
0ea9c63
merge: resolve conflict with main (lazy loading + backup route)
Apr 11, 2026
3c991f6
fix: resolve lint issues - raise-missing-from and add validate_package
Apr 11, 2026
4df4d51
style: run prettier on backup frontend files
Apr 11, 2026
4a31a67
style: fix black formatting and flake8 line-too-long
Apr 11, 2026
a89b174
style: fix black+flake8 conflict in version_checker.py
Apr 11, 2026
ea48d63
style: fix add-trailing-comma + black formatting
Apr 11, 2026
191dac2
feat: add export download endpoint + browser auto-download
Apr 11, 2026
65cb8de
fix: use agent workspace instead of WORKING_DIR, add file upload impo…
Apr 11, 2026
102b2eb
feat: complete i18n for all 4 locales, add strategy hints, improve im…
Apr 11, 2026
73056ab
style: prettier fix backup.ts and index.tsx
Apr 11, 2026
47e0a88
merge: resolve conflicts with main (copaw -> qwenpaw rename)
Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions src/copaw/app/crons/backup_job.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
"""Helper to build a CronJobSpec for the daily backup task."""
from __future__ import annotations

from .models import (
CronJobRequest,
CronJobSpec,
DispatchSpec,
DispatchTarget,
JobRuntimeSpec,
ScheduleSpec,
)

BACKUP_JOB_ID = "_backup"
BACKUP_JOB_NAME = "Daily Asset Backup"


def build_backup_cron_job(
*,
schedule: str = "0 2 * * *",
user_id: str = "system",
session_id: str = "backup",
channel: str = "internal",
timezone: str = "UTC",
timeout_seconds: int = 300,
) -> CronJobSpec:
"""Create a :class:`CronJobSpec` for the daily asset backup task.

Parameters
----------
schedule:
Cron expression (5-field). Defaults to ``"0 2 * * *"`` (02:00 daily).
user_id / session_id:
Dispatch target identifiers.
channel:
Dispatch channel name.
timezone:
Timezone for the cron schedule.
timeout_seconds:
Maximum execution time for a single backup run.
"""
return CronJobSpec(
id=BACKUP_JOB_ID,
name=BACKUP_JOB_NAME,
enabled=True,
schedule=ScheduleSpec(cron=schedule, timezone=timezone),
task_type="agent",
request=CronJobRequest(
input="Run daily asset backup",
user_id=user_id,
session_id=session_id,
),
dispatch=DispatchSpec(
channel=channel,
target=DispatchTarget(
user_id=user_id,
session_id=session_id,
),
),
runtime=JobRuntimeSpec(
max_concurrency=1,
timeout_seconds=timeout_seconds,
),
meta={"source": "backup_scheduler"},
)
37 changes: 37 additions & 0 deletions src/copaw/app/workspace/service_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,40 @@ def mcp_config_loader():
ws._service_manager.services["mcp_config_watcher"] = watcher
return watcher
# pylint: enable=protected-access


async def create_backup_service(ws: "Workspace", _):
"""Create backup-related services.

Creates AssetExporter, AssetImporter, BackupScheduler.

Args:
ws: Workspace instance
_: Unused service parameter

Returns:
dict with exporter, importer, and scheduler instances
"""
from ...backup.exporter import AssetExporter
from ...backup.importer import AssetImporter
from ...backup.scheduler import BackupScheduler

# Try to get memory manager for consistent memory snapshots
memory_manager = ws._service_manager.services.get(
"memory_manager",
) # pylint: disable=protected-access

exporter = AssetExporter(memory_manager=memory_manager)
importer = AssetImporter(workspace_dir=ws.workspace_dir)
scheduler = BackupScheduler(exporter=exporter)

services = {
"asset_exporter": exporter,
"asset_importer": importer,
"backup_scheduler": scheduler,
}
ws._service_manager.services.update(
services,
) # pylint: disable=protected-access
logger.info("Backup services registered for agent: %s", ws.agent_id)
return services
2 changes: 2 additions & 0 deletions src/copaw/backup/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# -*- coding: utf-8 -*-
"""CoPaw user asset backup & migration module."""
22 changes: 22 additions & 0 deletions src/copaw/backup/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
"""Custom exceptions for user asset backup & migration."""
from __future__ import annotations


class InvalidAssetPackageError(Exception):
"""Raised when a ZIP asset package is invalid or corrupted.

Examples: missing manifest.json, malformed JSON, checksum mismatch.
"""


class IncompatibleVersionError(Exception):
"""Raised when the asset package schema version is incompatible.

This happens when the package's major version is higher than the
current system version, or when a required migration path is missing.
"""


class InsufficientStorageError(Exception):
"""Raised when there is not enough disk space for backup or import."""
Loading
Loading