better-notion 1.4.0__py3-none-any.whl → 1.5.0__py3-none-any.whl

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.
@@ -15,7 +15,6 @@ Features:
15
15
 
16
16
  from __future__ import annotations
17
17
 
18
- import asyncio
19
18
  from pathlib import Path
20
19
  from typing import Optional
21
20
 
@@ -74,7 +73,7 @@ class AgentsPlugin(CombinedPluginInterface):
74
73
 
75
74
  # Register sub-commands
76
75
  @agents_app.command("init")
77
- def init_workspace(
76
+ async def init_workspace(
78
77
  parent_page_id: str = typer.Option(
79
78
  ...,
80
79
  "--parent-page",
@@ -87,6 +86,12 @@ class AgentsPlugin(CombinedPluginInterface):
87
86
  "-n",
88
87
  help="Name for the workspace",
89
88
  ),
89
+ debug: bool = typer.Option(
90
+ False,
91
+ "--debug",
92
+ "-d",
93
+ help="Enable debug logging",
94
+ ),
90
95
  ) -> None:
91
96
  """
92
97
  Initialize a new workspace with all required databases.
@@ -104,32 +109,44 @@ class AgentsPlugin(CombinedPluginInterface):
104
109
  Example:
105
110
  $ notion agents init --parent-page page123 --name "My Workspace"
106
111
  """
107
- async def _init() -> str:
108
- try:
109
- client = get_client()
110
- initializer = WorkspaceInitializer(client)
111
-
112
- database_ids = await initializer.initialize_workspace(
113
- parent_page_id=parent_page_id,
114
- workspace_name=workspace_name,
115
- )
112
+ import logging
113
+ import sys
114
+
115
+ # Enable debug logging if requested
116
+ if debug:
117
+ logging.basicConfig(
118
+ level=logging.DEBUG,
119
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
120
+ stream=sys.stderr,
121
+ )
122
+ # Also enable httpx debug logging
123
+ logging.getLogger("httpx").setLevel(logging.DEBUG)
116
124
 
117
- # Save database IDs
118
- initializer.save_database_ids()
125
+ try:
126
+ client = get_client()
127
+ initializer = WorkspaceInitializer(client)
119
128
 
120
- return format_success(
121
- {
122
- "message": "Workspace initialized successfully",
123
- "databases_created": len(database_ids),
124
- "database_ids": database_ids,
125
- }
126
- )
129
+ database_ids = await initializer.initialize_workspace(
130
+ parent_page_id=parent_page_id,
131
+ workspace_name=workspace_name,
132
+ )
133
+
134
+ # Save database IDs
135
+ initializer.save_database_ids()
127
136
 
128
- except Exception as e:
129
- return format_error("INIT_ERROR", str(e), retry=False)
137
+ result = format_success(
138
+ {
139
+ "message": "Workspace initialized successfully",
140
+ "databases_created": len(database_ids),
141
+ "database_ids": database_ids,
142
+ }
143
+ )
144
+ typer.echo(result)
130
145
 
131
- result = asyncio.run(_init())
132
- typer.echo(result)
146
+ except Exception as e:
147
+ result = format_error("INIT_ERROR", str(e), retry=False)
148
+ typer.echo(result)
149
+ raise typer.Exit(code=1)
133
150
 
134
151
  @agents_app.command("init-project")
135
152
  def init_project(
@@ -672,6 +672,9 @@ def tasks_next(
672
672
  },
673
673
  })
674
674
 
675
+ except ValueError as e:
676
+ # Specific validation error (e.g., project not found)
677
+ return format_error("VALIDATION_ERROR", str(e), retry=False)
675
678
  except Exception as e:
676
679
  return format_error("FIND_NEXT_TASK_ERROR", str(e), retry=False)
677
680
 
@@ -767,20 +770,28 @@ def tasks_start(task_id: str) -> str:
767
770
 
768
771
  def tasks_complete(
769
772
  task_id: str,
770
- actual_hours: Optional[int] = typer.Option(None, "--actual-hours", "-a", help="Actual hours spent"),
773
+ actual_hours: Optional[int] = typer.Option(None, "--actual-hours", "-a", help="Actual hours spent (must be non-negative)"),
771
774
  ) -> str:
772
775
  """
773
776
  Complete a task (transition to Completed).
774
777
 
775
778
  Args:
776
779
  task_id: Task page ID
777
- actual_hours: Actual hours spent (optional)
780
+ actual_hours: Actual hours spent (optional, must be non-negative)
778
781
 
779
782
  Example:
780
783
  $ notion tasks complete task_123 --actual-hours 3
781
784
  """
782
785
  async def _complete() -> str:
783
786
  try:
787
+ # Validate actual_hours parameter if provided
788
+ if actual_hours is not None:
789
+ from better_notion.utils.validators import Validators, ValidationError
790
+ try:
791
+ Validators.non_negative_float(actual_hours, "actual_hours")
792
+ except ValidationError as e:
793
+ return format_error("INVALID_PARAMETER", str(e), retry=False)
794
+
784
795
  client = get_client()
785
796
 
786
797
  # Register SDK plugin
@@ -1047,13 +1058,20 @@ def ideas_review(count: int = 10) -> str:
1047
1058
  Get a batch of ideas for review, prioritized by effort.
1048
1059
 
1049
1060
  Args:
1050
- count: Maximum number of ideas to return
1061
+ count: Maximum number of ideas to return (must be positive)
1051
1062
 
1052
1063
  Example:
1053
1064
  $ notion ideas review --count 5
1054
1065
  """
1055
1066
  async def _review() -> str:
1056
1067
  try:
1068
+ # Validate count parameter
1069
+ from better_notion.utils.validators import Validators, ValidationError
1070
+ try:
1071
+ Validators.positive_int(count, "count")
1072
+ except ValidationError as e:
1073
+ return format_error("INVALID_PARAMETER", str(e), retry=False)
1074
+
1057
1075
  client = get_client()
1058
1076
 
1059
1077
  # Register SDK plugin
@@ -1623,13 +1641,20 @@ def incidents_mttr(project_id: Optional[str] = None, within_days: int = 30) -> s
1623
1641
 
1624
1642
  Args:
1625
1643
  project_id: Filter by project ID (optional)
1626
- within_days: Only consider incidents from last N days
1644
+ within_days: Only consider incidents from last N days (must be positive)
1627
1645
 
1628
1646
  Example:
1629
1647
  $ notion incidents mttr --project-id proj_123 --within-days 30
1630
1648
  """
1631
1649
  async def _mttr() -> str:
1632
1650
  try:
1651
+ # Validate within_days parameter
1652
+ from better_notion.utils.validators import Validators, ValidationError
1653
+ try:
1654
+ Validators.positive_int(within_days, "within_days")
1655
+ except ValidationError as e:
1656
+ return format_error("INVALID_PARAMETER", str(e), retry=False)
1657
+
1633
1658
  client = get_client()
1634
1659
 
1635
1660
  # Register SDK plugin
@@ -400,13 +400,41 @@ class TaskManager:
400
400
 
401
401
  Returns:
402
402
  Task instance or None if no tasks available
403
+
404
+ Raises:
405
+ ValueError: If project_id is provided but project doesn't exist
403
406
  """
404
- from better_notion.plugins.official.agents_sdk.models import Task
407
+ from better_notion.plugins.official.agents_sdk.models import Task, Project
405
408
 
406
409
  database_id = self._get_database_id("Tasks")
407
410
  if not database_id:
408
411
  return None
409
412
 
413
+ # Validate project_id if provided
414
+ if project_id:
415
+ # First check if Projects database exists in workspace config
416
+ projects_db = self._get_database_id("Projects")
417
+ if not projects_db:
418
+ # Workspace not initialized, can't validate project
419
+ pass
420
+ else:
421
+ # Check if project exists by querying for it
422
+ try:
423
+ project_response = await self._client._api.databases.query(
424
+ database_id=projects_db,
425
+ filter={"property": "id", "rich_text": {"equals": project_id}}
426
+ )
427
+ # If no results, project doesn't exist
428
+ if not project_response.get("results"):
429
+ raise ValueError(
430
+ f"Project '{project_id}' not found. "
431
+ f"Please verify the project ID or run 'notion agents projects list' to see available projects."
432
+ )
433
+ except Exception as e:
434
+ if "not found" in str(e).lower():
435
+ raise
436
+ # If query fails for other reasons, continue without validation
437
+
410
438
  # Filter for backlog/claimed tasks
411
439
  response = await self._client._api.databases.query(
412
440
  database_id=database_id,
@@ -5,6 +5,7 @@ required databases for the workflow management system.
5
5
  """
6
6
 
7
7
  import json
8
+ import logging
8
9
  from pathlib import Path
9
10
  from typing import Any, Dict, Optional
10
11
 
@@ -22,6 +23,8 @@ from better_notion.utils.agents.schemas import (
22
23
  WorkIssueSchema,
23
24
  )
24
25
 
26
+ logger = logging.getLogger(__name__)
27
+
25
28
 
26
29
  class WorkspaceInitializer:
27
30
  """Initialize a new workspace with workflow databases.
@@ -64,38 +67,48 @@ class WorkspaceInitializer:
64
67
  Dict mapping database names to their IDs
65
68
 
66
69
  Raises:
67
- Exception: If database creation fails
70
+ Exception: If database creation fails with detailed error message
68
71
  """
72
+ logger.info(f"Initializing workspace '{workspace_name}' in page {parent_page_id}")
73
+
69
74
  # Get parent page
70
- parent = await Page.get(parent_page_id, client=self._client)
75
+ try:
76
+ parent = await Page.get(parent_page_id, client=self._client)
77
+ logger.info(f"Parent page found: {parent.id}")
78
+ except Exception as e:
79
+ error_msg = f"Failed to get parent page '{parent_page_id}': {str(e)}"
80
+ logger.error(error_msg)
81
+ raise Exception(error_msg) from e
71
82
 
72
83
  # Create databases in order (independent first, then dependent)
73
84
  self._database_ids = {}
74
-
75
- # 1. Organizations (independent)
76
- await self._create_organizations_db(parent)
77
-
78
- # 2. Tags (independent)
79
- await self._create_tags_db(parent)
80
-
81
- # 3. Projects (depends on Organizations)
82
- await self._create_projects_db(parent)
83
-
84
- # 4. Versions (depends on Projects)
85
- await self._create_versions_db(parent)
86
-
87
- # 5. Tasks (depends on Versions)
88
- await self._create_tasks_db(parent)
89
-
90
- # 6. Ideas (depends on Projects)
91
- await self._create_ideas_db(parent)
92
-
93
- # 7. Work Issues (depends on Projects, Tasks, Ideas)
94
- await self._create_work_issues_db(parent)
95
-
96
- # 8. Incidents (depends on Projects, Versions, Tasks)
97
- await self._create_incidents_db(parent)
98
-
85
+ databases_order = [
86
+ ("Organizations", "organizations", self._create_organizations_db),
87
+ ("Tags", "tags", self._create_tags_db),
88
+ ("Projects", "projects", self._create_projects_db),
89
+ ("Versions", "versions", self._create_versions_db),
90
+ ("Tasks", "tasks", self._create_tasks_db),
91
+ ("Ideas", "ideas", self._create_ideas_db),
92
+ ("Work Issues", "work_issues", self._create_work_issues_db),
93
+ ("Incidents", "incidents", self._create_incidents_db),
94
+ ]
95
+
96
+ for display_name, key, create_func in databases_order:
97
+ try:
98
+ logger.info(f"Creating {display_name} database...")
99
+ await create_func(parent)
100
+ logger.info(f"✓ {display_name} database created: {self._database_ids[key]}")
101
+ except Exception as e:
102
+ error_msg = (
103
+ f"Failed to create {display_name} database: {str(e)}\n"
104
+ f"Databases created so far: {list(self._database_ids.keys())}\n"
105
+ f"Parent page: {parent_page_id}\n"
106
+ f"Workspace name: {workspace_name}"
107
+ )
108
+ logger.error(error_msg)
109
+ raise Exception(error_msg) from e
110
+
111
+ logger.info(f"Workspace initialization complete. Created {len(self._database_ids)} databases")
99
112
  return self._database_ids
100
113
 
101
114
  async def _create_organizations_db(self, parent: Page) -> None:
@@ -0,0 +1,132 @@
1
+ """Validation helpers for CLI commands.
2
+
3
+ This module provides reusable validation functions for CLI parameters.
4
+ """
5
+
6
+
7
+ class ValidationError(Exception):
8
+ """Custom exception for validation errors."""
9
+
10
+ def __init__(self, message: str):
11
+ self.message = message
12
+ super().__init__(self.message)
13
+
14
+
15
+ class Validators:
16
+ """Reusable validation functions for CLI parameters."""
17
+
18
+ @staticmethod
19
+ def positive_int(value: int, name: str = "value") -> None:
20
+ """
21
+ Validate that a value is a positive integer.
22
+
23
+ Args:
24
+ value: The value to validate
25
+ name: Parameter name for error messages
26
+
27
+ Raises:
28
+ ValidationError: If value is not positive
29
+ """
30
+ if not isinstance(value, int):
31
+ raise ValidationError(f"{name} must be an integer (got: {type(value).__name__})")
32
+ if value <= 0:
33
+ raise ValidationError(f"{name} must be positive (greater than 0, got: {value})")
34
+
35
+ @staticmethod
36
+ def non_negative_int(value: int, name: str = "value") -> None:
37
+ """
38
+ Validate that a value is a non-negative integer.
39
+
40
+ Args:
41
+ value: The value to validate
42
+ name: Parameter name for error messages
43
+
44
+ Raises:
45
+ ValidationError: If value is negative
46
+ """
47
+ if not isinstance(value, int):
48
+ raise ValidationError(f"{name} must be an integer (got: {type(value).__name__})")
49
+ if value < 0:
50
+ raise ValidationError(f"{name} must be non-negative (0 or greater, got: {value})")
51
+
52
+ @staticmethod
53
+ def non_negative_float(value: float, name: str = "value") -> None:
54
+ """
55
+ Validate that a value is a non-negative float.
56
+
57
+ Args:
58
+ value: The value to validate
59
+ name: Parameter name for error messages
60
+
61
+ Raises:
62
+ ValidationError: If value is negative
63
+ """
64
+ if not isinstance(value, (int, float)):
65
+ raise ValidationError(f"{name} must be a number (got: {type(value).__name__})")
66
+ if value < 0:
67
+ raise ValidationError(f"{name} must be non-negative (0 or greater, got: {value})")
68
+
69
+ @staticmethod
70
+ def enum(value: str, name: str, allowed: list, case_sensitive: bool = False) -> None:
71
+ """
72
+ Validate that a value is in the allowed list.
73
+
74
+ Args:
75
+ value: The value to validate
76
+ name: Parameter name for error messages
77
+ allowed: List of allowed values
78
+ case_sensitive: Whether comparison should be case-sensitive
79
+
80
+ Raises:
81
+ ValidationError: If value is not in allowed list
82
+ """
83
+ if not case_sensitive:
84
+ value_lower = value.lower()
85
+ allowed_lower = [a.lower() for a in allowed]
86
+ if value_lower not in allowed_lower:
87
+ raise ValidationError(
88
+ f"{name} must be one of {allowed} (got: {value})"
89
+ )
90
+ else:
91
+ if value not in allowed:
92
+ raise ValidationError(
93
+ f"{name} must be one of {allowed} (got: {value})"
94
+ )
95
+
96
+ @staticmethod
97
+ def range_int(value: int, name: str, min_val: int, max_val: int) -> None:
98
+ """
99
+ Validate that a value is within a specified range.
100
+
101
+ Args:
102
+ value: The value to validate
103
+ name: Parameter name for error messages
104
+ min_val: Minimum allowed value (inclusive)
105
+ max_val: Maximum allowed value (inclusive)
106
+
107
+ Raises:
108
+ ValidationError: If value is outside the range
109
+ """
110
+ if not isinstance(value, int):
111
+ raise ValidationError(f"{name} must be an integer (got: {type(value).__name__})")
112
+ if value < min_val or value > max_val:
113
+ raise ValidationError(
114
+ f"{name} must be between {min_val} and {max_val} (got: {value})"
115
+ )
116
+
117
+ @staticmethod
118
+ def required(value: Any, name: str) -> None:
119
+ """
120
+ Validate that a value is not None or empty.
121
+
122
+ Args:
123
+ value: The value to validate
124
+ name: Parameter name for error messages
125
+
126
+ Raises:
127
+ ValidationError: If value is None or empty
128
+ """
129
+ if value is None:
130
+ raise ValidationError(f"{name} is required")
131
+ if isinstance(value, str) and not value.strip():
132
+ raise ValidationError(f"{name} cannot be empty")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: better-notion
3
- Version: 1.4.0
3
+ Version: 1.5.0
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
@@ -41,8 +41,8 @@ better_notion/_cli/commands/auth.py,sha256=d1-g8SKCrC8Di4yM36hafVlF9YZQ9pypRYU9I
41
41
  better_notion/_cli/commands/blocks.py,sha256=ErcR-SHhPoz6B9uy0xOG6CGZB-bhvny5lxBYufmYHbw,15711
42
42
  better_notion/_cli/commands/comments.py,sha256=dBdxvpkjk_yA_1WUivXl1Nt8Ig-uYD5-k4XtenHU_cI,4981
43
43
  better_notion/_cli/commands/config.py,sha256=3bOCmcRnpTb5SHB2TJPXdxrRshy84nZRAsULxfbSqk0,5360
44
- better_notion/_cli/commands/databases.py,sha256=05NR4BQBaxXedl1MBemwlXPA1Q7loWc36t56AGAofdQ,8320
45
- better_notion/_cli/commands/pages.py,sha256=Tt99IhQthAUjFVc6KkLmvubfYgW84RwCvcjt4gNXqdA,14280
44
+ better_notion/_cli/commands/databases.py,sha256=SRfUi74Cm7YVvdwPjbcwP1g__DiZsMyY0YLQTY-skx4,12953
45
+ better_notion/_cli/commands/pages.py,sha256=NZwrWDOim43FzIdtqubbXarsflnjQo2T4BGB1TJsco0,13068
46
46
  better_notion/_cli/commands/plugins.py,sha256=mBYSVciwwgnCRXYOuJfSYDV2UMAr_9P8KwPuiC-pY2A,27067
47
47
  better_notion/_cli/commands/search.py,sha256=JM20W5TiohsOKLyEUABEaWr0yB82W2_qAGQ1w64_pGk,4671
48
48
  better_notion/_cli/commands/update.py,sha256=NfmijzTpCbVTEY2h0QdcJZXEiGmWkk-iyEtZCqrgmAk,4997
@@ -107,16 +107,17 @@ better_notion/plugins/base.py,sha256=3h9jOZzS--UqmVW3RREtcQ2h1GTWWPUryTencsJKhTM
107
107
  better_notion/plugins/loader.py,sha256=zCWsMdJyvZs1IHFm0zjEiqm_l_5jB1Uw4x30Kq8rLS4,9527
108
108
  better_notion/plugins/state.py,sha256=jH_tZWvC35hqLO4qwl2Kwq9ziWVavwCEUcCqy3s5wMY,3780
109
109
  better_notion/plugins/official/__init__.py,sha256=rPg5vdk1cEANVstMPzxcWmImtsOpdSR40JSml7h1uUk,426
110
- better_notion/plugins/official/agents.py,sha256=UF9zn7DNBX-3_bmpjrHhbNd4dZXQIGa5ljhCC71SRcA,25939
111
- better_notion/plugins/official/agents_cli.py,sha256=v2yem60ER6EVRt9u1f5xF3MOExQeQ4lGqGCQ6z7pV18,51706
110
+ better_notion/plugins/official/agents.py,sha256=f86Qg456tbebFdB7xvSobSFqqAvJBLKYocm41ItRmYk,26507
111
+ better_notion/plugins/official/agents_cli.py,sha256=8l6e1zJCAT4DdAO-QfdjK_vrrrik3pmrojwakE32ZNY,53048
112
112
  better_notion/plugins/official/productivity.py,sha256=_-whP4pYA4HufE1aUFbIdhrjU-O9njI7xUO_Id2M1J8,8726
113
113
  better_notion/plugins/official/agents_sdk/__init__.py,sha256=luQBzZLsJ7fC5U0jFu8dfzMviiXj2SBZXcTohM53wkQ,725
114
- better_notion/plugins/official/agents_sdk/managers.py,sha256=dkpGozJhKXvz8rFqc2tAWyL6MeVzFVcjGMC0Ed2Z1dQ,29813
114
+ better_notion/plugins/official/agents_sdk/managers.py,sha256=0zMZPu63zhdyqiudO2gKsmM3YOJh0nFAR9FrMlwkV2A,31186
115
115
  better_notion/plugins/official/agents_sdk/models.py,sha256=gwGw9t-Z17NxddzmRKWiD9Noje86sxgPNlLJGARI0KA,75448
116
116
  better_notion/plugins/official/agents_sdk/plugin.py,sha256=bs9O8Unv6SARGj4lBU5Gj9HGbLTUNqTacJ3RLUhdbI4,4479
117
117
  better_notion/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
118
118
  better_notion/utils/helpers.py,sha256=HgFuUQlG_HzBOB0z2GA9RxPLoXgwRc0DIxa9Fg6C-Jk,2337
119
119
  better_notion/utils/retry.py,sha256=9WJDNitiIfVTL18hIlipvOKn41ukePrOwtAwx-LevpQ,7479
120
+ better_notion/utils/validators.py,sha256=RyAGcYJexKOq1YepF6insoBu4QEOVPoAMWqi8Mt_5Xk,4583
120
121
  better_notion/utils/agents/__init__.py,sha256=Zu32q0abbimrJY5dczjWNEastE-IrtGPQjpxD4JS4IU,1715
121
122
  better_notion/utils/agents/auth.py,sha256=_SBcqBjXmX8CJMCPpRWM-UuaDg7-OOtMWbhnYEiIBTs,6568
122
123
  better_notion/utils/agents/dependency_resolver.py,sha256=PfHHDIQztGih4LwylMb0_MyhDFbOYPjvUxcxY52mSEs,12033
@@ -124,9 +125,9 @@ better_notion/utils/agents/project_context.py,sha256=aJlzy5H2rL4sAfW2jHL_3K2VkBJ
124
125
  better_notion/utils/agents/rbac.py,sha256=8ZA8Y7wbOiVZDbpjpH7iC35SZrZ0jl4fcJ3xWCm3SsE,11820
125
126
  better_notion/utils/agents/schemas.py,sha256=e_lpGGO12FXtfqFyI91edj9xc5RUtuA6pU7Sk6ip7xg,21784
126
127
  better_notion/utils/agents/state_machine.py,sha256=xUBEeDTbU1Xq-rsRo2sbr6AUYpYrV9DTHOtZT2cWES8,6699
127
- better_notion/utils/agents/workspace.py,sha256=T8mP_DNIWhI1-k6UydJINsEJ6kQxQ6_Pa44ul58k088,12370
128
- better_notion-1.4.0.dist-info/METADATA,sha256=WXldrn_zOf9rVjRSQJmWLLr_ixIdAOtZxGW2BhWNmZc,11096
129
- better_notion-1.4.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
130
- better_notion-1.4.0.dist-info/entry_points.txt,sha256=D0bUcP7Z00Zyjxw7r2p29T95UrwioDO0aGDoHe9I6fo,55
131
- better_notion-1.4.0.dist-info/licenses/LICENSE,sha256=BAdN3JpgMY_y_fWqZSCFSvSbC2mTHP-BKDAzF5FXQAI,1069
132
- better_notion-1.4.0.dist-info/RECORD,,
128
+ better_notion/utils/agents/workspace.py,sha256=qsbVSFNtmpH87azvwlGShlloNVo9o2dhpohhtP9u8r8,13454
129
+ better_notion-1.5.0.dist-info/METADATA,sha256=N0p6fb7GbSnvgzixdJnPkMAo557uXqFLaHBKHP0Rpgc,11096
130
+ better_notion-1.5.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
131
+ better_notion-1.5.0.dist-info/entry_points.txt,sha256=D0bUcP7Z00Zyjxw7r2p29T95UrwioDO0aGDoHe9I6fo,55
132
+ better_notion-1.5.0.dist-info/licenses/LICENSE,sha256=BAdN3JpgMY_y_fWqZSCFSvSbC2mTHP-BKDAzF5FXQAI,1069
133
+ better_notion-1.5.0.dist-info/RECORD,,