better-notion 0.9.4__tar.gz → 0.9.6__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.4 → better_notion-0.9.6}/PKG-INFO +1 -1
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/plugins.py +127 -34
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/update.py +51 -30
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/main.py +14 -3
- better_notion-0.9.6/better_notion/plugins/state.py +123 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/pyproject.toml +1 -1
- better_notion-0.9.6/tests/plugins/test_plugin_commands_state.py +294 -0
- better_notion-0.9.6/tests/plugins/test_state.py +212 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/.gitignore +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/LICENSE +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/README.md +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/client.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/collections/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/collections/blocks.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/collections/comments.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/collections/databases.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/collections/pages.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/collections/users.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/entities/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/entities/block.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/entities/comment.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/entities/database.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/entities/page.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/entities/user.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/errors.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/oauth.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/base.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/checkbox.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/date.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/email.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/number.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/phone.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/rich_text.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/select.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/title.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/properties/url.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/utils/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_api/utils/pagination.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/async_typer.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/auth.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/blocks.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/comments.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/config.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/databases.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/pages.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/search.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/users.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/commands/workspace.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/config.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/errors.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/markdown.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/response.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_cli/utils/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/base/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/base/entity.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/cache/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/cache/cache.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/client.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/managers/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/managers/block_manager.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/managers/comment_manager.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/managers/database_manager.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/managers/page_manager.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/managers/user_manager.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/block.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/audio.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/bookmark.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/breadcrumb.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/bullet.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/callout.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/code.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/column.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/column_list.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/divider.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/embed.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/equation.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/file.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/heading.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/image.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/numbered.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/paragraph.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/pdf.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/quote.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/synced_block.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/table.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/table_row.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/template.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/todo.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/toggle.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/blocks/video.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/comment.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/database.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/page.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/models/user.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/parents/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/properties/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/properties/formula.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/properties/parsers.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/properties/relation.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/query/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/query/database_query.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/_sdk/query/filter_translator.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/plugins/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/plugins/base.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/plugins/loader.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/plugins/official/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/plugins/official/productivity.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/utils/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/utils/helpers.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/better_notion/utils/retry.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/base/test_entity.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/cache/test_cache.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/models/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/models/blocks/test_advanced_blocks.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/models/test_block.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/models/test_database.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/models/test_database_bug.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/models/test_page.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/models/test_user.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/properties/test_formula.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/properties/test_parsers.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/properties/test_relation.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/query/test_database_query.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/query/test_filter_translator.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/test_client.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/_sdk/test_comment.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/test_async_typer.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/test_config.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/test_errors.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/test_main.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/test_pages_commands.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/test_response.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/cli/test_update.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/conftest.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/integration/conftest.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/integration/test_blocks.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/integration/test_databases.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/integration/test_pages.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/integration/test_search.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/integration/test_users.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/plugins/__init__.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/plugins/test_base.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/plugins/test_loader.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/plugins/test_marketplace.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/plugins/test_productivity_plugin.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/unit/test_client.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/unit/test_collections.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/unit/test_entities.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/unit/test_errors.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/unit/test_helpers.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/tests/unit/test_properties.py +0 -0
- {better_notion-0.9.4 → better_notion-0.9.6}/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.6
|
|
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
|
|
@@ -183,40 +183,87 @@ def list_plugins(
|
|
|
183
183
|
"""
|
|
184
184
|
List all installed and available plugins.
|
|
185
185
|
|
|
186
|
+
Shows both official plugins (built-in) and user-installed plugins.
|
|
187
|
+
|
|
186
188
|
Examples:
|
|
187
189
|
notion plugin list
|
|
188
190
|
notion plugin list --verbose
|
|
189
191
|
notion plugin list --json
|
|
190
192
|
"""
|
|
191
193
|
try:
|
|
194
|
+
from better_notion.plugins.official import OFFICIAL_PLUGINS
|
|
195
|
+
from better_notion.plugins.state import PluginStateManager
|
|
196
|
+
|
|
192
197
|
loader = get_plugin_loader()
|
|
193
|
-
|
|
198
|
+
state_manager = PluginStateManager()
|
|
199
|
+
|
|
200
|
+
# Get user plugins
|
|
201
|
+
user_plugins = loader.list_plugins()
|
|
202
|
+
|
|
203
|
+
# Get official plugins with their state
|
|
204
|
+
official_plugins = {}
|
|
205
|
+
for plugin_class in OFFICIAL_PLUGINS:
|
|
206
|
+
try:
|
|
207
|
+
plugin = plugin_class()
|
|
208
|
+
info = plugin.get_info()
|
|
209
|
+
plugin_name = info.get('name')
|
|
210
|
+
|
|
211
|
+
# Add state information
|
|
212
|
+
info['bundled'] = True
|
|
213
|
+
info['enabled'] = state_manager.is_enabled(plugin_name)
|
|
214
|
+
|
|
215
|
+
official_plugins[plugin_name] = info
|
|
216
|
+
except Exception:
|
|
217
|
+
# Skip plugins that fail to instantiate
|
|
218
|
+
continue
|
|
219
|
+
|
|
220
|
+
# Merge both
|
|
221
|
+
all_plugins = {**official_plugins, **user_plugins}
|
|
194
222
|
|
|
195
223
|
if json_output:
|
|
196
|
-
return typer.echo(json.dumps(
|
|
224
|
+
return typer.echo(json.dumps(all_plugins, indent=2))
|
|
197
225
|
|
|
198
|
-
# Display as
|
|
199
|
-
if not
|
|
200
|
-
typer.echo("No plugins
|
|
226
|
+
# Display as formatted output
|
|
227
|
+
if not all_plugins:
|
|
228
|
+
typer.echo("No plugins found.")
|
|
201
229
|
typer.echo(f"\nPlugins directory: {Path.home() / '.notion' / 'plugins'}")
|
|
202
230
|
return
|
|
203
231
|
|
|
204
|
-
typer.echo("
|
|
205
|
-
typer.echo("
|
|
232
|
+
typer.echo("Plugins:")
|
|
233
|
+
typer.echo("=" * 70)
|
|
206
234
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
235
|
+
# Group by type
|
|
236
|
+
has_official = bool(official_plugins)
|
|
237
|
+
has_user = bool(user_plugins)
|
|
210
238
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
239
|
+
if has_official:
|
|
240
|
+
typer.echo("\nOfficial Plugins (built-in):")
|
|
241
|
+
for name, info in official_plugins.items():
|
|
242
|
+
enabled = info.get('enabled', True)
|
|
243
|
+
status = "✓ Enabled" if enabled else "✗ Disabled"
|
|
244
|
+
typer.echo(f" • {name:20} {info.get('description', 'No description'):40} [{status}]")
|
|
245
|
+
|
|
246
|
+
if verbose:
|
|
247
|
+
typer.echo(f" Version: {info.get('version', 'unknown')}")
|
|
248
|
+
typer.echo(f" Author: {info.get('author', 'unknown')}")
|
|
249
|
+
if info.get('category'):
|
|
250
|
+
typer.echo(f" Category: {info.get('category')}")
|
|
251
|
+
|
|
252
|
+
if has_user:
|
|
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')}")
|
|
215
262
|
|
|
216
|
-
typer.echo("
|
|
263
|
+
typer.echo("\nTip: Use 'notion plugin enable/disable <name>' to toggle official plugins")
|
|
217
264
|
|
|
218
265
|
except Exception as e:
|
|
219
|
-
return format_error("LIST_ERROR",
|
|
266
|
+
return format_error("LIST_ERROR", str(e))
|
|
220
267
|
|
|
221
268
|
|
|
222
269
|
@app.command()
|
|
@@ -476,14 +523,33 @@ def enable(
|
|
|
476
523
|
"""
|
|
477
524
|
Enable a plugin.
|
|
478
525
|
|
|
526
|
+
For official plugins, this re-enables them if they were disabled.
|
|
527
|
+
For user plugins, this adds them to the enabled list.
|
|
528
|
+
|
|
479
529
|
Examples:
|
|
530
|
+
notion plugin enable productivity
|
|
480
531
|
notion plugin enable organizations
|
|
481
532
|
"""
|
|
482
|
-
# Create enabled plugins list
|
|
483
|
-
config_file = Path.home() / ".notion" / "plugins" / "enabled.json"
|
|
484
|
-
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
485
|
-
|
|
486
533
|
try:
|
|
534
|
+
loader = get_plugin_loader()
|
|
535
|
+
|
|
536
|
+
# Check if it's an official plugin
|
|
537
|
+
if loader.is_official_plugin(plugin_name):
|
|
538
|
+
from better_notion.plugins.state import PluginStateManager
|
|
539
|
+
|
|
540
|
+
state_manager = PluginStateManager()
|
|
541
|
+
state_manager.enable(plugin_name)
|
|
542
|
+
|
|
543
|
+
return format_success({
|
|
544
|
+
"plugin": plugin_name,
|
|
545
|
+
"status": "enabled",
|
|
546
|
+
"type": "official"
|
|
547
|
+
})
|
|
548
|
+
|
|
549
|
+
# For user plugins, use the existing enabled.json logic
|
|
550
|
+
config_file = Path.home() / ".notion" / "plugins" / "enabled.json"
|
|
551
|
+
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
552
|
+
|
|
487
553
|
if config_file.exists():
|
|
488
554
|
config = json.loads(config_file.read_text())
|
|
489
555
|
else:
|
|
@@ -495,11 +561,12 @@ def enable(
|
|
|
495
561
|
|
|
496
562
|
return format_success({
|
|
497
563
|
"plugin": plugin_name,
|
|
498
|
-
"status": "enabled"
|
|
564
|
+
"status": "enabled",
|
|
565
|
+
"type": "user"
|
|
499
566
|
})
|
|
500
567
|
|
|
501
568
|
except Exception as e:
|
|
502
|
-
return format_error("ENABLE_ERROR",
|
|
569
|
+
return format_error("ENABLE_ERROR", str(e))
|
|
503
570
|
|
|
504
571
|
|
|
505
572
|
@app.command()
|
|
@@ -509,25 +576,51 @@ def disable(
|
|
|
509
576
|
"""
|
|
510
577
|
Disable a plugin.
|
|
511
578
|
|
|
579
|
+
For official plugins, this disables them (but doesn't remove them).
|
|
580
|
+
For user plugins, this removes them from the enabled list.
|
|
581
|
+
|
|
512
582
|
Examples:
|
|
583
|
+
notion plugin disable productivity
|
|
513
584
|
notion plugin disable organizations
|
|
514
585
|
"""
|
|
515
|
-
config_file = Path.home() / ".notion" / "plugins" / "enabled.json"
|
|
516
|
-
|
|
517
586
|
try:
|
|
518
|
-
|
|
519
|
-
config = json.loads(config_file.read_text())
|
|
520
|
-
if plugin_name in config.get("enabled", []):
|
|
521
|
-
config["enabled"].remove(plugin_name)
|
|
522
|
-
config_file.write_text(json.dumps(config, indent=2))
|
|
587
|
+
loader = get_plugin_loader()
|
|
523
588
|
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
589
|
+
# Check if it's an official plugin
|
|
590
|
+
if loader.is_official_plugin(plugin_name):
|
|
591
|
+
from better_notion.plugins.state import PluginStateManager
|
|
592
|
+
|
|
593
|
+
state_manager = PluginStateManager()
|
|
594
|
+
state_manager.disable(plugin_name)
|
|
595
|
+
|
|
596
|
+
return format_success({
|
|
597
|
+
"plugin": plugin_name,
|
|
598
|
+
"status": "disabled",
|
|
599
|
+
"type": "official",
|
|
600
|
+
"message": "Plugin will be disabled after CLI restart"
|
|
601
|
+
})
|
|
602
|
+
|
|
603
|
+
# For user plugins, use the existing enabled.json logic
|
|
604
|
+
config_file = Path.home() / ".notion" / "plugins" / "enabled.json"
|
|
605
|
+
|
|
606
|
+
try:
|
|
607
|
+
if config_file.exists():
|
|
608
|
+
config = json.loads(config_file.read_text())
|
|
609
|
+
if plugin_name in config.get("enabled", []):
|
|
610
|
+
config["enabled"].remove(plugin_name)
|
|
611
|
+
config_file.write_text(json.dumps(config, indent=2))
|
|
612
|
+
|
|
613
|
+
return format_success({
|
|
614
|
+
"plugin": plugin_name,
|
|
615
|
+
"status": "disabled",
|
|
616
|
+
"type": "user"
|
|
617
|
+
})
|
|
618
|
+
|
|
619
|
+
except Exception as e:
|
|
620
|
+
return format_error("DISABLE_ERROR", str(e))
|
|
528
621
|
|
|
529
622
|
except Exception as e:
|
|
530
|
-
return format_error("DISABLE_ERROR",
|
|
623
|
+
return format_error("DISABLE_ERROR", str(e))
|
|
531
624
|
|
|
532
625
|
|
|
533
626
|
@app.command()
|
|
@@ -13,40 +13,19 @@ import typer
|
|
|
13
13
|
from better_notion._cli.async_typer import AsyncTyper
|
|
14
14
|
from better_notion._cli.response import format_error, format_success
|
|
15
15
|
|
|
16
|
-
app = AsyncTyper(help="Update Better Notion CLI", invoke_without_command=True)
|
|
16
|
+
app = AsyncTyper(help="Update Better Notion CLI", invoke_without_command=True, no_args_is_help=False)
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
def main(
|
|
21
|
-
ctx: typer.Context,
|
|
22
|
-
check: bool = typer.Option(False, "--check", "-c", help="Only check if an update is available"),
|
|
23
|
-
) -> None:
|
|
19
|
+
def _perform_upgrade(check: bool = False) -> None:
|
|
24
20
|
"""
|
|
25
|
-
|
|
21
|
+
Internal function to perform the upgrade.
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
# If no subcommand was provided, execute upgrade
|
|
30
|
-
if ctx.invoked_subcommand is None:
|
|
31
|
-
upgrade(check=check)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
@app.command()
|
|
35
|
-
def upgrade(
|
|
36
|
-
check: bool = typer.Option(False, "--check", "-c", help="Only check if an update is available"),
|
|
37
|
-
) -> None:
|
|
23
|
+
Args:
|
|
24
|
+
check: If True, only check for updates without installing
|
|
38
25
|
"""
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
This is a simple wrapper around pip that checks for and installs updates.
|
|
42
|
-
pip handles all version checking, downloading, and dependency management.
|
|
26
|
+
import platform
|
|
27
|
+
import sys
|
|
43
28
|
|
|
44
|
-
Examples:
|
|
45
|
-
notion update # Install latest version (default)
|
|
46
|
-
notion update upgrade # Same as above
|
|
47
|
-
notion update --check # Check for updates only
|
|
48
|
-
notion update upgrade --check # Same as above
|
|
49
|
-
"""
|
|
50
29
|
package_name = "better-notion"
|
|
51
30
|
|
|
52
31
|
if check:
|
|
@@ -85,6 +64,14 @@ def upgrade(
|
|
|
85
64
|
else:
|
|
86
65
|
# Perform the actual upgrade
|
|
87
66
|
typer.echo(f"Updating {package_name} to the latest version...")
|
|
67
|
+
|
|
68
|
+
# Warn Windows users about potential file locking
|
|
69
|
+
if platform.system() == "Windows":
|
|
70
|
+
typer.echo("")
|
|
71
|
+
typer.echo("⚠ Note: On Windows, if you see a file access error,")
|
|
72
|
+
typer.echo(" close all terminals and run: pip install --upgrade better-notion")
|
|
73
|
+
typer.echo("")
|
|
74
|
+
|
|
88
75
|
typer.echo("")
|
|
89
76
|
|
|
90
77
|
try:
|
|
@@ -112,6 +99,40 @@ def upgrade(
|
|
|
112
99
|
return format_error("UPDATE_ERROR", str(e))
|
|
113
100
|
|
|
114
101
|
|
|
102
|
+
@app.callback()
|
|
103
|
+
def main(
|
|
104
|
+
ctx: typer.Context,
|
|
105
|
+
check: bool = typer.Option(False, "--check", "-c", help="Only check if an update is available"),
|
|
106
|
+
) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Update Better Notion CLI to the latest version.
|
|
109
|
+
|
|
110
|
+
If no subcommand is specified, performs an update by default.
|
|
111
|
+
"""
|
|
112
|
+
# If no subcommand was provided, execute upgrade
|
|
113
|
+
if ctx.invoked_subcommand is None:
|
|
114
|
+
_perform_upgrade(check=check)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@app.command()
|
|
118
|
+
def upgrade(
|
|
119
|
+
check: bool = typer.Option(False, "--check", "-c", help="Only check if an update is available"),
|
|
120
|
+
) -> None:
|
|
121
|
+
"""
|
|
122
|
+
Update Better Notion CLI to the latest version.
|
|
123
|
+
|
|
124
|
+
This is a simple wrapper around pip that checks for and installs updates.
|
|
125
|
+
pip handles all version checking, downloading, and dependency management.
|
|
126
|
+
|
|
127
|
+
Examples:
|
|
128
|
+
notion update # Install latest version (default)
|
|
129
|
+
notion update upgrade # Same as above
|
|
130
|
+
notion update --check # Check for updates only
|
|
131
|
+
notion update upgrade --check # Same as above
|
|
132
|
+
"""
|
|
133
|
+
_perform_upgrade(check=check)
|
|
134
|
+
|
|
135
|
+
|
|
115
136
|
@app.command()
|
|
116
137
|
def check() -> None:
|
|
117
138
|
"""
|
|
@@ -122,7 +143,7 @@ def check() -> None:
|
|
|
122
143
|
Examples:
|
|
123
144
|
notion update check
|
|
124
145
|
"""
|
|
125
|
-
|
|
146
|
+
_perform_upgrade(check=True)
|
|
126
147
|
|
|
127
148
|
|
|
128
149
|
@app.command()
|
|
@@ -135,4 +156,4 @@ def self() -> None:
|
|
|
135
156
|
Examples:
|
|
136
157
|
notion update self
|
|
137
158
|
"""
|
|
138
|
-
|
|
159
|
+
_perform_upgrade(check=False)
|
|
@@ -41,22 +41,33 @@ app.add_typer(update.app, name="update")
|
|
|
41
41
|
|
|
42
42
|
# Load and register official plugins
|
|
43
43
|
def _load_official_plugins():
|
|
44
|
-
"""Load and register official plugins."""
|
|
44
|
+
"""Load and register official plugins, respecting their enabled/disabled state."""
|
|
45
45
|
try:
|
|
46
46
|
from better_notion.plugins.official import OFFICIAL_PLUGINS
|
|
47
47
|
from better_notion.plugins.loader import PluginLoader
|
|
48
|
+
from better_notion.plugins.state import PluginStateManager
|
|
48
49
|
|
|
49
50
|
loader = PluginLoader()
|
|
51
|
+
state_manager = PluginStateManager()
|
|
50
52
|
|
|
51
53
|
for plugin_class in OFFICIAL_PLUGINS:
|
|
52
54
|
try:
|
|
53
55
|
plugin = plugin_class()
|
|
54
|
-
plugin.register_commands(app)
|
|
55
56
|
info = plugin.get_info()
|
|
57
|
+
plugin_name = info.get('name')
|
|
58
|
+
|
|
59
|
+
# Check if plugin is disabled
|
|
60
|
+
if not state_manager.is_enabled(plugin_name):
|
|
61
|
+
# Skip loading this plugin
|
|
62
|
+
continue
|
|
63
|
+
|
|
64
|
+
# Register the plugin's commands
|
|
65
|
+
plugin.register_commands(app)
|
|
66
|
+
|
|
56
67
|
# Store plugin for later reference
|
|
57
68
|
if not hasattr(app, '_loaded_plugins'):
|
|
58
69
|
app._loaded_plugins = {}
|
|
59
|
-
app._loaded_plugins[
|
|
70
|
+
app._loaded_plugins[plugin_name] = plugin
|
|
60
71
|
except Exception as e:
|
|
61
72
|
# Log but don't fail if a plugin fails to load
|
|
62
73
|
pass
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Plugin state management for Better Notion CLI.
|
|
3
|
+
|
|
4
|
+
This module handles the state of official plugins (enabled/disabled).
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from datetime import datetime
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class PluginStateManager:
|
|
15
|
+
"""Manages the enabled/disabled state of official plugins."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, state_file: Path | None = None):
|
|
18
|
+
"""
|
|
19
|
+
Initialize the plugin state manager.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
state_file: Path to the state file. Defaults to ~/.notion/plugins/state.json
|
|
23
|
+
"""
|
|
24
|
+
self.state_file = state_file or (Path.home() / ".notion" / "plugins" / "state.json")
|
|
25
|
+
self.state_file.parent.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
self._state = self._load_state()
|
|
27
|
+
|
|
28
|
+
def _load_state(self) -> dict[str, Any]:
|
|
29
|
+
"""Load state from file."""
|
|
30
|
+
if self.state_file.exists():
|
|
31
|
+
try:
|
|
32
|
+
return json.loads(self.state_file.read_text())
|
|
33
|
+
except (json.JSONDecodeError, IOError):
|
|
34
|
+
pass
|
|
35
|
+
return {"official_plugins": {}}
|
|
36
|
+
|
|
37
|
+
def _save_state(self) -> None:
|
|
38
|
+
"""Save state to file."""
|
|
39
|
+
self.state_file.write_text(json.dumps(self._state, indent=2))
|
|
40
|
+
|
|
41
|
+
def is_enabled(self, plugin_name: str) -> bool:
|
|
42
|
+
"""
|
|
43
|
+
Check if a plugin is enabled.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
plugin_name: Name of the plugin
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
True if the plugin is enabled, False if disabled
|
|
50
|
+
"""
|
|
51
|
+
plugin_state = self._state.get("official_plugins", {}).get(plugin_name, {})
|
|
52
|
+
return plugin_state.get("state") != "disabled"
|
|
53
|
+
|
|
54
|
+
def enable(self, plugin_name: str) -> None:
|
|
55
|
+
"""
|
|
56
|
+
Enable a plugin.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
plugin_name: Name of the plugin to enable
|
|
60
|
+
"""
|
|
61
|
+
if "official_plugins" not in self._state:
|
|
62
|
+
self._state["official_plugins"] = {}
|
|
63
|
+
|
|
64
|
+
# Remove disabled state if it exists
|
|
65
|
+
if plugin_name in self._state["official_plugins"]:
|
|
66
|
+
del self._state["official_plugins"][plugin_name]
|
|
67
|
+
|
|
68
|
+
self._save_state()
|
|
69
|
+
|
|
70
|
+
def disable(self, plugin_name: str) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Disable a plugin.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
plugin_name: Name of the plugin to disable
|
|
76
|
+
"""
|
|
77
|
+
if "official_plugins" not in self._state:
|
|
78
|
+
self._state["official_plugins"] = {}
|
|
79
|
+
|
|
80
|
+
self._state["official_plugins"][plugin_name] = {
|
|
81
|
+
"state": "disabled",
|
|
82
|
+
"disabled_at": datetime.now().isoformat()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
self._save_state()
|
|
86
|
+
|
|
87
|
+
def get_plugin_state(self, plugin_name: str) -> dict[str, Any] | None:
|
|
88
|
+
"""
|
|
89
|
+
Get the state of a specific plugin.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
plugin_name: Name of the plugin
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Plugin state dict or None if no state exists
|
|
96
|
+
"""
|
|
97
|
+
return self._state.get("official_plugins", {}).get(plugin_name)
|
|
98
|
+
|
|
99
|
+
def get_all_states(self) -> dict[str, dict[str, Any]]:
|
|
100
|
+
"""
|
|
101
|
+
Get states of all plugins.
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
Dict mapping plugin names to their states
|
|
105
|
+
"""
|
|
106
|
+
return self._state.get("official_plugins", {})
|
|
107
|
+
|
|
108
|
+
def is_official_plugin(self, plugin_name: str) -> bool:
|
|
109
|
+
"""
|
|
110
|
+
Check if a plugin is an official plugin.
|
|
111
|
+
|
|
112
|
+
This is a helper that can be used to determine if a plugin
|
|
113
|
+
should be managed through the state system.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
plugin_name: Name of the plugin
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
True if the plugin is official
|
|
120
|
+
"""
|
|
121
|
+
# This will be checked against the OFFICIAL_PLUGINS list
|
|
122
|
+
# For now, return False - the actual check is done elsewhere
|
|
123
|
+
return False
|