feat: v0.6.0 — collapse policy, session limiter, Claude Code provider, prune, browser auth, media downloads
This commit is contained in:
+75
-1
@@ -20,6 +20,12 @@ _CLAUDE_PLACEHOLDER = ""
|
||||
# Valid OUTPUT_STRUCTURE values
|
||||
VALID_STRUCTURES = {"provider/project/year", "provider/project", "provider/year"}
|
||||
|
||||
# Valid EXPORTER_HIDDEN_CONTENT values (see src/providers/base.py)
|
||||
VALID_HIDDEN_CONTENT = {"full", "placeholder", "omit"}
|
||||
|
||||
# Valid EXPORTER_DOWNLOAD_MEDIA values (see src/media.py)
|
||||
VALID_DOWNLOAD_MEDIA = {"images", "all", "off"}
|
||||
|
||||
|
||||
class ConfigError(Exception):
|
||||
"""Raised when required configuration is missing or invalid."""
|
||||
@@ -43,6 +49,16 @@ class Config:
|
||||
# Joplin local REST API settings (Web Clipper service)
|
||||
joplin_api_token: str | None = None
|
||||
joplin_api_url: str = "http://localhost:41184"
|
||||
# Policy for content invisible in the provider web UI (retrieval dumps,
|
||||
# hidden context): full | placeholder | omit
|
||||
hidden_content: str = "placeholder"
|
||||
# Session cap: max conversations downloaded per export run (None = unlimited).
|
||||
# Every run is resumable, so a capped run just continues next time.
|
||||
max_conversations: int | None = None
|
||||
# Seconds between consecutive API requests (politeness pacing; 0 disables)
|
||||
request_delay: float = 1.0
|
||||
# Asset download policy: images | all | off
|
||||
download_media: str = "images"
|
||||
|
||||
|
||||
def load_config() -> Config:
|
||||
@@ -67,6 +83,12 @@ def load_config() -> Config:
|
||||
joplin_token = os.getenv("JOPLIN_API_TOKEN", "").strip() or None
|
||||
joplin_url = os.getenv("JOPLIN_API_URL", "http://localhost:41184").strip()
|
||||
|
||||
hidden_content = os.getenv("EXPORTER_HIDDEN_CONTENT", "").strip().lower() or "placeholder"
|
||||
|
||||
max_conversations_raw = os.getenv("MAX_CONVERSATIONS_PER_RUN", "").strip()
|
||||
request_delay_raw = os.getenv("REQUEST_DELAY", "").strip()
|
||||
download_media = os.getenv("EXPORTER_DOWNLOAD_MEDIA", "").strip().lower() or "images"
|
||||
|
||||
# Parse CHATGPT_PROJECT_IDS — comma-separated list of gizmo IDs (g-p-xxx)
|
||||
_project_ids_raw = os.getenv("CHATGPT_PROJECT_IDS", "").strip()
|
||||
chatgpt_project_ids = [
|
||||
@@ -90,6 +112,46 @@ def load_config() -> Config:
|
||||
f"Must be one of: {', '.join(sorted(VALID_STRUCTURES))}"
|
||||
)
|
||||
|
||||
# Validate hidden-content policy
|
||||
if hidden_content not in VALID_HIDDEN_CONTENT:
|
||||
errors.append(
|
||||
f"EXPORTER_HIDDEN_CONTENT '{hidden_content}' is invalid. "
|
||||
f"Must be one of: {', '.join(sorted(VALID_HIDDEN_CONTENT))}"
|
||||
)
|
||||
|
||||
# Validate media download policy
|
||||
if download_media not in VALID_DOWNLOAD_MEDIA:
|
||||
errors.append(
|
||||
f"EXPORTER_DOWNLOAD_MEDIA '{download_media}' is invalid. "
|
||||
f"Must be one of: {', '.join(sorted(VALID_DOWNLOAD_MEDIA))}"
|
||||
)
|
||||
|
||||
# Validate session cap
|
||||
max_conversations: int | None = None
|
||||
if max_conversations_raw:
|
||||
try:
|
||||
max_conversations = int(max_conversations_raw)
|
||||
except ValueError:
|
||||
errors.append(
|
||||
f"MAX_CONVERSATIONS_PER_RUN '{max_conversations_raw}' is not an integer."
|
||||
)
|
||||
else:
|
||||
if max_conversations < 1:
|
||||
errors.append(
|
||||
f"MAX_CONVERSATIONS_PER_RUN must be at least 1 (got {max_conversations})."
|
||||
)
|
||||
|
||||
# Validate request pacing
|
||||
request_delay = 1.0
|
||||
if request_delay_raw:
|
||||
try:
|
||||
request_delay = float(request_delay_raw)
|
||||
except ValueError:
|
||||
errors.append(f"REQUEST_DELAY '{request_delay_raw}' is not a number.")
|
||||
else:
|
||||
if request_delay < 0:
|
||||
errors.append(f"REQUEST_DELAY must be >= 0 (got {request_delay}).")
|
||||
|
||||
# Validate and decode ChatGPT JWT
|
||||
chatgpt_expiry: datetime | None = None
|
||||
if chatgpt_token:
|
||||
@@ -139,6 +201,10 @@ def load_config() -> Config:
|
||||
chatgpt_project_ids=chatgpt_project_ids,
|
||||
joplin_api_token=joplin_token,
|
||||
joplin_api_url=joplin_url,
|
||||
hidden_content=hidden_content,
|
||||
max_conversations=max_conversations,
|
||||
request_delay=request_delay,
|
||||
download_media=download_media,
|
||||
)
|
||||
|
||||
_log_startup_summary(config)
|
||||
@@ -223,7 +289,11 @@ def _log_startup_summary(cfg: Config) -> None:
|
||||
"Joplin: %s | "
|
||||
"export_dir=%s | "
|
||||
"structure=%s | "
|
||||
"cache_dir=%s",
|
||||
"cache_dir=%s | "
|
||||
"hidden_content=%s | "
|
||||
"max_conversations=%s | "
|
||||
"request_delay=%.1fs | "
|
||||
"download_media=%s",
|
||||
chatgpt_status,
|
||||
claude_status,
|
||||
len(cfg.chatgpt_project_ids),
|
||||
@@ -231,4 +301,8 @@ def _log_startup_summary(cfg: Config) -> None:
|
||||
cfg.export_dir,
|
||||
cfg.output_structure,
|
||||
cfg.cache_dir,
|
||||
cfg.hidden_content,
|
||||
cfg.max_conversations if cfg.max_conversations is not None else "unlimited",
|
||||
cfg.request_delay,
|
||||
cfg.download_media,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user