better-notion 1.5.3__tar.gz → 1.5.5__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-1.5.3 → better_notion-1.5.5}/PKG-INFO +1 -1
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/pages.py +1 -1
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/page.py +4 -3
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/agents.py +118 -3
- better_notion-1.5.5/better_notion/utils/agents/metadata.py +185 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/workspace.py +49 -2
- {better_notion-1.5.3 → better_notion-1.5.5}/pyproject.toml +1 -1
- {better_notion-1.5.3 → better_notion-1.5.5}/.gitignore +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/LICENSE +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/README.md +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/client.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/collections/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/collections/blocks.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/collections/comments.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/collections/databases.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/collections/pages.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/collections/users.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/entities/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/entities/block.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/entities/comment.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/entities/database.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/entities/page.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/entities/user.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/errors.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/oauth.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/base.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/checkbox.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/date.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/email.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/number.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/phone.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/rich_text.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/select.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/title.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/properties/url.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/utils/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_api/utils/pagination.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/async_typer.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/auth.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/blocks.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/comments.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/config.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/databases.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/plugins.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/search.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/update.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/users.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/commands/workspace.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/config.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/display.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/errors.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/main.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/markdown.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/response.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_cli/utils/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/base/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/base/entity.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/cache/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/cache/cache.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/client.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/managers/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/managers/block_manager.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/managers/comment_manager.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/managers/database_manager.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/managers/page_manager.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/managers/user_manager.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/block.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/audio.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/bookmark.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/breadcrumb.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/bullet.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/callout.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/code.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/column.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/column_list.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/divider.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/embed.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/equation.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/file.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/heading.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/image.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/numbered.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/paragraph.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/pdf.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/quote.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/synced_block.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/table.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/table_row.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/template.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/todo.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/toggle.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/blocks/video.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/comment.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/database.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/models/user.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/parents/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/plugins.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/properties/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/properties/formula.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/properties/parsers.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/properties/relation.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/query/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/query/database_query.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/_sdk/query/filter_translator.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/base.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/loader.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/agents_cli.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/agents_sdk/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/agents_sdk/managers.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/agents_sdk/models.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/agents_sdk/plugin.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/official/productivity.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/plugins/state.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/auth.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/dependency_resolver.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/project_context.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/rbac.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/schemas.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/agents/state_machine.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/helpers.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/retry.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/better_notion/utils/validators.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/base/test_entity.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/cache/test_cache.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/models/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/models/blocks/test_advanced_blocks.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/models/test_block.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/models/test_database.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/models/test_database_bug.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/models/test_page.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/models/test_user.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/properties/test_formula.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/properties/test_parsers.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/properties/test_relation.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/query/test_database_query.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/query/test_filter_translator.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/test_client.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/_sdk/test_comment.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_auth.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_dependency_resolver.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_plugin.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_project_context.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_rbac.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_schemas.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_state_machine.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/agents/test_workspace.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_async_typer.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_config.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_display.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_errors.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_main.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_pages_commands.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_response.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/cli/test_update.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/conftest.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/integration/conftest.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/integration/test_blocks.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/integration/test_databases.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/integration/test_pages.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/integration/test_search.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/integration/test_users.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_agents_cli.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_agents_sdk_integration.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_agents_sdk_managers.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_agents_sdk_models.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_base.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_loader.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_marketplace.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_plugin_commands_state.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_productivity_plugin.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/plugins/test_state.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/sdk/__init__.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/sdk/test_plugin_system.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/unit/test_client.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/unit/test_collections.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/unit/test_entities.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/unit/test_errors.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/unit/test_helpers.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/unit/test_properties.py +0 -0
- {better_notion-1.5.3 → better_notion-1.5.5}/tests/utils/test_retry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: better-notion
|
|
3
|
-
Version: 1.5.
|
|
3
|
+
Version: 1.5.5
|
|
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
|
|
@@ -390,7 +390,7 @@ async def clear(page_id: str) -> None:
|
|
|
390
390
|
deleted_count = 0
|
|
391
391
|
for block_id in block_ids:
|
|
392
392
|
try:
|
|
393
|
-
await client.api.blocks
|
|
393
|
+
await client.api._request("DELETE", f"/blocks/{block_id}")
|
|
394
394
|
deleted_count += 1
|
|
395
395
|
except Exception:
|
|
396
396
|
# Continue even if deletion fails
|
|
@@ -522,9 +522,10 @@ class Page(BaseEntity):
|
|
|
522
522
|
if cursor:
|
|
523
523
|
params["start_cursor"] = cursor
|
|
524
524
|
|
|
525
|
-
return await self._client.api.
|
|
526
|
-
|
|
527
|
-
|
|
525
|
+
return await self._client.api._request(
|
|
526
|
+
"GET",
|
|
527
|
+
f"/blocks/{self.id}/children",
|
|
528
|
+
params=params
|
|
528
529
|
)
|
|
529
530
|
|
|
530
531
|
# Define item parser
|
|
@@ -86,6 +86,18 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
86
86
|
"-n",
|
|
87
87
|
help="Name for the workspace",
|
|
88
88
|
),
|
|
89
|
+
reset: bool = typer.Option(
|
|
90
|
+
False,
|
|
91
|
+
"--reset",
|
|
92
|
+
"-r",
|
|
93
|
+
help="Reset workspace (delete existing databases and recreate)",
|
|
94
|
+
),
|
|
95
|
+
skip: bool = typer.Option(
|
|
96
|
+
False,
|
|
97
|
+
"--skip",
|
|
98
|
+
"-s",
|
|
99
|
+
help="Skip if workspace already exists (return existing databases)",
|
|
100
|
+
),
|
|
89
101
|
debug: bool = typer.Option(
|
|
90
102
|
False,
|
|
91
103
|
"--debug",
|
|
@@ -106,8 +118,13 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
106
118
|
- Incidents
|
|
107
119
|
- Tags
|
|
108
120
|
|
|
121
|
+
If a workspace already exists in the page, use --reset to recreate
|
|
122
|
+
or --skip to keep the existing one.
|
|
123
|
+
|
|
109
124
|
Example:
|
|
110
125
|
$ notion agents init --parent-page page123 --name "My Workspace"
|
|
126
|
+
$ notion agents init --parent-page page123 --reset # Recreate
|
|
127
|
+
$ notion agents init --parent-page page123 --skip # Keep existing
|
|
111
128
|
"""
|
|
112
129
|
import asyncio
|
|
113
130
|
import logging
|
|
@@ -128,17 +145,40 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
128
145
|
client = get_client()
|
|
129
146
|
initializer = WorkspaceInitializer(client)
|
|
130
147
|
|
|
148
|
+
# Handle --skip flag
|
|
149
|
+
if skip:
|
|
150
|
+
from better_notion._sdk.models.page import Page
|
|
151
|
+
from better_notion.utils.agents.metadata import WorkspaceMetadata
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
page = await Page.get(parent_page_id, client=client)
|
|
155
|
+
existing = await WorkspaceMetadata.detect_workspace(page, client)
|
|
156
|
+
|
|
157
|
+
if existing:
|
|
158
|
+
database_ids = existing.get("database_ids", {})
|
|
159
|
+
return format_success(
|
|
160
|
+
{
|
|
161
|
+
"message": "Workspace already exists, skipping initialization",
|
|
162
|
+
"workspace_id": existing.get("workspace_id"),
|
|
163
|
+
"databases_found": len(database_ids),
|
|
164
|
+
"database_ids": database_ids,
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
except Exception:
|
|
168
|
+
# If detection fails, proceed with normal init
|
|
169
|
+
pass
|
|
170
|
+
|
|
171
|
+
# Initialize workspace (with skip_detection=True if --reset)
|
|
131
172
|
database_ids = await initializer.initialize_workspace(
|
|
132
173
|
parent_page_id=parent_page_id,
|
|
133
174
|
workspace_name=workspace_name,
|
|
175
|
+
skip_detection=reset, # Skip detection if resetting
|
|
134
176
|
)
|
|
135
177
|
|
|
136
|
-
# Save database IDs
|
|
137
|
-
initializer.save_database_ids()
|
|
138
|
-
|
|
139
178
|
return format_success(
|
|
140
179
|
{
|
|
141
180
|
"message": "Workspace initialized successfully",
|
|
181
|
+
"workspace_id": initializer._workspace_id,
|
|
142
182
|
"databases_created": len(database_ids),
|
|
143
183
|
"database_ids": database_ids,
|
|
144
184
|
}
|
|
@@ -151,6 +191,81 @@ class AgentsPlugin(CombinedPluginInterface):
|
|
|
151
191
|
result = asyncio.run(_init())
|
|
152
192
|
typer.echo(result)
|
|
153
193
|
|
|
194
|
+
@agents_app.command("info")
|
|
195
|
+
def workspace_info(
|
|
196
|
+
parent_page_id: str = typer.Option(
|
|
197
|
+
...,
|
|
198
|
+
"--parent-page",
|
|
199
|
+
"-p",
|
|
200
|
+
help="ID of the parent page to check",
|
|
201
|
+
),
|
|
202
|
+
) -> None:
|
|
203
|
+
"""
|
|
204
|
+
Show workspace information for a page.
|
|
205
|
+
|
|
206
|
+
Displays whether a workspace is initialized in the given page,
|
|
207
|
+
along with workspace metadata and database information.
|
|
208
|
+
|
|
209
|
+
Example:
|
|
210
|
+
$ notion agents info --parent-page page123
|
|
211
|
+
"""
|
|
212
|
+
import asyncio
|
|
213
|
+
|
|
214
|
+
async def _info() -> str:
|
|
215
|
+
try:
|
|
216
|
+
client = get_client()
|
|
217
|
+
from better_notion._sdk.models.page import Page
|
|
218
|
+
from better_notion.utils.agents.metadata import WorkspaceMetadata
|
|
219
|
+
|
|
220
|
+
page = await Page.get(parent_page_id, client=client)
|
|
221
|
+
existing = await WorkspaceMetadata.detect_workspace(page, client)
|
|
222
|
+
|
|
223
|
+
if existing:
|
|
224
|
+
database_ids = existing.get("database_ids", {})
|
|
225
|
+
|
|
226
|
+
return format_success(
|
|
227
|
+
{
|
|
228
|
+
"message": "Workspace found in this page",
|
|
229
|
+
"parent_page": parent_page_id,
|
|
230
|
+
"parent_title": page.title,
|
|
231
|
+
"workspace_id": existing.get("workspace_id"),
|
|
232
|
+
"workspace_name": existing.get("workspace_name"),
|
|
233
|
+
"initialized_at": existing.get("initialized_at"),
|
|
234
|
+
"databases_count": len(database_ids),
|
|
235
|
+
"database_ids": database_ids,
|
|
236
|
+
"detection_method": existing.get("detection_method", "config_file"),
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
else:
|
|
240
|
+
# Search for any databases in the page
|
|
241
|
+
results = await client.search(
|
|
242
|
+
query="",
|
|
243
|
+
filter={"value": "database", "property": "object"}
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Count databases in this page
|
|
247
|
+
databases_in_page = []
|
|
248
|
+
for result in results:
|
|
249
|
+
if hasattr(result, 'title'):
|
|
250
|
+
databases_in_page.append(result.title)
|
|
251
|
+
|
|
252
|
+
return format_success(
|
|
253
|
+
{
|
|
254
|
+
"message": "No agents workspace found in this page",
|
|
255
|
+
"parent_page": parent_page_id,
|
|
256
|
+
"parent_title": page.title,
|
|
257
|
+
"workspace_initialized": False,
|
|
258
|
+
"other_databases": len(databases_in_page),
|
|
259
|
+
}
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
except Exception as e:
|
|
263
|
+
result = format_error("INFO_ERROR", str(e), retry=False)
|
|
264
|
+
return result
|
|
265
|
+
|
|
266
|
+
result = asyncio.run(_info())
|
|
267
|
+
typer.echo(result)
|
|
268
|
+
|
|
154
269
|
@agents_app.command("init-project")
|
|
155
270
|
def init_project(
|
|
156
271
|
project_id: str = typer.Option(
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Workspace metadata management for agents plugin.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to manage workspace metadata,
|
|
4
|
+
including detecting duplicate workspaces and storing workspace information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import logging
|
|
9
|
+
from datetime import datetime, timezone
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
from uuid import uuid4
|
|
13
|
+
|
|
14
|
+
from better_notion._sdk.client import NotionClient
|
|
15
|
+
from better_notion._sdk.models.page import Page
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class WorkspaceMetadata:
|
|
21
|
+
"""Manage workspace metadata for duplicate detection."""
|
|
22
|
+
|
|
23
|
+
# Property names used in Notion page
|
|
24
|
+
PROP_WORKSPACE_ID = "agents_workspace_id"
|
|
25
|
+
PROP_WORKSPACE_NAME = "agents_workspace_name"
|
|
26
|
+
PROP_INITIALIZED_AT = "agents_workspace_created"
|
|
27
|
+
PROP_VERSION = "agents_workspace_version"
|
|
28
|
+
PROP_DATABASE_IDS = "agents_workspace_databases"
|
|
29
|
+
|
|
30
|
+
@staticmethod
|
|
31
|
+
def generate_workspace_id() -> str:
|
|
32
|
+
"""Generate a unique workspace ID.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Unique workspace ID (UUID without hyphens for compactness)
|
|
36
|
+
"""
|
|
37
|
+
return str(uuid4()).replace("-", "")
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def extract_metadata_from_page(page: Page) -> dict[str, Any]:
|
|
41
|
+
"""Extract workspace metadata from page properties.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
page: Page object to extract metadata from
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Dict with workspace metadata (empty if not initialized)
|
|
48
|
+
|
|
49
|
+
Example:
|
|
50
|
+
>>> metadata = WorkspaceMetadata.extract_metadata_from_page(page)
|
|
51
|
+
>>> if metadata.get("workspace_id"):
|
|
52
|
+
... print(f"Workspace: {metadata['workspace_name']}")
|
|
53
|
+
"""
|
|
54
|
+
metadata = {}
|
|
55
|
+
|
|
56
|
+
# Try to get workspace ID from icon (stored as emoji)
|
|
57
|
+
# Notion doesn't support custom properties, so we use creative approaches
|
|
58
|
+
|
|
59
|
+
# Check if page icon contains our metadata marker
|
|
60
|
+
icon = page.icon
|
|
61
|
+
if icon and icon.startswith("🤖"):
|
|
62
|
+
metadata["is_agents_workspace"] = True
|
|
63
|
+
# Could encode data in icon, but keep it simple for now
|
|
64
|
+
|
|
65
|
+
return metadata
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
async def detect_workspace(
|
|
69
|
+
page: Page,
|
|
70
|
+
client: NotionClient
|
|
71
|
+
) -> dict[str, Any] | None:
|
|
72
|
+
"""Detect if page already has an agents workspace.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
page: Parent page to check
|
|
76
|
+
client: NotionClient instance
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
Workspace metadata dict if found, None otherwise
|
|
80
|
+
"""
|
|
81
|
+
# Primary detection: Scan for databases with expected names
|
|
82
|
+
expected_databases = [
|
|
83
|
+
"Organizations",
|
|
84
|
+
"Tags",
|
|
85
|
+
"Projects",
|
|
86
|
+
"Versions",
|
|
87
|
+
"Tasks",
|
|
88
|
+
"Ideas",
|
|
89
|
+
"Work Issues",
|
|
90
|
+
"Incidents"
|
|
91
|
+
]
|
|
92
|
+
|
|
93
|
+
try:
|
|
94
|
+
# Search for databases in this page
|
|
95
|
+
results = await client.search(
|
|
96
|
+
query="",
|
|
97
|
+
filter={"value": "database", "property": "object"}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
databases_found = []
|
|
101
|
+
database_ids = {}
|
|
102
|
+
|
|
103
|
+
for result in results:
|
|
104
|
+
if hasattr(result, 'title') and result.title in expected_databases:
|
|
105
|
+
databases_found.append(result.title)
|
|
106
|
+
# Extract the key name (lowercase with underscores)
|
|
107
|
+
key = result.title.lower().replace(" ", "_")
|
|
108
|
+
database_ids[key] = result.id
|
|
109
|
+
|
|
110
|
+
# Check if we have at least 5 of the expected databases
|
|
111
|
+
matches = len(databases_found)
|
|
112
|
+
if matches >= 5:
|
|
113
|
+
logger.info(f"Detected existing workspace with {matches}/{len(expected_databases)} databases in page {page.id}")
|
|
114
|
+
|
|
115
|
+
# Try to load workspace metadata from local config to get workspace_id
|
|
116
|
+
workspace_id = None
|
|
117
|
+
workspace_name = None
|
|
118
|
+
initialized_at = None
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
config_path = Path.home() / ".notion" / "workspace.json"
|
|
122
|
+
if config_path.exists():
|
|
123
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
124
|
+
config = json.load(f)
|
|
125
|
+
# Only use config if it matches this page
|
|
126
|
+
if config.get("parent_page") == page.id:
|
|
127
|
+
workspace_id = config.get("workspace_id")
|
|
128
|
+
workspace_name = config.get("workspace_name")
|
|
129
|
+
initialized_at = config.get("initialized_at")
|
|
130
|
+
logger.info(f"Config file matches this page, workspace_id: {workspace_id}")
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.debug(f"Could not load local config: {e}")
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
"workspace_id": workspace_id,
|
|
136
|
+
"workspace_name": workspace_name,
|
|
137
|
+
"initialized_at": initialized_at,
|
|
138
|
+
"database_ids": database_ids,
|
|
139
|
+
"detection_method": "database_scan",
|
|
140
|
+
"databases_count": matches
|
|
141
|
+
}
|
|
142
|
+
except Exception as e:
|
|
143
|
+
logger.debug(f"Could not scan for databases: {e}")
|
|
144
|
+
|
|
145
|
+
return None
|
|
146
|
+
|
|
147
|
+
@staticmethod
|
|
148
|
+
def save_workspace_config(
|
|
149
|
+
page_id: str,
|
|
150
|
+
workspace_id: str,
|
|
151
|
+
workspace_name: str,
|
|
152
|
+
database_ids: dict[str, str],
|
|
153
|
+
path: Optional[Path] = None
|
|
154
|
+
) -> Path:
|
|
155
|
+
"""Save workspace configuration to local file.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
page_id: Parent page ID
|
|
159
|
+
workspace_id: Unique workspace ID
|
|
160
|
+
workspace_name: Workspace name
|
|
161
|
+
database_ids: Dict of database name to ID
|
|
162
|
+
path: Optional custom path for config file
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Path to saved config file
|
|
166
|
+
"""
|
|
167
|
+
if path is None:
|
|
168
|
+
path = Path.home() / ".notion" / "workspace.json"
|
|
169
|
+
|
|
170
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
171
|
+
|
|
172
|
+
config = {
|
|
173
|
+
"workspace_id": workspace_id,
|
|
174
|
+
"workspace_name": workspace_name,
|
|
175
|
+
"parent_page": page_id,
|
|
176
|
+
"initialized_at": datetime.now(timezone.utc).isoformat(),
|
|
177
|
+
"version": "1.5.4", # Track version for migrations
|
|
178
|
+
"database_ids": database_ids
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
with open(path, "w", encoding="utf-8") as f:
|
|
182
|
+
json.dump(config, f, indent=2)
|
|
183
|
+
|
|
184
|
+
logger.info(f"Saved workspace config to {path}")
|
|
185
|
+
return path
|
|
@@ -6,12 +6,14 @@ required databases for the workflow management system.
|
|
|
6
6
|
|
|
7
7
|
import json
|
|
8
8
|
import logging
|
|
9
|
+
from datetime import datetime, timezone
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
from typing import Any, Dict, Optional
|
|
11
12
|
|
|
12
13
|
from better_notion._cli.config import Config
|
|
13
14
|
from better_notion._sdk.client import NotionClient
|
|
14
15
|
from better_notion._sdk.models.page import Page
|
|
16
|
+
from better_notion.utils.agents.metadata import WorkspaceMetadata
|
|
15
17
|
from better_notion.utils.agents.schemas import (
|
|
16
18
|
IncidentSchema,
|
|
17
19
|
IdeaSchema,
|
|
@@ -48,11 +50,15 @@ class WorkspaceInitializer:
|
|
|
48
50
|
"""
|
|
49
51
|
self._client = client
|
|
50
52
|
self._database_ids: Dict[str, str] = {}
|
|
53
|
+
self._workspace_id: Optional[str] = None
|
|
54
|
+
self._parent_page_id: Optional[str] = None
|
|
55
|
+
self._workspace_name: Optional[str] = None
|
|
51
56
|
|
|
52
57
|
async def initialize_workspace(
|
|
53
58
|
self,
|
|
54
59
|
parent_page_id: str,
|
|
55
60
|
workspace_name: str = "Agents Workspace",
|
|
61
|
+
skip_detection: bool = False,
|
|
56
62
|
) -> Dict[str, str]:
|
|
57
63
|
"""Initialize a complete workspace with all databases.
|
|
58
64
|
|
|
@@ -62,15 +68,22 @@ class WorkspaceInitializer:
|
|
|
62
68
|
Args:
|
|
63
69
|
parent_page_id: ID of the parent page where databases will be created
|
|
64
70
|
workspace_name: Name for the workspace (used for database titles)
|
|
71
|
+
skip_detection: If True, skip duplicate detection (for reset operation)
|
|
65
72
|
|
|
66
73
|
Returns:
|
|
67
74
|
Dict mapping database names to their IDs
|
|
68
75
|
|
|
69
76
|
Raises:
|
|
70
77
|
Exception: If database creation fails with detailed error message
|
|
78
|
+
Exception: If workspace already exists (and skip_detection is False)
|
|
71
79
|
"""
|
|
72
80
|
logger.info(f"Initializing workspace '{workspace_name}' in page {parent_page_id}")
|
|
73
81
|
|
|
82
|
+
# Store workspace info
|
|
83
|
+
self._parent_page_id = parent_page_id
|
|
84
|
+
self._workspace_name = workspace_name
|
|
85
|
+
self._workspace_id = WorkspaceMetadata.generate_workspace_id()
|
|
86
|
+
|
|
74
87
|
# Get parent page
|
|
75
88
|
try:
|
|
76
89
|
parent = await Page.get(parent_page_id, client=self._client)
|
|
@@ -80,6 +93,26 @@ class WorkspaceInitializer:
|
|
|
80
93
|
logger.error(error_msg)
|
|
81
94
|
raise Exception(error_msg) from e
|
|
82
95
|
|
|
96
|
+
# Check for existing workspace unless skipped
|
|
97
|
+
if not skip_detection:
|
|
98
|
+
existing = await WorkspaceMetadata.detect_workspace(parent, self._client)
|
|
99
|
+
if existing:
|
|
100
|
+
workspace_info = {
|
|
101
|
+
"workspace_id": existing.get("workspace_id", "unknown"),
|
|
102
|
+
"workspace_name": existing.get("workspace_name", workspace_name),
|
|
103
|
+
"initialized_at": existing.get("initialized_at", "unknown"),
|
|
104
|
+
"databases": existing.get("database_ids", {})
|
|
105
|
+
}
|
|
106
|
+
error_msg = (
|
|
107
|
+
f"Workspace already initialized in this page\n"
|
|
108
|
+
f"Workspace ID: {workspace_info['workspace_id']}\n"
|
|
109
|
+
f"Initialized: {workspace_info['initialized_at']}\n"
|
|
110
|
+
f"Databases: {len(workspace_info.get('databases', {}))} created\n"
|
|
111
|
+
f"Use --reset to reinitialize or --skip to keep existing"
|
|
112
|
+
)
|
|
113
|
+
logger.error(error_msg)
|
|
114
|
+
raise Exception(error_msg) from None
|
|
115
|
+
|
|
83
116
|
# Create databases in order (independent first, then dependent)
|
|
84
117
|
self._database_ids = {}
|
|
85
118
|
databases_order = [
|
|
@@ -108,7 +141,11 @@ class WorkspaceInitializer:
|
|
|
108
141
|
logger.error(error_msg)
|
|
109
142
|
raise Exception(error_msg) from e
|
|
110
143
|
|
|
144
|
+
# Save workspace metadata
|
|
145
|
+
self.save_database_ids()
|
|
146
|
+
|
|
111
147
|
logger.info(f"Workspace initialization complete. Created {len(self._database_ids)} databases")
|
|
148
|
+
logger.info(f"Workspace ID: {self._workspace_id}")
|
|
112
149
|
return self._database_ids
|
|
113
150
|
|
|
114
151
|
async def _create_organizations_db(self, parent: Page) -> None:
|
|
@@ -312,7 +349,7 @@ class WorkspaceInitializer:
|
|
|
312
349
|
pass
|
|
313
350
|
|
|
314
351
|
def save_database_ids(self, path: Optional[Path] = None) -> None:
|
|
315
|
-
"""Save database IDs to
|
|
352
|
+
"""Save workspace metadata (database IDs and workspace info) to config file.
|
|
316
353
|
|
|
317
354
|
Args:
|
|
318
355
|
path: Path to save config file (default: ~/.notion/workspace.json)
|
|
@@ -322,8 +359,18 @@ class WorkspaceInitializer:
|
|
|
322
359
|
|
|
323
360
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
324
361
|
|
|
362
|
+
# Save full workspace metadata
|
|
363
|
+
config = {
|
|
364
|
+
"workspace_id": self._workspace_id,
|
|
365
|
+
"workspace_name": self._workspace_name,
|
|
366
|
+
"parent_page": self._parent_page_id,
|
|
367
|
+
"initialized_at": datetime.now(timezone.utc).isoformat(),
|
|
368
|
+
"version": "1.5.4",
|
|
369
|
+
"database_ids": self._database_ids
|
|
370
|
+
}
|
|
371
|
+
|
|
325
372
|
with open(path, "w", encoding="utf-8") as f:
|
|
326
|
-
json.dump(
|
|
373
|
+
json.dump(config, f, indent=2)
|
|
327
374
|
|
|
328
375
|
@classmethod
|
|
329
376
|
def load_database_ids(cls, path: Optional[Path] = None) -> Dict[str, str]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|