Metadata-Version: 2.4
Name: opencode-migrate
Version: 0.1.0
Summary: Migrate OpenCode sessions between servers
License-Expression: MIT
Project-URL: Repository, https://github.com/anomalyco/opencode
Requires-Python: >=3.10
Description-Content-Type: text/markdown

# opencode-migrate

Migrate [OpenCode](https://github.com/anomalyco/opencode) sessions between servers.

Exports full session state — messages, parts, inputs, todos, context epochs, and
git-based file snapshots — from one OpenCode instance and imports it into another.

## Installation

```bash
pip install -e .
# or
pip install opencode-migrate
```

No external dependencies required (stdlib only: `sqlite3`, `json`, `subprocess`, `tarfile`).

## Quick start

```python
from opencode_migrate import SessionMigrator

# Push a session to another server via SSH
m = SessionMigrator()
m.migrate_to_remote(
    session_id="sess-abc123def456",
    remote_host="server2.example.com",
    remote_user="deploy",
)
```

## CLI usage

```bash
# List sessions on this machine
opencode-migrate list

# Export to a portable bundle
opencode-migrate export <session-id> -o /tmp/bundle

# Import a bundle on the target machine
opencode-migrate import /tmp/bundle

# Push directly over SSH
opencode-migrate push <session-id> --host server2 --user deploy

# Pull from a remote server
opencode-migrate pull <session-id> --host server1 --user deploy
```

### Path remapping

Since the project directory differs between machines, use `--remap-directory`:

```bash
opencode-migrate push sess-abc --host server2 --remap-directory /home/deploy/myproject
```

## Database path resolution

`opencode-migrate` automatically detects the database location using the same
environment variables as OpenCode itself. No manual `--db` flag needed in most cases.

### How OpenCode resolves the DB path

The resolution logic (from `packages/core/src/database/database.ts`) is:

1. **`OPENCODE_DB` env var** (highest priority):
   - Absolute path → used directly (e.g. `OPENCODE_DB=/srv/opencode/my.db`)
   - Relative name → joined with data dir (e.g. `OPENCODE_DB=custom.db` → `$XDG_DATA_HOME/opencode/custom.db`)
   - `:memory:` → in-memory SQLite (not migratable)

2. **Channel-based naming** (when `OPENCODE_DB` is not set):
   - Channels `latest`, `beta`, `prod` → `$XDG_DATA_HOME/opencode/opencode.db`
   - Other channels (e.g. `local`) → `$XDG_DATA_HOME/opencode/opencode-{channel}.db`
   - Set `OPENCODE_DISABLE_CHANNEL_DB=1` to force the default `opencode.db` name

3. **Data directory** (`$XDG_DATA_HOME/opencode/`):
   - `XDG_DATA_HOME` env var if set, otherwise `~/.local/share`

### Custom database isolation

To point OpenCode (and this migration tool) at a custom database:

```bash
# Set XDG_DATA_HOME to isolate all data
XDG_DATA_HOME="/path/to/custom-dir" opencode

# Or set OPENCODE_DB for just the database file
OPENCODE_DB="/path/to/custom.db" opencode
```

OpenCode will create and read a separate `opencode.db` inside the specified
folder's `opencode/` subdirectory.

### Using with opencode-migrate

```bash
# Auto-detects DB from your environment (same as OpenCode would use)
opencode-migrate list
opencode-migrate detect  # shows the resolved path

# Explicit env var override
OPENCODE_DB="/custom/path/opencode.db" opencode-migrate list

# XDG override
XDG_DATA_HOME="/srv/data" opencode-migrate export sess-abc -o /tmp/bundle

# Channel-based (for non-standard installations)
opencode-migrate --channel local list

# Manual override still works (takes precedence over env)
opencode-migrate list --db /explicit/path/to/opencode.db
```

### Docker / isolated environments

When running OpenCode in Docker or wanting to isolate from your host machine:

```bash
# In your Dockerfile or docker-compose.yml
ENV XDG_DATA_HOME=/app/data

# Then migrate into/out of the container
opencode-migrate push sess-abc --host docker-host --user root \
  --remote-db /app/data/opencode/opencode.db
```

## Exported data format

The bundle is a directory containing:

```
bundle/
├── session_data.json    # All DB rows (see schema below)
└── snapshots-<id>.tar.gz  # Git-based file snapshots (if any)
```

### session_data.json structure

```json
{
  "version": "1",
  "session": { ... },
  "project": { ... },
  "messages": [ ... ],
  "parts": [ ... ],
  "session_messages": [ ... ],
  "session_inputs": [ ... ],
  "todos": [ ... ],
  "context_epoch": { ... } | null
}
```

See [DATA_FORMAT.md](DATA_FORMAT.md) for full field-level documentation.

## Python API

```python
from opencode_migrate import SessionExporter, SessionImporter, SessionMigrator
from opencode_migrate import resolve_db_path, resolve_snapshot_dir

# Check resolved paths (respects OPENCODE_DB / XDG_DATA_HOME)
print(resolve_db_path())           # e.g. /root/.local/share/opencode/opencode.db
print(resolve_db_path("local"))    # e.g. /root/.local/share/opencode/opencode-local.db

# Export — auto-detects DB from environment
exporter = SessionExporter()  # uses OPENCODE_DB / XDG_DATA_HOME automatically
exporter.list_sessions()
exporter.export_session("sess-id")
exporter.export_to_bundle("sess-id", "/tmp/out")

# Or with explicit path
exporter = SessionExporter(db_path="/custom/opencode.db")

# Import
importer = SessionImporter()  # auto-detects target DB too
importer.import_from_bundle("/tmp/out", remap_directory="/new/path")

# Migrate (orchestrates export + transfer + import)
migrator = SessionMigrator()
migrator.migrate_to_remote(session_id="...", remote_host="...", remote_user="...")
migrator.migrate_from_remote(session_id="...", remote_host="...", remote_user="...")
migrator.migrate_local(session_id="...", target_db="/other/opencode.db")
```

## Requirements

- Python >= 3.10
- SSH access for remote transfers (ssh, scp in PATH)
- `opencode-migrate` must be installed on both source and target for push/pull

## Testing

```bash
python -m pytest tests/
# or directly:
python tests/test_migrate.py
```

## License

MIT
