cync-cli 0.3.0__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.
- cync/__init__.py +3 -0
- cync/auth.py +184 -0
- cync/client.py +319 -0
- cync/common.py +185 -0
- cync/config.py +108 -0
- cync/server.py +261 -0
- cync/storage.py +98 -0
- cync/supabase_store.py +199 -0
- cync_cli-0.3.0.dist-info/METADATA +176 -0
- cync_cli-0.3.0.dist-info/RECORD +13 -0
- cync_cli-0.3.0.dist-info/WHEEL +4 -0
- cync_cli-0.3.0.dist-info/entry_points.txt +3 -0
- cync_cli-0.3.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cync-cli
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Sync Claude Code conversations across machines — GitHub-authed CLI + server (Postgres metadata, R2 blobs).
|
|
5
|
+
Project-URL: Homepage, https://github.com/03hgryan/cync
|
|
6
|
+
Project-URL: Repository, https://github.com/03hgryan/cync
|
|
7
|
+
Author: 03hgryan
|
|
8
|
+
License: MIT License
|
|
9
|
+
|
|
10
|
+
Copyright (c) 2026 03hgryan
|
|
11
|
+
|
|
12
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
13
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
14
|
+
in the Software without restriction, including without limitation the rights
|
|
15
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
16
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
17
|
+
furnished to do so, subject to the following conditions:
|
|
18
|
+
|
|
19
|
+
The above copyright notice and this permission notice shall be included in all
|
|
20
|
+
copies or substantial portions of the Software.
|
|
21
|
+
|
|
22
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
23
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
24
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
25
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
26
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
27
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
28
|
+
SOFTWARE.
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Keywords: claude,claude-code,cli,conversations,sync
|
|
31
|
+
Classifier: Environment :: Console
|
|
32
|
+
Classifier: Intended Audience :: Developers
|
|
33
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
34
|
+
Classifier: Programming Language :: Python :: 3
|
|
35
|
+
Classifier: Topic :: Utilities
|
|
36
|
+
Requires-Python: >=3.11
|
|
37
|
+
Requires-Dist: httpx<1,>=0.27
|
|
38
|
+
Requires-Dist: python-dotenv<2,>=1.0
|
|
39
|
+
Requires-Dist: rich<15,>=13
|
|
40
|
+
Requires-Dist: typer<1,>=0.12
|
|
41
|
+
Provides-Extra: dev
|
|
42
|
+
Requires-Dist: boto3<2,>=1.34; extra == 'dev'
|
|
43
|
+
Requires-Dist: fastapi<1,>=0.110; extra == 'dev'
|
|
44
|
+
Requires-Dist: pydantic<3,>=2; extra == 'dev'
|
|
45
|
+
Requires-Dist: pytest<9,>=8; extra == 'dev'
|
|
46
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
47
|
+
Requires-Dist: uvicorn[standard]<1,>=0.29; extra == 'dev'
|
|
48
|
+
Provides-Extra: server
|
|
49
|
+
Requires-Dist: boto3<2,>=1.34; extra == 'server'
|
|
50
|
+
Requires-Dist: fastapi<1,>=0.110; extra == 'server'
|
|
51
|
+
Requires-Dist: pydantic<3,>=2; extra == 'server'
|
|
52
|
+
Requires-Dist: uvicorn[standard]<1,>=0.29; extra == 'server'
|
|
53
|
+
Description-Content-Type: text/markdown
|
|
54
|
+
|
|
55
|
+
# cync — code sync for Claude Code conversations
|
|
56
|
+
|
|
57
|
+
Sync your Claude Code conversations across machines, signed in with **GitHub**.
|
|
58
|
+
Your conversation **metadata** lives in your **Supabase** Postgres (per-user, RLS),
|
|
59
|
+
the **transcripts** live in your **Cloudflare R2** bucket, and a small FastAPI
|
|
60
|
+
server brokers both. Like git, but for your chats: `cync push` on one machine,
|
|
61
|
+
`cync pull` on another, and `claude --resume` picks up where you left off — plus
|
|
62
|
+
a web viewer to browse them.
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
Browser / CLI ── GitHub login (Supabase Auth) ──┐
|
|
66
|
+
▼ JWT
|
|
67
|
+
FastAPI ── Postgres (metadata, per-user RLS)
|
|
68
|
+
── R2 (transcript blobs)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## How it works
|
|
72
|
+
- Sign in once per machine with `cync login` (GitHub via Supabase, browser PKCE).
|
|
73
|
+
- A **project** is a first-class record you own; create one with `cync init <name>`
|
|
74
|
+
and link a directory to it. Conversations are keyed by their Claude session UUID.
|
|
75
|
+
- `push` uploads `~/.claude/projects/<repo>/<id>.jsonl`; `pull` writes it back on the
|
|
76
|
+
other machine and rewrites the embedded `cwd` so `claude --resume` just works.
|
|
77
|
+
- Everything is scoped to your GitHub user — projects/conversations are private to you.
|
|
78
|
+
|
|
79
|
+
## Install & use (hosted)
|
|
80
|
+
|
|
81
|
+
The CLI is on PyPI as **`cync-cli`** (the command is `cync`). No server setup —
|
|
82
|
+
it points at the hosted cync by default:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pipx install cync-cli # or: uv tool install cync-cli
|
|
86
|
+
cync login # sign in with GitHub
|
|
87
|
+
cd ~/path/to/your/project
|
|
88
|
+
cync init myproject && cync push # machine A
|
|
89
|
+
cync link myproject && cync pull # machine B → claude --resume
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Self-hosting
|
|
93
|
+
|
|
94
|
+
Prefer your own stack? Point the CLI at your deployment via `CYNC_SERVER_URL`
|
|
95
|
+
(env or `~/.config/cync/config.toml`), then deploy the server + provision your
|
|
96
|
+
own Supabase + R2:
|
|
97
|
+
|
|
98
|
+
### 1. Provision (one time)
|
|
99
|
+
- **Cloudflare R2:** create a bucket (default name `cync`) and an API token with
|
|
100
|
+
**Object Read & Write**.
|
|
101
|
+
- **Supabase:** create a project, then run [`supabase/schema.sql`](supabase/schema.sql)
|
|
102
|
+
in the SQL editor. Enable **Auth → Providers → GitHub** (create a GitHub OAuth App
|
|
103
|
+
with callback `https://<ref>.supabase.co/auth/v1/callback`). Under **Auth → URL
|
|
104
|
+
Configuration**, add redirect URLs `http://127.0.0.1:8765/callback` (CLI) and
|
|
105
|
+
`http://localhost:5173/auth/callback` (web).
|
|
106
|
+
|
|
107
|
+
### 2. Server
|
|
108
|
+
```bash
|
|
109
|
+
uv venv && source .venv/bin/activate
|
|
110
|
+
uv pip install -e ".[server]"
|
|
111
|
+
cp .env.example .env # fill in R2_* and SUPABASE_* (URL, anon, service_role)
|
|
112
|
+
cync-server # http://127.0.0.1:8787
|
|
113
|
+
```
|
|
114
|
+
> For cross-machine use, expose the server over **HTTPS** (Tailscale, Fly.io, Railway).
|
|
115
|
+
> The CLI refuses non-HTTPS server URLs except on localhost.
|
|
116
|
+
|
|
117
|
+
### 3. CLI (each machine)
|
|
118
|
+
```bash
|
|
119
|
+
uv pip install -e . # base install
|
|
120
|
+
export CYNC_SERVER_URL=http://127.0.0.1:8787 # or your https URL / config.toml
|
|
121
|
+
cync login # sign in with GitHub (opens a browser)
|
|
122
|
+
|
|
123
|
+
cd ~/path/to/your/project
|
|
124
|
+
cync init droneforge # create + link a project (machine A)
|
|
125
|
+
cync push
|
|
126
|
+
# on machine B: cync link droneforge && cync pull → claude --resume
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### 4. Web viewer (optional)
|
|
130
|
+
```bash
|
|
131
|
+
cd client
|
|
132
|
+
pnpm install
|
|
133
|
+
cp .env.example .env # PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, CYNC_SERVER_URL
|
|
134
|
+
pnpm dev # http://localhost:5173 → "Sign in with GitHub"
|
|
135
|
+
```
|
|
136
|
+
The SvelteKit server holds the token (Supabase SSR cookies); browse at `/p/<project>`.
|
|
137
|
+
|
|
138
|
+
## Commands
|
|
139
|
+
| Command | What it does |
|
|
140
|
+
|---|---|
|
|
141
|
+
| `cync login` / `logout` / `whoami` | GitHub auth session |
|
|
142
|
+
| `cync init <name>` | create a project + link this directory |
|
|
143
|
+
| `cync link <name>` | link this directory to an existing project |
|
|
144
|
+
| `cync unlink` | remove this directory's link |
|
|
145
|
+
| `cync push [id]` | upload this project's conversation(s) |
|
|
146
|
+
| `cync pull [id]` | download into `~/.claude` (overwrite/add) |
|
|
147
|
+
| `cync list` | list this project's conversations |
|
|
148
|
+
| `cync project list` / `rm <name>` | manage your projects |
|
|
149
|
+
|
|
150
|
+
## Migrating from v0.2
|
|
151
|
+
If you have v0.2 (static-token) data still in R2, after `cync login`:
|
|
152
|
+
```bash
|
|
153
|
+
python scripts/migrate_legacy.py --slug <name> --email <your-github-email> [--purge]
|
|
154
|
+
```
|
|
155
|
+
Or simply re-`cync push` from a machine that has the conversations locally.
|
|
156
|
+
|
|
157
|
+
## Security
|
|
158
|
+
- Per-user GitHub auth (Supabase JWT); every project/conversation is owner-scoped,
|
|
159
|
+
with Postgres RLS as a backstop. The server holds R2 + Supabase keys; clients never do.
|
|
160
|
+
- CLI login uses PKCE + a single-use loopback callback; the session (refresh token)
|
|
161
|
+
is stored `0600` at `~/.config/cync/session.json`.
|
|
162
|
+
- Request bodies are capped (`CYNC_MAX_BODY_BYTES`, default 64 MB).
|
|
163
|
+
|
|
164
|
+
## Layout
|
|
165
|
+
```
|
|
166
|
+
src/cync/ server (FastAPI), CLI, auth, Supabase + R2 stores
|
|
167
|
+
client/ SvelteKit web viewer (GitHub login)
|
|
168
|
+
supabase/ Postgres schema + RLS
|
|
169
|
+
scripts/ legacy migration
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Limitations
|
|
173
|
+
- Last-writer-wins; no version history yet.
|
|
174
|
+
- Syncs the `.jsonl` transcript only (enough for `--resume`); not `todos/` sidecars.
|
|
175
|
+
- Don't `push` a conversation that's open in Claude Code — quit first so it isn't mid-write.
|
|
176
|
+
- Listing reads one object per conversation from Postgres (fine for personal scale).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
cync/__init__.py,sha256=zLzi8QYdTRxMNwW8_UYz52doOLfruWhym0WJ2mTkXpo,86
|
|
2
|
+
cync/auth.py,sha256=EjmMk4YYFTrdjspc242Y32EJr_iJ-YssEtHXEpCU7h4,6208
|
|
3
|
+
cync/client.py,sha256=ZXKZgtflRa6rOCjTpq3YOO2ZJLd1XG1a3jMbZwX5EOs,10215
|
|
4
|
+
cync/common.py,sha256=X88MLWdvW8_0E-apyWWFqVSAzgOTi4s6X9hkaHudoyo,6129
|
|
5
|
+
cync/config.py,sha256=pTJTmOUuwnTooIepPF7y4DrygYdqUex4Mwem-bkWGzg,3284
|
|
6
|
+
cync/server.py,sha256=JwI9kCyAky_rGAl8SUlsIWdLUbrMDA-HC6LeZluDqzc,9155
|
|
7
|
+
cync/storage.py,sha256=PdI87L0NyWY7zxVBLPzTFVMNwpI7iLB2rdSfSR3nioI,3450
|
|
8
|
+
cync/supabase_store.py,sha256=LjmT82al5YI3sLMKmIu4G5tEqoN8MYTJKQTWJDZGPE0,7975
|
|
9
|
+
cync_cli-0.3.0.dist-info/METADATA,sha256=cmzr7k4j5RbCy5QZWsETAOmnyNutVKhR15lAnyYvnIU,7854
|
|
10
|
+
cync_cli-0.3.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
11
|
+
cync_cli-0.3.0.dist-info/entry_points.txt,sha256=BgC_BCWbrrdyQbkZUeU3O-uuhHvb90FlxVbb0Y5sLx0,72
|
|
12
|
+
cync_cli-0.3.0.dist-info/licenses/LICENSE,sha256=UpIdwVYVVw1dRHLxxn-OR_4Bctr34wj-nbFm-VzwplY,1065
|
|
13
|
+
cync_cli-0.3.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 03hgryan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|