feat: v0.2.0 — Joplin import, ChatGPT Projects, --project filter

Core features:
- Add `joplin` command: syncs exported Markdown to Joplin via local REST API
- Notebooks auto-created per provider+project (e.g. "ChatGPT - My Project")
- Idempotent: notes updated (not duplicated) on re-run; note ID tracked in manifest
- Add `--project` filter to `export` and `list` commands (substring or 'none')
- Add ChatGPT Projects support via CHATGPT_PROJECT_IDS env var

Config:
- Add JOPLIN_API_TOKEN, JOPLIN_API_URL, JOPLIN_REQUEST_TIMEOUT
- Version now read from importlib.metadata (single source of truth: pyproject.toml)
- Bump version to 0.2.0

Quality:
- Explicit Timeout handling in JoplinClient with actionable error messages
- token validation (validate_token) separate from connectivity (ping)
- Remove debug_auth.py, debug_claude.py, and untracked .har file
- Add *.har to .gitignore (may contain auth cookies/session tokens)
- Update README, CHANGELOG, FUTURE.md to reflect v0.2.0

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-01 06:04:03 -05:00
co-authored by Claude Sonnet 4.6
parent 23d7c17255
commit 304cf4fde4
16 changed files with 1795 additions and 133 deletions
+17 -2
View File
@@ -11,6 +11,21 @@ import requests
from src.utils import redact_secrets
# curl_cffi has its own exception hierarchy (rooted at CurlError → OSError),
# completely separate from requests.exceptions. Import them so _make_request
# can catch both when a curl_cffi session is in use.
try:
from curl_cffi.requests.exceptions import (
HTTPError as _CurlHTTPError,
ConnectionError as _CurlConnectionError,
Timeout as _CurlTimeout,
)
except ImportError:
# Fall back to requests types — catching them twice is harmless.
_CurlHTTPError = requests.HTTPError # type: ignore[misc,assignment]
_CurlConnectionError = requests.ConnectionError # type: ignore[misc,assignment]
_CurlTimeout = requests.Timeout # type: ignore[misc,assignment]
logger = logging.getLogger(__name__)
# Request timeouts (connect, read) in seconds
@@ -271,7 +286,7 @@ class BaseProvider(ABC):
except ProviderError:
raise
except (requests.ConnectionError, requests.Timeout) as e:
except (requests.ConnectionError, requests.Timeout, _CurlConnectionError, _CurlTimeout) as e:
last_exc = e
if attempt > MAX_RETRIES:
raise ProviderError(
@@ -293,7 +308,7 @@ class BaseProvider(ABC):
)
time.sleep(wait)
except requests.HTTPError as e:
except (requests.HTTPError, _CurlHTTPError) as e:
raise ProviderError(
self.provider_name, f"{method} {url}", e
) from e