collabmark 0.1.0__tar.gz
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.
- collabmark-0.1.0/.gitignore +33 -0
- collabmark-0.1.0/PKG-INFO +286 -0
- collabmark-0.1.0/README.md +266 -0
- collabmark-0.1.0/pyproject.toml +46 -0
- collabmark-0.1.0/src/collabmark/__init__.py +3 -0
- collabmark-0.1.0/src/collabmark/__main__.py +5 -0
- collabmark-0.1.0/src/collabmark/commands/__init__.py +1 -0
- collabmark-0.1.0/src/collabmark/commands/clean.py +106 -0
- collabmark-0.1.0/src/collabmark/commands/init.py +83 -0
- collabmark-0.1.0/src/collabmark/commands/list_syncs.py +21 -0
- collabmark-0.1.0/src/collabmark/commands/login.py +110 -0
- collabmark-0.1.0/src/collabmark/commands/logout.py +20 -0
- collabmark-0.1.0/src/collabmark/commands/logs.py +183 -0
- collabmark-0.1.0/src/collabmark/commands/start.py +345 -0
- collabmark-0.1.0/src/collabmark/commands/status.py +177 -0
- collabmark-0.1.0/src/collabmark/commands/stop.py +141 -0
- collabmark-0.1.0/src/collabmark/lib/__init__.py +1 -0
- collabmark-0.1.0/src/collabmark/lib/api.py +400 -0
- collabmark-0.1.0/src/collabmark/lib/auth.py +220 -0
- collabmark-0.1.0/src/collabmark/lib/browser_auth.py +162 -0
- collabmark-0.1.0/src/collabmark/lib/config.py +225 -0
- collabmark-0.1.0/src/collabmark/lib/crdt_sync.py +264 -0
- collabmark-0.1.0/src/collabmark/lib/daemon.py +149 -0
- collabmark-0.1.0/src/collabmark/lib/logger.py +135 -0
- collabmark-0.1.0/src/collabmark/lib/registry.py +252 -0
- collabmark-0.1.0/src/collabmark/lib/sync_engine.py +596 -0
- collabmark-0.1.0/src/collabmark/lib/watcher.py +147 -0
- collabmark-0.1.0/src/collabmark/main.py +71 -0
- collabmark-0.1.0/src/collabmark/types.py +128 -0
- collabmark-0.1.0/tests/__init__.py +0 -0
- collabmark-0.1.0/tests/conftest.py +12 -0
- collabmark-0.1.0/tests/test_api.py +586 -0
- collabmark-0.1.0/tests/test_auth.py +309 -0
- collabmark-0.1.0/tests/test_browser_auth.py +249 -0
- collabmark-0.1.0/tests/test_clean.py +116 -0
- collabmark-0.1.0/tests/test_config.py +318 -0
- collabmark-0.1.0/tests/test_crdt_sync.py +212 -0
- collabmark-0.1.0/tests/test_daemon.py +140 -0
- collabmark-0.1.0/tests/test_integration.py +369 -0
- collabmark-0.1.0/tests/test_list_syncs.py +31 -0
- collabmark-0.1.0/tests/test_logger.py +104 -0
- collabmark-0.1.0/tests/test_logs.py +108 -0
- collabmark-0.1.0/tests/test_main.py +77 -0
- collabmark-0.1.0/tests/test_registry.py +226 -0
- collabmark-0.1.0/tests/test_start.py +164 -0
- collabmark-0.1.0/tests/test_status.py +68 -0
- collabmark-0.1.0/tests/test_stop.py +76 -0
- collabmark-0.1.0/tests/test_sync_engine.py +577 -0
- collabmark-0.1.0/tests/test_watcher.py +173 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.pyc
|
|
3
|
+
*.pyo
|
|
4
|
+
.env
|
|
5
|
+
.env.local
|
|
6
|
+
.env.*.local
|
|
7
|
+
|
|
8
|
+
.venv/
|
|
9
|
+
venv/
|
|
10
|
+
node_modules/
|
|
11
|
+
frontend/dist/
|
|
12
|
+
*.egg-info/
|
|
13
|
+
.pytest_cache/
|
|
14
|
+
.mypy_cache/
|
|
15
|
+
.ruff_cache/
|
|
16
|
+
dist/
|
|
17
|
+
build/
|
|
18
|
+
|
|
19
|
+
# OS
|
|
20
|
+
.DS_Store
|
|
21
|
+
Thumbs.db
|
|
22
|
+
|
|
23
|
+
# IDE
|
|
24
|
+
.idea/
|
|
25
|
+
.vscode/
|
|
26
|
+
*.swp
|
|
27
|
+
*.swo
|
|
28
|
+
*~
|
|
29
|
+
|
|
30
|
+
# Logs
|
|
31
|
+
*.log
|
|
32
|
+
backend/.coverage
|
|
33
|
+
frontend/coverage/
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: collabmark
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Sync markdown files with CollabMark cloud
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: click>=8.1
|
|
8
|
+
Requires-Dist: httpx>=0.27
|
|
9
|
+
Requires-Dist: keyring>=25.0
|
|
10
|
+
Requires-Dist: pycrdt>=0.12
|
|
11
|
+
Requires-Dist: rich>=13.0
|
|
12
|
+
Requires-Dist: watchdog>=4.0
|
|
13
|
+
Requires-Dist: websockets>=14.0
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
17
|
+
Requires-Dist: respx>=0.22; extra == 'dev'
|
|
18
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# CollabMark CLI
|
|
22
|
+
|
|
23
|
+
Keep your local markdown files in sync with your team's CollabMark cloud
|
|
24
|
+
workspace -- bidirectionally and in real time, powered by CRDTs.
|
|
25
|
+
|
|
26
|
+
## Installation
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
pip install collabmark
|
|
30
|
+
collabmark --version
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For development:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
cd cli
|
|
37
|
+
pip install -e ".[dev]"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# Step 1: Log in (opens your browser for one-click authentication)
|
|
44
|
+
collabmark login
|
|
45
|
+
|
|
46
|
+
# Step 2: Start syncing the current directory
|
|
47
|
+
collabmark start
|
|
48
|
+
|
|
49
|
+
# That's it! Your .md files are now synced with CollabMark.
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Run `collabmark` with no arguments for a friendly getting-started guide.
|
|
53
|
+
|
|
54
|
+
## Commands
|
|
55
|
+
|
|
56
|
+
### `collabmark login`
|
|
57
|
+
|
|
58
|
+
Authenticate with CollabMark. Opens your browser for a one-click Google/SSO
|
|
59
|
+
login. Credentials are stored securely in your OS keychain via `keyring`.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
collabmark login # Browser login (recommended)
|
|
63
|
+
collabmark login --api-key <KEY> # API key fallback (headless environments)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### `collabmark logout`
|
|
67
|
+
|
|
68
|
+
Remove stored credentials from the OS keychain.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
collabmark logout
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `collabmark init`
|
|
75
|
+
|
|
76
|
+
Set up the current directory for syncing. Creates a `.collabmark/` config folder
|
|
77
|
+
and links to a cloud folder. After init, use `start` to begin syncing.
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
collabmark init # Interactive folder picker
|
|
81
|
+
collabmark init <share-link> # Join a shared folder by link
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `collabmark start`
|
|
85
|
+
|
|
86
|
+
Begin syncing markdown files. Performs an initial sync, then watches for
|
|
87
|
+
changes on both sides. Content is synced via CRDT WebSocket for real-time
|
|
88
|
+
bidirectional updates; REST is used only for metadata and authentication.
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
collabmark start # Sync current dir, choose folder interactively
|
|
92
|
+
collabmark start <share-link> # Join a shared folder by link
|
|
93
|
+
collabmark start -d # Run as a background daemon
|
|
94
|
+
collabmark start -p ~/notes # Sync a specific directory
|
|
95
|
+
collabmark start --interval 30 # Poll every 30 seconds (default: 10s)
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `collabmark status`
|
|
99
|
+
|
|
100
|
+
Show the current sync state. When run from a project directory, shows detailed
|
|
101
|
+
project info. When run without a project, shows a global overview of all syncs.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
collabmark status # Project-specific or global view
|
|
105
|
+
collabmark status -p ~/notes # Check a specific project
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### `collabmark list`
|
|
109
|
+
|
|
110
|
+
List all registered syncs across all projects with status, PID, last sync
|
|
111
|
+
time, and cloud folder info.
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
collabmark list
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### `collabmark stop`
|
|
118
|
+
|
|
119
|
+
Gracefully stop sync processes. Supports interactive selection when multiple
|
|
120
|
+
syncs are running.
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
collabmark stop # Stop current project or choose interactively
|
|
124
|
+
collabmark stop --all # Stop all running syncs
|
|
125
|
+
collabmark stop --path ~/notes # Stop a specific project's sync
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### `collabmark logs`
|
|
129
|
+
|
|
130
|
+
View structured sync log output. Each sync project has its own log file.
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
collabmark logs # Current project logs (last 50 entries)
|
|
134
|
+
collabmark logs -n 100 # Last 100 entries
|
|
135
|
+
collabmark logs -f # Follow in real time (like tail -f)
|
|
136
|
+
collabmark logs --folder <id> # Logs for a specific cloud folder
|
|
137
|
+
collabmark logs --all-syncs # Interleaved logs from all syncs
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### `collabmark clean`
|
|
141
|
+
|
|
142
|
+
Remove stale entries from the sync registry (stopped syncs, missing directories).
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
collabmark clean # Interactive selection of stale entries
|
|
146
|
+
collabmark clean --all # Remove all stopped entries
|
|
147
|
+
collabmark clean --force # Remove all entries (including running)
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## How Sync Works
|
|
151
|
+
|
|
152
|
+
1. **Local scan** -- finds all `.md` files recursively (ignoring `.collabmark/`).
|
|
153
|
+
2. **Cloud scan** -- fetches the full folder tree from CollabMark in one API call.
|
|
154
|
+
3. **Three-way reconciliation** -- compares local files, the last-known sync state
|
|
155
|
+
(`.collabmark/sync.json`), and cloud documents to determine what changed where.
|
|
156
|
+
4. **CRDT sync** -- content is pushed/pulled via WebSocket using pycrdt (the same
|
|
157
|
+
CRDT library the server uses), so edits merge correctly with concurrent web users.
|
|
158
|
+
5. **Continuous watch** -- uses OS filesystem events (debounced) plus periodic
|
|
159
|
+
cloud polling to keep both sides in sync.
|
|
160
|
+
|
|
161
|
+
### Conflict Resolution
|
|
162
|
+
|
|
163
|
+
When the same file changes both locally and on the cloud between sync cycles,
|
|
164
|
+
it is flagged as a **conflict**. Neither side is overwritten. The conflict is
|
|
165
|
+
logged and shown in `collabmark logs`.
|
|
166
|
+
|
|
167
|
+
## Monitoring & Observability
|
|
168
|
+
|
|
169
|
+
CollabMark CLI uses a centralized registry at `~/.collabmark/registry.json`
|
|
170
|
+
to track all active and stopped syncs. This enables:
|
|
171
|
+
|
|
172
|
+
- **Global visibility**: `collabmark list` shows all syncs at a glance
|
|
173
|
+
- **Per-project logs**: each sync writes to `~/.collabmark/logs/{folder_id}.log`
|
|
174
|
+
- **Heartbeat tracking**: last sync time, action count, and error status
|
|
175
|
+
- **Dead process detection**: `collabmark status` prunes crashed processes
|
|
176
|
+
- **Stale cleanup**: `collabmark clean` removes orphaned registry entries
|
|
177
|
+
|
|
178
|
+
## Configuration
|
|
179
|
+
|
|
180
|
+
All per-project configuration lives in `.collabmark/` at the root of your
|
|
181
|
+
synced directory:
|
|
182
|
+
|
|
183
|
+
| File | Purpose |
|
|
184
|
+
|-------------------|----------------------------------------------|
|
|
185
|
+
| `config.json` | Linked cloud folder, server URL, user info |
|
|
186
|
+
| `sync.json` | Per-file sync state (hashes, doc IDs) |
|
|
187
|
+
| `trash/` | Files removed by cloud-side deletions |
|
|
188
|
+
|
|
189
|
+
Global state is stored under `~/.collabmark/`:
|
|
190
|
+
|
|
191
|
+
| Path | Purpose |
|
|
192
|
+
|-----------------------------------|------------------------------------------|
|
|
193
|
+
| `~/.collabmark/registry.json` | Centralized sync registry (all projects) |
|
|
194
|
+
| `~/.collabmark/logs/{id}.log` | Per-project structured JSON logs |
|
|
195
|
+
| `~/.collabmark/pids/{id}.pid` | Per-project PID files (daemon mode) |
|
|
196
|
+
| `~/.collabmark/credentials.json` | Non-sensitive login metadata |
|
|
197
|
+
| OS keychain | API key (encrypted, via `keyring`) |
|
|
198
|
+
|
|
199
|
+
### Environment Variables
|
|
200
|
+
|
|
201
|
+
| Variable | Purpose | Default |
|
|
202
|
+
|---------------------------|------------------------------------|--------------------------------------------------|
|
|
203
|
+
| `COLLABMARK_API_URL` | Override API server URL | `https://web-production-5e1bc.up.railway.app` |
|
|
204
|
+
| `COLLABMARK_FRONTEND_URL` | Override frontend URL | `https://web-production-5e1bc.up.railway.app` |
|
|
205
|
+
| `COLLABMARK_HOME` | Override global config directory | `~/.collabmark` |
|
|
206
|
+
|
|
207
|
+
## Development
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
cd cli
|
|
211
|
+
|
|
212
|
+
# Install with dev dependencies
|
|
213
|
+
pip install -e ".[dev]"
|
|
214
|
+
|
|
215
|
+
# Run all tests (313 tests)
|
|
216
|
+
python -m pytest -v
|
|
217
|
+
|
|
218
|
+
# Run tests with coverage
|
|
219
|
+
pytest --cov=collabmark
|
|
220
|
+
|
|
221
|
+
# Lint and format
|
|
222
|
+
ruff check src/ tests/
|
|
223
|
+
ruff format src/ tests/
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Releasing to PyPI
|
|
227
|
+
|
|
228
|
+
Releases are automated via GitHub Actions. To publish a new version:
|
|
229
|
+
|
|
230
|
+
1. Update the version in `cli/src/collabmark/__init__.py` and `cli/pyproject.toml`
|
|
231
|
+
2. Commit and push to `main`
|
|
232
|
+
3. Create and push a tag: `git tag cli-v0.1.0 && git push origin cli-v0.1.0`
|
|
233
|
+
4. The `cli-release.yml` workflow runs lint, tests, builds, and publishes to PyPI
|
|
234
|
+
|
|
235
|
+
## Project Structure
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
cli/
|
|
239
|
+
src/collabmark/
|
|
240
|
+
__init__.py Package version
|
|
241
|
+
__main__.py python -m collabmark entry point
|
|
242
|
+
main.py Root CLI group, welcome banner
|
|
243
|
+
types.py Shared dataclasses (SyncConfig, DocumentInfo, etc.)
|
|
244
|
+
commands/
|
|
245
|
+
init.py collabmark init
|
|
246
|
+
login.py collabmark login
|
|
247
|
+
logout.py collabmark logout
|
|
248
|
+
start.py collabmark start (with registry + heartbeat)
|
|
249
|
+
status.py collabmark status (global + project views)
|
|
250
|
+
stop.py collabmark stop (interactive + --all + --path)
|
|
251
|
+
logs.py collabmark logs (per-project + --all-syncs)
|
|
252
|
+
list_syncs.py collabmark list (global sync overview)
|
|
253
|
+
clean.py collabmark clean (stale entry removal)
|
|
254
|
+
lib/
|
|
255
|
+
api.py Async REST client with retry/backoff
|
|
256
|
+
auth.py Keychain credential management
|
|
257
|
+
browser_auth.py Browser-based OAuth flow
|
|
258
|
+
config.py .collabmark/ config and state management
|
|
259
|
+
crdt_sync.py CRDT WebSocket sync (pycrdt)
|
|
260
|
+
daemon.py Per-project PID file and process management
|
|
261
|
+
logger.py Per-project structured JSON logging
|
|
262
|
+
registry.py Centralized sync registry (~/.collabmark/registry.json)
|
|
263
|
+
sync_engine.py Three-way reconciliation and sync actions
|
|
264
|
+
watcher.py Debounced filesystem watcher
|
|
265
|
+
tests/ 313 tests
|
|
266
|
+
conftest.py Shared fixtures
|
|
267
|
+
test_api.py API client tests
|
|
268
|
+
test_auth.py Auth and keychain tests
|
|
269
|
+
test_browser_auth.py Browser OAuth flow tests
|
|
270
|
+
test_clean.py Clean command tests
|
|
271
|
+
test_config.py Config/state management tests
|
|
272
|
+
test_crdt_sync.py CRDT sync tests
|
|
273
|
+
test_daemon.py Per-project daemon PID tests
|
|
274
|
+
test_integration.py End-to-end sync flow tests
|
|
275
|
+
test_list_syncs.py List command tests
|
|
276
|
+
test_logger.py Logging tests
|
|
277
|
+
test_logs.py Logs command tests
|
|
278
|
+
test_main.py CLI entry point tests
|
|
279
|
+
test_registry.py Sync registry tests
|
|
280
|
+
test_start.py Start command tests
|
|
281
|
+
test_status.py Status command tests
|
|
282
|
+
test_stop.py Stop command tests
|
|
283
|
+
test_sync_engine.py Reconciliation logic tests
|
|
284
|
+
test_watcher.py File watcher tests
|
|
285
|
+
pyproject.toml hatchling build config + CLI entry point
|
|
286
|
+
```
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# CollabMark CLI
|
|
2
|
+
|
|
3
|
+
Keep your local markdown files in sync with your team's CollabMark cloud
|
|
4
|
+
workspace -- bidirectionally and in real time, powered by CRDTs.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
pip install collabmark
|
|
10
|
+
collabmark --version
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For development:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cd cli
|
|
17
|
+
pip install -e ".[dev]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Step 1: Log in (opens your browser for one-click authentication)
|
|
24
|
+
collabmark login
|
|
25
|
+
|
|
26
|
+
# Step 2: Start syncing the current directory
|
|
27
|
+
collabmark start
|
|
28
|
+
|
|
29
|
+
# That's it! Your .md files are now synced with CollabMark.
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Run `collabmark` with no arguments for a friendly getting-started guide.
|
|
33
|
+
|
|
34
|
+
## Commands
|
|
35
|
+
|
|
36
|
+
### `collabmark login`
|
|
37
|
+
|
|
38
|
+
Authenticate with CollabMark. Opens your browser for a one-click Google/SSO
|
|
39
|
+
login. Credentials are stored securely in your OS keychain via `keyring`.
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
collabmark login # Browser login (recommended)
|
|
43
|
+
collabmark login --api-key <KEY> # API key fallback (headless environments)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### `collabmark logout`
|
|
47
|
+
|
|
48
|
+
Remove stored credentials from the OS keychain.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
collabmark logout
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### `collabmark init`
|
|
55
|
+
|
|
56
|
+
Set up the current directory for syncing. Creates a `.collabmark/` config folder
|
|
57
|
+
and links to a cloud folder. After init, use `start` to begin syncing.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
collabmark init # Interactive folder picker
|
|
61
|
+
collabmark init <share-link> # Join a shared folder by link
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### `collabmark start`
|
|
65
|
+
|
|
66
|
+
Begin syncing markdown files. Performs an initial sync, then watches for
|
|
67
|
+
changes on both sides. Content is synced via CRDT WebSocket for real-time
|
|
68
|
+
bidirectional updates; REST is used only for metadata and authentication.
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
collabmark start # Sync current dir, choose folder interactively
|
|
72
|
+
collabmark start <share-link> # Join a shared folder by link
|
|
73
|
+
collabmark start -d # Run as a background daemon
|
|
74
|
+
collabmark start -p ~/notes # Sync a specific directory
|
|
75
|
+
collabmark start --interval 30 # Poll every 30 seconds (default: 10s)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### `collabmark status`
|
|
79
|
+
|
|
80
|
+
Show the current sync state. When run from a project directory, shows detailed
|
|
81
|
+
project info. When run without a project, shows a global overview of all syncs.
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
collabmark status # Project-specific or global view
|
|
85
|
+
collabmark status -p ~/notes # Check a specific project
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### `collabmark list`
|
|
89
|
+
|
|
90
|
+
List all registered syncs across all projects with status, PID, last sync
|
|
91
|
+
time, and cloud folder info.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
collabmark list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### `collabmark stop`
|
|
98
|
+
|
|
99
|
+
Gracefully stop sync processes. Supports interactive selection when multiple
|
|
100
|
+
syncs are running.
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
collabmark stop # Stop current project or choose interactively
|
|
104
|
+
collabmark stop --all # Stop all running syncs
|
|
105
|
+
collabmark stop --path ~/notes # Stop a specific project's sync
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### `collabmark logs`
|
|
109
|
+
|
|
110
|
+
View structured sync log output. Each sync project has its own log file.
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
collabmark logs # Current project logs (last 50 entries)
|
|
114
|
+
collabmark logs -n 100 # Last 100 entries
|
|
115
|
+
collabmark logs -f # Follow in real time (like tail -f)
|
|
116
|
+
collabmark logs --folder <id> # Logs for a specific cloud folder
|
|
117
|
+
collabmark logs --all-syncs # Interleaved logs from all syncs
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### `collabmark clean`
|
|
121
|
+
|
|
122
|
+
Remove stale entries from the sync registry (stopped syncs, missing directories).
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
collabmark clean # Interactive selection of stale entries
|
|
126
|
+
collabmark clean --all # Remove all stopped entries
|
|
127
|
+
collabmark clean --force # Remove all entries (including running)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## How Sync Works
|
|
131
|
+
|
|
132
|
+
1. **Local scan** -- finds all `.md` files recursively (ignoring `.collabmark/`).
|
|
133
|
+
2. **Cloud scan** -- fetches the full folder tree from CollabMark in one API call.
|
|
134
|
+
3. **Three-way reconciliation** -- compares local files, the last-known sync state
|
|
135
|
+
(`.collabmark/sync.json`), and cloud documents to determine what changed where.
|
|
136
|
+
4. **CRDT sync** -- content is pushed/pulled via WebSocket using pycrdt (the same
|
|
137
|
+
CRDT library the server uses), so edits merge correctly with concurrent web users.
|
|
138
|
+
5. **Continuous watch** -- uses OS filesystem events (debounced) plus periodic
|
|
139
|
+
cloud polling to keep both sides in sync.
|
|
140
|
+
|
|
141
|
+
### Conflict Resolution
|
|
142
|
+
|
|
143
|
+
When the same file changes both locally and on the cloud between sync cycles,
|
|
144
|
+
it is flagged as a **conflict**. Neither side is overwritten. The conflict is
|
|
145
|
+
logged and shown in `collabmark logs`.
|
|
146
|
+
|
|
147
|
+
## Monitoring & Observability
|
|
148
|
+
|
|
149
|
+
CollabMark CLI uses a centralized registry at `~/.collabmark/registry.json`
|
|
150
|
+
to track all active and stopped syncs. This enables:
|
|
151
|
+
|
|
152
|
+
- **Global visibility**: `collabmark list` shows all syncs at a glance
|
|
153
|
+
- **Per-project logs**: each sync writes to `~/.collabmark/logs/{folder_id}.log`
|
|
154
|
+
- **Heartbeat tracking**: last sync time, action count, and error status
|
|
155
|
+
- **Dead process detection**: `collabmark status` prunes crashed processes
|
|
156
|
+
- **Stale cleanup**: `collabmark clean` removes orphaned registry entries
|
|
157
|
+
|
|
158
|
+
## Configuration
|
|
159
|
+
|
|
160
|
+
All per-project configuration lives in `.collabmark/` at the root of your
|
|
161
|
+
synced directory:
|
|
162
|
+
|
|
163
|
+
| File | Purpose |
|
|
164
|
+
|-------------------|----------------------------------------------|
|
|
165
|
+
| `config.json` | Linked cloud folder, server URL, user info |
|
|
166
|
+
| `sync.json` | Per-file sync state (hashes, doc IDs) |
|
|
167
|
+
| `trash/` | Files removed by cloud-side deletions |
|
|
168
|
+
|
|
169
|
+
Global state is stored under `~/.collabmark/`:
|
|
170
|
+
|
|
171
|
+
| Path | Purpose |
|
|
172
|
+
|-----------------------------------|------------------------------------------|
|
|
173
|
+
| `~/.collabmark/registry.json` | Centralized sync registry (all projects) |
|
|
174
|
+
| `~/.collabmark/logs/{id}.log` | Per-project structured JSON logs |
|
|
175
|
+
| `~/.collabmark/pids/{id}.pid` | Per-project PID files (daemon mode) |
|
|
176
|
+
| `~/.collabmark/credentials.json` | Non-sensitive login metadata |
|
|
177
|
+
| OS keychain | API key (encrypted, via `keyring`) |
|
|
178
|
+
|
|
179
|
+
### Environment Variables
|
|
180
|
+
|
|
181
|
+
| Variable | Purpose | Default |
|
|
182
|
+
|---------------------------|------------------------------------|--------------------------------------------------|
|
|
183
|
+
| `COLLABMARK_API_URL` | Override API server URL | `https://web-production-5e1bc.up.railway.app` |
|
|
184
|
+
| `COLLABMARK_FRONTEND_URL` | Override frontend URL | `https://web-production-5e1bc.up.railway.app` |
|
|
185
|
+
| `COLLABMARK_HOME` | Override global config directory | `~/.collabmark` |
|
|
186
|
+
|
|
187
|
+
## Development
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
cd cli
|
|
191
|
+
|
|
192
|
+
# Install with dev dependencies
|
|
193
|
+
pip install -e ".[dev]"
|
|
194
|
+
|
|
195
|
+
# Run all tests (313 tests)
|
|
196
|
+
python -m pytest -v
|
|
197
|
+
|
|
198
|
+
# Run tests with coverage
|
|
199
|
+
pytest --cov=collabmark
|
|
200
|
+
|
|
201
|
+
# Lint and format
|
|
202
|
+
ruff check src/ tests/
|
|
203
|
+
ruff format src/ tests/
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
## Releasing to PyPI
|
|
207
|
+
|
|
208
|
+
Releases are automated via GitHub Actions. To publish a new version:
|
|
209
|
+
|
|
210
|
+
1. Update the version in `cli/src/collabmark/__init__.py` and `cli/pyproject.toml`
|
|
211
|
+
2. Commit and push to `main`
|
|
212
|
+
3. Create and push a tag: `git tag cli-v0.1.0 && git push origin cli-v0.1.0`
|
|
213
|
+
4. The `cli-release.yml` workflow runs lint, tests, builds, and publishes to PyPI
|
|
214
|
+
|
|
215
|
+
## Project Structure
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
cli/
|
|
219
|
+
src/collabmark/
|
|
220
|
+
__init__.py Package version
|
|
221
|
+
__main__.py python -m collabmark entry point
|
|
222
|
+
main.py Root CLI group, welcome banner
|
|
223
|
+
types.py Shared dataclasses (SyncConfig, DocumentInfo, etc.)
|
|
224
|
+
commands/
|
|
225
|
+
init.py collabmark init
|
|
226
|
+
login.py collabmark login
|
|
227
|
+
logout.py collabmark logout
|
|
228
|
+
start.py collabmark start (with registry + heartbeat)
|
|
229
|
+
status.py collabmark status (global + project views)
|
|
230
|
+
stop.py collabmark stop (interactive + --all + --path)
|
|
231
|
+
logs.py collabmark logs (per-project + --all-syncs)
|
|
232
|
+
list_syncs.py collabmark list (global sync overview)
|
|
233
|
+
clean.py collabmark clean (stale entry removal)
|
|
234
|
+
lib/
|
|
235
|
+
api.py Async REST client with retry/backoff
|
|
236
|
+
auth.py Keychain credential management
|
|
237
|
+
browser_auth.py Browser-based OAuth flow
|
|
238
|
+
config.py .collabmark/ config and state management
|
|
239
|
+
crdt_sync.py CRDT WebSocket sync (pycrdt)
|
|
240
|
+
daemon.py Per-project PID file and process management
|
|
241
|
+
logger.py Per-project structured JSON logging
|
|
242
|
+
registry.py Centralized sync registry (~/.collabmark/registry.json)
|
|
243
|
+
sync_engine.py Three-way reconciliation and sync actions
|
|
244
|
+
watcher.py Debounced filesystem watcher
|
|
245
|
+
tests/ 313 tests
|
|
246
|
+
conftest.py Shared fixtures
|
|
247
|
+
test_api.py API client tests
|
|
248
|
+
test_auth.py Auth and keychain tests
|
|
249
|
+
test_browser_auth.py Browser OAuth flow tests
|
|
250
|
+
test_clean.py Clean command tests
|
|
251
|
+
test_config.py Config/state management tests
|
|
252
|
+
test_crdt_sync.py CRDT sync tests
|
|
253
|
+
test_daemon.py Per-project daemon PID tests
|
|
254
|
+
test_integration.py End-to-end sync flow tests
|
|
255
|
+
test_list_syncs.py List command tests
|
|
256
|
+
test_logger.py Logging tests
|
|
257
|
+
test_logs.py Logs command tests
|
|
258
|
+
test_main.py CLI entry point tests
|
|
259
|
+
test_registry.py Sync registry tests
|
|
260
|
+
test_start.py Start command tests
|
|
261
|
+
test_status.py Status command tests
|
|
262
|
+
test_stop.py Stop command tests
|
|
263
|
+
test_sync_engine.py Reconciliation logic tests
|
|
264
|
+
test_watcher.py File watcher tests
|
|
265
|
+
pyproject.toml hatchling build config + CLI entry point
|
|
266
|
+
```
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "collabmark"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Sync markdown files with CollabMark cloud"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
license = "MIT"
|
|
12
|
+
|
|
13
|
+
dependencies = [
|
|
14
|
+
"click>=8.1",
|
|
15
|
+
"rich>=13.0",
|
|
16
|
+
"httpx>=0.27",
|
|
17
|
+
"watchdog>=4.0",
|
|
18
|
+
"keyring>=25.0",
|
|
19
|
+
"pycrdt>=0.12",
|
|
20
|
+
"websockets>=14.0",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
[project.optional-dependencies]
|
|
24
|
+
dev = [
|
|
25
|
+
"pytest>=8.0",
|
|
26
|
+
"pytest-asyncio>=0.24",
|
|
27
|
+
"respx>=0.22",
|
|
28
|
+
"ruff>=0.8",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
collabmark = "collabmark.main:cli"
|
|
33
|
+
|
|
34
|
+
[tool.hatch.build.targets.wheel]
|
|
35
|
+
packages = ["src/collabmark"]
|
|
36
|
+
|
|
37
|
+
[tool.pytest.ini_options]
|
|
38
|
+
testpaths = ["tests"]
|
|
39
|
+
asyncio_mode = "auto"
|
|
40
|
+
|
|
41
|
+
[tool.ruff]
|
|
42
|
+
target-version = "py312"
|
|
43
|
+
line-length = 120
|
|
44
|
+
|
|
45
|
+
[tool.ruff.lint]
|
|
46
|
+
select = ["E", "F", "I", "W", "RUF"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""CLI command implementations."""
|