better-notion 0.9.6__tar.gz → 0.9.7__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.
- {better_notion-0.9.6 → better_notion-0.9.7}/PKG-INFO +2 -1
- better_notion-0.9.7/better_notion/_cli/commands/auth.py +136 -0
- better_notion-0.9.7/better_notion/_cli/commands/config.py +178 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/plugins.py +140 -92
- better_notion-0.9.7/better_notion/_cli/display.py +293 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/pyproject.toml +4 -1
- better_notion-0.9.7/tests/cli/test_display.py +253 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/plugins/test_plugin_commands_state.py +30 -25
- better_notion-0.9.6/better_notion/_cli/commands/auth.py +0 -107
- better_notion-0.9.6/better_notion/_cli/commands/config.py +0 -131
- {better_notion-0.9.6 → better_notion-0.9.7}/.gitignore +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/LICENSE +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/README.md +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/client.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/collections/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/collections/blocks.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/collections/comments.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/collections/databases.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/collections/pages.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/collections/users.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/entities/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/entities/block.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/entities/comment.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/entities/database.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/entities/page.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/entities/user.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/errors.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/oauth.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/base.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/checkbox.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/date.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/email.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/number.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/phone.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/rich_text.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/select.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/title.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/properties/url.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/utils/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_api/utils/pagination.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/async_typer.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/blocks.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/comments.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/databases.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/pages.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/search.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/update.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/users.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/commands/workspace.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/config.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/errors.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/main.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/markdown.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/response.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_cli/utils/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/base/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/base/entity.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/cache/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/cache/cache.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/client.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/managers/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/managers/block_manager.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/managers/comment_manager.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/managers/database_manager.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/managers/page_manager.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/managers/user_manager.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/block.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/audio.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/bookmark.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/breadcrumb.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/bullet.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/callout.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/code.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/column.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/column_list.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/divider.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/embed.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/equation.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/file.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/heading.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/image.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/numbered.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/paragraph.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/pdf.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/quote.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/synced_block.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/table.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/table_row.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/template.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/todo.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/toggle.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/blocks/video.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/comment.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/database.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/page.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/models/user.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/parents/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/properties/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/properties/formula.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/properties/parsers.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/properties/relation.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/query/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/query/database_query.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/_sdk/query/filter_translator.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/plugins/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/plugins/base.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/plugins/loader.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/plugins/official/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/plugins/official/productivity.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/plugins/state.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/utils/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/utils/helpers.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/better_notion/utils/retry.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/base/test_entity.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/cache/test_cache.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/models/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/models/blocks/test_advanced_blocks.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/models/test_block.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/models/test_database.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/models/test_database_bug.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/models/test_page.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/models/test_user.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/properties/test_formula.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/properties/test_parsers.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/properties/test_relation.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/query/test_database_query.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/query/test_filter_translator.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/test_client.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/_sdk/test_comment.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/test_async_typer.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/test_config.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/test_errors.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/test_main.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/test_pages_commands.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/test_response.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/cli/test_update.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/conftest.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/integration/conftest.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/integration/test_blocks.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/integration/test_databases.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/integration/test_pages.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/integration/test_search.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/integration/test_users.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/plugins/__init__.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/plugins/test_base.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/plugins/test_loader.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/plugins/test_marketplace.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/plugins/test_productivity_plugin.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/plugins/test_state.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/unit/test_client.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/unit/test_collections.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/unit/test_entities.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/unit/test_errors.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/unit/test_helpers.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/unit/test_properties.py +0 -0
- {better_notion-0.9.6 → better_notion-0.9.7}/tests/utils/test_retry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: better-notion
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.7
|
|
4
4
|
Summary: A high-level Python SDK for the Notion API with developer experience in mind.
|
|
5
5
|
Project-URL: Homepage, https://github.com/nesalia-inc/better-notion
|
|
6
6
|
Project-URL: Documentation, https://github.com/nesalia-inc/better-notion#readme
|
|
@@ -25,6 +25,7 @@ Classifier: Typing :: Typed
|
|
|
25
25
|
Requires-Python: >=3.10
|
|
26
26
|
Requires-Dist: httpx<0.28.0,>=0.27.0
|
|
27
27
|
Requires-Dist: pydantic<3.0.0,>=2.0.0
|
|
28
|
+
Requires-Dist: rich<14.0.0,>=13.0.0
|
|
28
29
|
Requires-Dist: typing-extensions>=4.0.0
|
|
29
30
|
Provides-Extra: all
|
|
30
31
|
Requires-Dist: asyncer>=0.0.5; extra == 'all'
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication commands for Better Notion CLI.
|
|
3
|
+
|
|
4
|
+
This module provides commands for managing authentication tokens.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from better_notion._cli.async_typer import AsyncTyper
|
|
11
|
+
from better_notion._cli.config import Config
|
|
12
|
+
from better_notion._cli.display import is_human_mode, print_rich_error, print_rich_success
|
|
13
|
+
|
|
14
|
+
app = AsyncTyper(help="Authentication commands")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@app.command()
|
|
18
|
+
def login(
|
|
19
|
+
ctx: typer.Context,
|
|
20
|
+
token: str = typer.Option(
|
|
21
|
+
...,
|
|
22
|
+
"--token",
|
|
23
|
+
"-t",
|
|
24
|
+
help="Notion API integration token",
|
|
25
|
+
prompt=True,
|
|
26
|
+
hide_input=True,
|
|
27
|
+
),
|
|
28
|
+
json_output: bool = typer.Option(
|
|
29
|
+
False,
|
|
30
|
+
"--json",
|
|
31
|
+
"-j",
|
|
32
|
+
help="Output in JSON format (for AI agents)",
|
|
33
|
+
),
|
|
34
|
+
) -> None:
|
|
35
|
+
"""
|
|
36
|
+
Authenticate with Notion API.
|
|
37
|
+
|
|
38
|
+
Stores the authentication token for subsequent CLI commands.
|
|
39
|
+
The token can be obtained from https://www.notion.so/my-integrations.
|
|
40
|
+
"""
|
|
41
|
+
config_path = Config.get_config_path()
|
|
42
|
+
|
|
43
|
+
# Check if already logged in
|
|
44
|
+
if config_path.exists():
|
|
45
|
+
typer.confirm(
|
|
46
|
+
"You are already authenticated. Overwrite existing credentials?",
|
|
47
|
+
abort=True,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Save the token
|
|
51
|
+
try:
|
|
52
|
+
Config.save(token=token)
|
|
53
|
+
print_rich_success(
|
|
54
|
+
"Successfully authenticated with Notion API",
|
|
55
|
+
data={"config_path": str(config_path)},
|
|
56
|
+
json_output=json_output and not is_human_mode()
|
|
57
|
+
)
|
|
58
|
+
except OSError as e:
|
|
59
|
+
print_rich_error(
|
|
60
|
+
f"Failed to save credentials: {e}",
|
|
61
|
+
details={"config_path": str(config_path)},
|
|
62
|
+
json_output=json_output and not is_human_mode()
|
|
63
|
+
)
|
|
64
|
+
raise typer.Exit(1)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@app.command()
|
|
68
|
+
def status(
|
|
69
|
+
json_output: bool = typer.Option(
|
|
70
|
+
False,
|
|
71
|
+
"--json",
|
|
72
|
+
"-j",
|
|
73
|
+
help="Output in JSON format (for AI agents)",
|
|
74
|
+
),
|
|
75
|
+
) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Check authentication status.
|
|
78
|
+
|
|
79
|
+
Verifies the stored authentication token and displays workspace information.
|
|
80
|
+
"""
|
|
81
|
+
from better_notion._cli.display import print_rich_info
|
|
82
|
+
|
|
83
|
+
config = Config.load()
|
|
84
|
+
|
|
85
|
+
info = {
|
|
86
|
+
"status": "authenticated",
|
|
87
|
+
"token_preview": config.token[:20] + "...",
|
|
88
|
+
"timeout": config.timeout,
|
|
89
|
+
"retry_attempts": config.retry_attempts,
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
print_rich_info(
|
|
93
|
+
info,
|
|
94
|
+
title="Authentication Status",
|
|
95
|
+
json_output=json_output and not is_human_mode()
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@app.command()
|
|
100
|
+
def logout(
|
|
101
|
+
json_output: bool = typer.Option(
|
|
102
|
+
False,
|
|
103
|
+
"--json",
|
|
104
|
+
"-j",
|
|
105
|
+
help="Output in JSON format (for AI agents)",
|
|
106
|
+
),
|
|
107
|
+
) -> None:
|
|
108
|
+
"""
|
|
109
|
+
Remove stored credentials.
|
|
110
|
+
|
|
111
|
+
Deletes the authentication token from the configuration file.
|
|
112
|
+
"""
|
|
113
|
+
from better_notion._cli.display import print_rich_error
|
|
114
|
+
|
|
115
|
+
config_path = Config.get_config_path()
|
|
116
|
+
|
|
117
|
+
if not config_path.exists():
|
|
118
|
+
print_rich_error(
|
|
119
|
+
"No credentials found",
|
|
120
|
+
details={"config_path": str(config_path)},
|
|
121
|
+
json_output=json_output and not is_human_mode()
|
|
122
|
+
)
|
|
123
|
+
raise typer.Exit(1)
|
|
124
|
+
|
|
125
|
+
try:
|
|
126
|
+
config_path.unlink()
|
|
127
|
+
print_rich_success(
|
|
128
|
+
f"Credentials removed from {config_path}",
|
|
129
|
+
json_output=json_output and not is_human_mode()
|
|
130
|
+
)
|
|
131
|
+
except OSError as e:
|
|
132
|
+
print_rich_error(
|
|
133
|
+
f"Failed to remove credentials: {e}",
|
|
134
|
+
json_output=json_output and not is_human_mode()
|
|
135
|
+
)
|
|
136
|
+
raise typer.Exit(1)
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Config commands for Better Notion CLI.
|
|
3
|
+
|
|
4
|
+
This module provides commands for managing CLI configuration.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
import typer
|
|
11
|
+
|
|
12
|
+
from better_notion._cli.async_typer import AsyncTyper
|
|
13
|
+
from better_notion._cli.config import Config
|
|
14
|
+
from better_notion._cli.display import is_human_mode, print_rich_error, print_rich_info, print_rich_success
|
|
15
|
+
|
|
16
|
+
app = AsyncTyper(help="Configuration commands")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@app.command("get")
|
|
20
|
+
def get(
|
|
21
|
+
key: str = typer.Argument(..., help="Configuration key to get"),
|
|
22
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
23
|
+
) -> None:
|
|
24
|
+
"""Get a configuration value."""
|
|
25
|
+
use_json = json_output or not is_human_mode()
|
|
26
|
+
|
|
27
|
+
try:
|
|
28
|
+
config = Config.load()
|
|
29
|
+
|
|
30
|
+
valid_keys = ["token", "default_database", "default_output", "timeout", "retry_attempts"]
|
|
31
|
+
|
|
32
|
+
if key not in valid_keys:
|
|
33
|
+
print_rich_error(
|
|
34
|
+
f"Invalid key. Valid keys: {', '.join(valid_keys)}",
|
|
35
|
+
code="INVALID_KEY",
|
|
36
|
+
json_output=use_json
|
|
37
|
+
)
|
|
38
|
+
raise typer.Exit(1)
|
|
39
|
+
|
|
40
|
+
value = getattr(config, key, None)
|
|
41
|
+
|
|
42
|
+
# Mask token for security
|
|
43
|
+
if key == "token" and value:
|
|
44
|
+
value = value[:20] + "..."
|
|
45
|
+
|
|
46
|
+
print_rich_info(
|
|
47
|
+
{"key": key, "value": value},
|
|
48
|
+
title="Configuration Value",
|
|
49
|
+
json_output=use_json
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
except typer.Exit:
|
|
53
|
+
raise
|
|
54
|
+
except Exception as e:
|
|
55
|
+
print_rich_error(
|
|
56
|
+
f"Failed to get configuration: {e}",
|
|
57
|
+
code="UNKNOWN_ERROR",
|
|
58
|
+
json_output=use_json
|
|
59
|
+
)
|
|
60
|
+
raise typer.Exit(1)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@app.command("set")
|
|
64
|
+
def set(
|
|
65
|
+
key: str = typer.Argument(..., help="Configuration key to set"),
|
|
66
|
+
value: str = typer.Argument(..., help="Value to set"),
|
|
67
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
68
|
+
) -> None:
|
|
69
|
+
"""Set a configuration value."""
|
|
70
|
+
use_json = json_output or not is_human_mode()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
config_path = Config.get_config_path()
|
|
74
|
+
|
|
75
|
+
# Load existing config
|
|
76
|
+
if config_path.exists():
|
|
77
|
+
with open(config_path) as f:
|
|
78
|
+
config_data = json.load(f)
|
|
79
|
+
else:
|
|
80
|
+
config_data = {}
|
|
81
|
+
|
|
82
|
+
# Parse value based on type
|
|
83
|
+
parsed_value = value
|
|
84
|
+
if key == "timeout" or key == "retry_attempts":
|
|
85
|
+
parsed_value = int(value)
|
|
86
|
+
elif key == "token" or key == "default_database" or key == "default_output":
|
|
87
|
+
pass # Keep as string
|
|
88
|
+
|
|
89
|
+
config_data[key] = parsed_value
|
|
90
|
+
|
|
91
|
+
# Save updated config
|
|
92
|
+
with open(config_path, "w") as f:
|
|
93
|
+
json.dump(config_data, f, indent=2)
|
|
94
|
+
|
|
95
|
+
print_rich_success(
|
|
96
|
+
f"Configuration '{key}' set to {parsed_value}",
|
|
97
|
+
data={"key": key, "value": parsed_value},
|
|
98
|
+
json_output=use_json
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
except ValueError:
|
|
102
|
+
print_rich_error(
|
|
103
|
+
f"Invalid value for '{key}'. Expected type: int for timeout/retry_attempts",
|
|
104
|
+
code="INVALID_VALUE",
|
|
105
|
+
json_output=use_json
|
|
106
|
+
)
|
|
107
|
+
raise typer.Exit(1)
|
|
108
|
+
except Exception as e:
|
|
109
|
+
print_rich_error(
|
|
110
|
+
f"Failed to set configuration: {e}",
|
|
111
|
+
code="UNKNOWN_ERROR",
|
|
112
|
+
json_output=use_json
|
|
113
|
+
)
|
|
114
|
+
raise typer.Exit(1)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@app.command("list")
|
|
118
|
+
def list_all(
|
|
119
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
120
|
+
) -> None:
|
|
121
|
+
"""List all configuration values."""
|
|
122
|
+
use_json = json_output or not is_human_mode()
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
from better_notion._cli.display import print_rich_table
|
|
126
|
+
|
|
127
|
+
config = Config.load()
|
|
128
|
+
|
|
129
|
+
config_data = [
|
|
130
|
+
{"key": "token", "value": config.token[:20] + "..." if config.token else None},
|
|
131
|
+
{"key": "default_database", "value": config.default_database},
|
|
132
|
+
{"key": "default_output", "value": config.default_output},
|
|
133
|
+
{"key": "timeout", "value": config.timeout},
|
|
134
|
+
{"key": "retry_attempts", "value": config.retry_attempts},
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
print_rich_table(
|
|
138
|
+
config_data,
|
|
139
|
+
title="Configuration",
|
|
140
|
+
columns=["key", "value"],
|
|
141
|
+
json_output=use_json
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
except Exception as e:
|
|
145
|
+
print_rich_error(
|
|
146
|
+
f"Failed to list configuration: {e}",
|
|
147
|
+
code="UNKNOWN_ERROR",
|
|
148
|
+
json_output=use_json
|
|
149
|
+
)
|
|
150
|
+
raise typer.Exit(1)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@app.command("reset")
|
|
154
|
+
def reset(
|
|
155
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
156
|
+
) -> None:
|
|
157
|
+
"""Reset configuration to defaults."""
|
|
158
|
+
use_json = json_output or not is_human_mode()
|
|
159
|
+
|
|
160
|
+
try:
|
|
161
|
+
config_path = Config.get_config_path()
|
|
162
|
+
|
|
163
|
+
if config_path.exists():
|
|
164
|
+
config_path.unlink()
|
|
165
|
+
|
|
166
|
+
print_rich_success(
|
|
167
|
+
"Configuration has been reset. Run 'notion auth login' to configure.",
|
|
168
|
+
data={"status": "reset"},
|
|
169
|
+
json_output=use_json
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
except Exception as e:
|
|
173
|
+
print_rich_error(
|
|
174
|
+
f"Failed to reset configuration: {e}",
|
|
175
|
+
code="UNKNOWN_ERROR",
|
|
176
|
+
json_output=use_json
|
|
177
|
+
)
|
|
178
|
+
raise typer.Exit(1)
|
|
@@ -25,7 +25,12 @@ import typer
|
|
|
25
25
|
from typer.testing import CliRunner
|
|
26
26
|
|
|
27
27
|
from better_notion._cli.async_typer import AsyncTyper
|
|
28
|
-
from better_notion._cli.
|
|
28
|
+
from better_notion._cli.display import (
|
|
29
|
+
is_human_mode,
|
|
30
|
+
print_rich_error,
|
|
31
|
+
print_rich_success,
|
|
32
|
+
print_rich_table,
|
|
33
|
+
)
|
|
29
34
|
from better_notion.plugins.loader import PluginLoader
|
|
30
35
|
from better_notion.plugins.base import CommandPlugin
|
|
31
36
|
|
|
@@ -220,7 +225,10 @@ def list_plugins(
|
|
|
220
225
|
# Merge both
|
|
221
226
|
all_plugins = {**official_plugins, **user_plugins}
|
|
222
227
|
|
|
223
|
-
|
|
228
|
+
# Determine output mode
|
|
229
|
+
use_json = json_output or not is_human_mode()
|
|
230
|
+
|
|
231
|
+
if use_json:
|
|
224
232
|
return typer.echo(json.dumps(all_plugins, indent=2))
|
|
225
233
|
|
|
226
234
|
# Display as formatted output
|
|
@@ -229,41 +237,49 @@ def list_plugins(
|
|
|
229
237
|
typer.echo(f"\nPlugins directory: {Path.home() / '.notion' / 'plugins'}")
|
|
230
238
|
return
|
|
231
239
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if has_official:
|
|
254
|
-
typer.echo("\nUser Plugins:")
|
|
255
|
-
for name, info in user_plugins.items():
|
|
256
|
-
official_marker = "✓" if info.get("official") else " "
|
|
257
|
-
typer.echo(f" • [{official_marker}] {name:20} {info.get('description', 'No description'):40}")
|
|
258
|
-
|
|
259
|
-
if verbose:
|
|
260
|
-
typer.echo(f" Version: {info.get('version', 'unknown')}")
|
|
261
|
-
typer.echo(f" Author: {info.get('author', 'unknown')}")
|
|
240
|
+
# Build table data for Rich display
|
|
241
|
+
table_data = []
|
|
242
|
+
for name, info in all_plugins.items():
|
|
243
|
+
is_bundled = info.get('bundled', False)
|
|
244
|
+
enabled = info.get('enabled', True) if is_bundled else True
|
|
245
|
+
|
|
246
|
+
status = "✓ Enabled" if enabled else "✗ Disabled"
|
|
247
|
+
if not is_bundled:
|
|
248
|
+
status = "User"
|
|
249
|
+
|
|
250
|
+
row = {
|
|
251
|
+
"name": name,
|
|
252
|
+
"description": info.get('description', 'No description')[:40],
|
|
253
|
+
"status": status,
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if verbose:
|
|
257
|
+
row["version"] = info.get('version', 'unknown')
|
|
258
|
+
row["author"] = info.get('author', 'unknown')
|
|
259
|
+
|
|
260
|
+
table_data.append(row)
|
|
262
261
|
|
|
262
|
+
# Display table
|
|
263
|
+
columns = ["name", "description", "status"]
|
|
264
|
+
if verbose:
|
|
265
|
+
columns.extend(["version", "author"])
|
|
266
|
+
|
|
267
|
+
print_rich_table(
|
|
268
|
+
table_data,
|
|
269
|
+
title=f"Plugins ({len(all_plugins)} total)",
|
|
270
|
+
columns=columns,
|
|
271
|
+
json_output=False
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# Show tip
|
|
263
275
|
typer.echo("\nTip: Use 'notion plugin enable/disable <name>' to toggle official plugins")
|
|
264
276
|
|
|
265
277
|
except Exception as e:
|
|
266
|
-
|
|
278
|
+
print_rich_error(
|
|
279
|
+
f"Failed to list plugins: {e}",
|
|
280
|
+
code="LIST_ERROR",
|
|
281
|
+
json_output=json_output or not is_human_mode()
|
|
282
|
+
)
|
|
267
283
|
|
|
268
284
|
|
|
269
285
|
@app.command()
|
|
@@ -519,6 +535,7 @@ def validate(
|
|
|
519
535
|
@app.command()
|
|
520
536
|
def enable(
|
|
521
537
|
plugin_name: str = typer.Argument(..., help="Name of the plugin to enable"),
|
|
538
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
522
539
|
) -> None:
|
|
523
540
|
"""
|
|
524
541
|
Enable a plugin.
|
|
@@ -540,11 +557,13 @@ def enable(
|
|
|
540
557
|
state_manager = PluginStateManager()
|
|
541
558
|
state_manager.enable(plugin_name)
|
|
542
559
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
"
|
|
546
|
-
"type": "official"
|
|
547
|
-
|
|
560
|
+
use_json = json_output or not is_human_mode()
|
|
561
|
+
print_rich_success(
|
|
562
|
+
f"Plugin '{plugin_name}' enabled (may require CLI restart)",
|
|
563
|
+
data={"plugin": plugin_name, "type": "official"},
|
|
564
|
+
json_output=use_json
|
|
565
|
+
)
|
|
566
|
+
return
|
|
548
567
|
|
|
549
568
|
# For user plugins, use the existing enabled.json logic
|
|
550
569
|
config_file = Path.home() / ".notion" / "plugins" / "enabled.json"
|
|
@@ -559,19 +578,26 @@ def enable(
|
|
|
559
578
|
config["enabled"].append(plugin_name)
|
|
560
579
|
config_file.write_text(json.dumps(config, indent=2))
|
|
561
580
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
"
|
|
565
|
-
"type": "user"
|
|
566
|
-
|
|
581
|
+
use_json = json_output or not is_human_mode()
|
|
582
|
+
print_rich_success(
|
|
583
|
+
f"Plugin '{plugin_name}' enabled",
|
|
584
|
+
data={"plugin": plugin_name, "type": "user"},
|
|
585
|
+
json_output=use_json
|
|
586
|
+
)
|
|
567
587
|
|
|
568
588
|
except Exception as e:
|
|
569
|
-
|
|
589
|
+
use_json = json_output or not is_human_mode()
|
|
590
|
+
print_rich_error(
|
|
591
|
+
f"Failed to enable plugin: {e}",
|
|
592
|
+
code="ENABLE_ERROR",
|
|
593
|
+
json_output=use_json
|
|
594
|
+
)
|
|
570
595
|
|
|
571
596
|
|
|
572
597
|
@app.command()
|
|
573
598
|
def disable(
|
|
574
599
|
plugin_name: str = typer.Argument(..., help="Name of the plugin to disable"),
|
|
600
|
+
json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
|
|
575
601
|
) -> None:
|
|
576
602
|
"""
|
|
577
603
|
Disable a plugin.
|
|
@@ -593,12 +619,13 @@ def disable(
|
|
|
593
619
|
state_manager = PluginStateManager()
|
|
594
620
|
state_manager.disable(plugin_name)
|
|
595
621
|
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
"
|
|
599
|
-
"type": "official",
|
|
600
|
-
|
|
601
|
-
|
|
622
|
+
use_json = json_output or not is_human_mode()
|
|
623
|
+
print_rich_success(
|
|
624
|
+
f"Plugin '{plugin_name}' disabled (will take effect after CLI restart)",
|
|
625
|
+
data={"plugin": plugin_name, "type": "official"},
|
|
626
|
+
json_output=use_json
|
|
627
|
+
)
|
|
628
|
+
return
|
|
602
629
|
|
|
603
630
|
# For user plugins, use the existing enabled.json logic
|
|
604
631
|
config_file = Path.home() / ".notion" / "plugins" / "enabled.json"
|
|
@@ -610,17 +637,28 @@ def disable(
|
|
|
610
637
|
config["enabled"].remove(plugin_name)
|
|
611
638
|
config_file.write_text(json.dumps(config, indent=2))
|
|
612
639
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
"
|
|
616
|
-
"type": "user"
|
|
617
|
-
|
|
640
|
+
use_json = json_output or not is_human_mode()
|
|
641
|
+
print_rich_success(
|
|
642
|
+
f"Plugin '{plugin_name}' disabled",
|
|
643
|
+
data={"plugin": plugin_name, "type": "user"},
|
|
644
|
+
json_output=use_json
|
|
645
|
+
)
|
|
618
646
|
|
|
619
647
|
except Exception as e:
|
|
620
|
-
|
|
648
|
+
use_json = json_output or not is_human_mode()
|
|
649
|
+
print_rich_error(
|
|
650
|
+
f"Failed to disable plugin: {e}",
|
|
651
|
+
code="DISABLE_ERROR",
|
|
652
|
+
json_output=use_json
|
|
653
|
+
)
|
|
621
654
|
|
|
622
655
|
except Exception as e:
|
|
623
|
-
|
|
656
|
+
use_json = json_output or not is_human_mode()
|
|
657
|
+
print_rich_error(
|
|
658
|
+
f"Failed to disable plugin: {e}",
|
|
659
|
+
code="DISABLE_ERROR",
|
|
660
|
+
json_output=use_json
|
|
661
|
+
)
|
|
624
662
|
|
|
625
663
|
|
|
626
664
|
@app.command()
|
|
@@ -702,8 +740,10 @@ def marketplace(
|
|
|
702
740
|
if category:
|
|
703
741
|
plugins_data = [p for p in plugins_data if p.get("category") == category]
|
|
704
742
|
|
|
705
|
-
#
|
|
706
|
-
|
|
743
|
+
# Determine output mode
|
|
744
|
+
use_json = json_output or not is_human_mode()
|
|
745
|
+
|
|
746
|
+
if use_json:
|
|
707
747
|
return typer.echo(json.dumps({"plugins": plugins_data}, indent=2))
|
|
708
748
|
|
|
709
749
|
# Display in formatted table
|
|
@@ -714,42 +754,50 @@ def marketplace(
|
|
|
714
754
|
typer.echo("No official plugins available.")
|
|
715
755
|
return
|
|
716
756
|
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
# Always show basic info
|
|
727
|
-
typer.echo(f" Version: {info.get('version', 'unknown'):8} │ "
|
|
728
|
-
f"Author: {info.get('author', 'unknown')}")
|
|
757
|
+
# Build table data for Rich
|
|
758
|
+
table_data = []
|
|
759
|
+
for info in plugins_data:
|
|
760
|
+
row = {
|
|
761
|
+
"name": info.get('name', 'unknown'),
|
|
762
|
+
"description": info.get('description', 'No description')[:40],
|
|
763
|
+
"version": info.get('version', 'unknown'),
|
|
764
|
+
}
|
|
729
765
|
|
|
730
766
|
if verbose:
|
|
731
|
-
|
|
767
|
+
row["author"] = info.get('author', 'unknown')
|
|
732
768
|
if info.get("category"):
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
769
|
+
row["category"] = info.get('category')
|
|
770
|
+
deps = info.get("dependencies", [])
|
|
771
|
+
row["dependencies"] = ', '.join(deps) if deps else "None"
|
|
772
|
+
|
|
773
|
+
table_data.append(row)
|
|
774
|
+
|
|
775
|
+
# Display table
|
|
776
|
+
columns = ["name", "description", "version"]
|
|
777
|
+
if verbose:
|
|
778
|
+
columns.append("author")
|
|
779
|
+
if any("category" in row for row in table_data):
|
|
780
|
+
columns.append("category")
|
|
781
|
+
if any("dependencies" in row for row in table_data):
|
|
782
|
+
columns.append("dependencies")
|
|
783
|
+
|
|
784
|
+
title = f"Official Plugins Marketplace ({len(plugins_data)} available)"
|
|
750
785
|
if category:
|
|
751
|
-
|
|
752
|
-
|
|
786
|
+
title += f" - Category: {category}"
|
|
787
|
+
|
|
788
|
+
print_rich_table(
|
|
789
|
+
table_data,
|
|
790
|
+
title=title,
|
|
791
|
+
columns=columns,
|
|
792
|
+
json_output=False
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
if not verbose:
|
|
796
|
+
typer.echo("\nTip: Use --verbose to see more details")
|
|
753
797
|
|
|
754
798
|
except Exception as e:
|
|
755
|
-
|
|
799
|
+
print_rich_error(
|
|
800
|
+
f"Failed to load marketplace: {e}",
|
|
801
|
+
code="MARKETPLACE_ERROR",
|
|
802
|
+
json_output=json_output or not is_human_mode()
|
|
803
|
+
)
|