droid-sync
v0.1.0
Published
Sync Factory Droid sessions to OpenSync dashboard
Maintainers
Readme
droid-sync
Sync your Factory Droid coding sessions to OpenSync in real-time.
Installation
npm install -g droid-syncSetup
# Configure credentials and register hooks
droid-sync login
# Verify connection
droid-sync statusCore Architecture
Factory Droid → hooks (stdin JSON) → CLI parses → SyncClient → Convex BackendSource Files
| File | Purpose |
| ------------------- | ---------------------------------------------------------------------- |
| src/cli.ts | CLI entry point: login, logout, status, verify, hook <event> |
| src/hooks.ts | Event handlers (primary: Stop) |
| src/api.ts | SyncClient class - HTTP requests to Convex backend |
| src/config.ts | Configuration loading/saving |
| src/transcript.ts | JSONL transcript parsing, incremental message extraction |
| src/types.ts | TypeScript type definitions |
Event Flow
The plugin uses the Stop event for all syncing:
- Stop → Creates/updates session with metadata (project, git branch, model, tokens, duration), parses transcript, syncs new messages
- SessionEnd → Clears local sync state (synced message ID cache)
How Hooks Work
When Factory Droid triggers a hook, it:
- Invokes
droid-sync hook <EventName>as a subprocess - Pipes JSON context to stdin (sessionId, transcriptPath, cwd, etc.)
- The CLI reads stdin, parses JSON, dispatches to the appropriate handler
Example hook registration in ~/.factory/settings.json:
{
"hooks": {
"Stop": [
{ "hooks": [{ "type": "command", "command": "droid-sync hook Stop" }] }
]
}
}Note: Only the
Stophook is required.SessionEndonly clears local cache.
Data Transformation
The plugin transforms Factory Droid's internal schema to the backend's expected format:
Plugin receives → Backend expects
───────────────────────────────────────────────────
sessionId → externalId
messageId → externalId
content → textContent
tool_use blocks → parts[{ type: "tool_use", content: {...} }]
assistantActiveTimeMs → durationMs
tokenUsage.inputTokens → tokenUsage.input
tokenUsage.outputTokens → tokenUsage.outputThis mapping happens in transformSession() and transformMessage() methods in src/api.ts.
Incremental Sync
Messages are synced incrementally to avoid duplicates:
- On each
Stopevent, parse the transcript JSONL file - Load previously synced message IDs from state file
- Extract only new messages not in the synced set
- Sync new messages via batch API
- Save updated message IDs to state file
State files: ~/.config/droid-sync/state/{sessionId}.json
Config Storage
| Location | Purpose |
| ---------------------------------- | ------------------------------------------------ |
| ~/.config/droid-sync/config.json | Primary config (convexUrl, apiKey, sync options) |
| ~/.factory/settings.json | Hook registrations |
| ~/.config/droid-sync/state/ | Per-session sync state (synced message IDs) |
| Environment variables | Override config (DROID_SYNC_*) |
Commands
droid-sync login # Configure credentials and register hooks
droid-sync logout # Clear credentials
droid-sync status # Show connection status
droid-sync verify # Test connectivity
droid-sync config # Show configuration
droid-sync version # Show versionConfiguration
Config file: ~/.config/droid-sync/config.json
{
"convexUrl": "https://your-project.convex.cloud",
"apiKey": "osk_your_api_key",
"autoSync": true,
"syncToolCalls": true,
"syncThinking": false
}License
MIT
