tinybird 0.0.1.dev272__py3-none-any.whl → 0.0.1.dev274__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.

Potentially problematic release.


This version of tinybird might be problematic. Click here for more details.

tinybird/tb/__cli__.py CHANGED
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/forward/commands'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '0.0.1.dev272'
8
- __revision__ = '6fc56e0'
7
+ __version__ = '0.0.1.dev274'
8
+ __revision__ = 'c3d62b9'
@@ -37,7 +37,7 @@ from tinybird.tb.modules.agent.testing_agent import TestingAgent
37
37
  from tinybird.tb.modules.agent.tools.analyze import analyze_file, analyze_url
38
38
  from tinybird.tb.modules.agent.tools.append import append_file, append_url
39
39
  from tinybird.tb.modules.agent.tools.build import build
40
- from tinybird.tb.modules.agent.tools.create_datafile import create_datafile, rename_datafile_or_fixture
40
+ from tinybird.tb.modules.agent.tools.datafile import create_datafile, remove_file, rename_datafile_or_fixture
41
41
  from tinybird.tb.modules.agent.tools.deploy import deploy
42
42
  from tinybird.tb.modules.agent.tools.deploy_check import deploy_check
43
43
  from tinybird.tb.modules.agent.tools.diff_resource import diff_resource
@@ -48,7 +48,13 @@ from tinybird.tb.modules.agent.tools.plan import plan
48
48
  from tinybird.tb.modules.agent.tools.secret import create_or_update_secrets
49
49
  from tinybird.tb.modules.agent.utils import AgentRunCancelled, TinybirdAgentContext, show_confirmation, show_input
50
50
  from tinybird.tb.modules.build_common import process as build_process
51
- from tinybird.tb.modules.common import _analyze, _get_tb_client, echo_safe_humanfriendly_tables_format_pretty_table
51
+ from tinybird.tb.modules.common import (
52
+ _analyze,
53
+ _get_tb_client,
54
+ echo_safe_humanfriendly_tables_format_pretty_table,
55
+ get_region_from_host,
56
+ get_regions,
57
+ )
52
58
  from tinybird.tb.modules.config import CLIConfig
53
59
  from tinybird.tb.modules.deployment_common import create_deployment
54
60
  from tinybird.tb.modules.exceptions import CLIBuildException, CLIDeploymentException, CLIMockException
@@ -83,13 +89,15 @@ class TinybirdAgent:
83
89
  self.messages: list[ModelMessage] = get_last_messages_from_last_user_prompt()
84
90
  else:
85
91
  self.messages = []
86
-
92
+ cli_config = CLIConfig.get_project_config()
93
+ regions = get_regions(cli_config)
87
94
  self.agent = Agent(
88
95
  model=create_model(user_token, host, workspace_id),
89
96
  deps_type=TinybirdAgentContext,
90
97
  system_prompt=agent_system_prompt,
91
98
  tools=[
92
99
  Tool(create_datafile, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
100
+ Tool(remove_file, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
93
101
  Tool(
94
102
  rename_datafile_or_fixture,
95
103
  docstring_format="google",
@@ -206,7 +214,27 @@ class TinybirdAgent:
206
214
 
207
215
  @self.agent.instructions
208
216
  def get_cloud_host(ctx: RunContext[TinybirdAgentContext]) -> str:
209
- return f"Tinybird Cloud host: {ctx.deps.host}"
217
+ try:
218
+ region = get_region_from_host(ctx.deps.host, regions) or {
219
+ "provider": "Unknown",
220
+ "name": "Unknown",
221
+ }
222
+ except Exception as e:
223
+ click.echo(FeedbackManager.error(message=f"Error getting region info: {e}"))
224
+ region = {
225
+ "provider": "Unknown",
226
+ "name": "Unknown",
227
+ }
228
+
229
+ region_provider = region["provider"]
230
+ region_name = region["name"]
231
+ return f"""Tinybird Cloud info (region details):
232
+ - API Host: {ctx.deps.host}
233
+ - Workspace ID: {ctx.deps.workspace_id}
234
+ - Workspace Name: {project.workspace_name} (in Tinybird Local the workspace name is the same because it is synced with Cloud)
235
+ - Region provider: {region_provider}
236
+ - Region name: {region_name}
237
+ """
210
238
 
211
239
  @self.agent.instructions
212
240
  def get_local_token(ctx: RunContext[TinybirdAgentContext]) -> str:
@@ -533,7 +561,7 @@ def build_project_test(
533
561
  raise CLIBuildException(build_error)
534
562
 
535
563
 
536
- def deploy_project(config: dict[str, Any], project: Project) -> None:
564
+ def deploy_project(config: dict[str, Any], project: Project, allow_destructive_operations: bool = False) -> None:
537
565
  client = _get_tb_client(config["token"], config["host"])
538
566
  try:
539
567
  create_deployment(
@@ -542,7 +570,7 @@ def deploy_project(config: dict[str, Any], project: Project) -> None:
542
570
  config=config,
543
571
  wait=True,
544
572
  auto=True,
545
- allow_destructive_operations=False,
573
+ allow_destructive_operations=allow_destructive_operations,
546
574
  )
547
575
  except SystemExit as e:
548
576
  raise CLIDeploymentException(e.args[0])
@@ -676,9 +704,9 @@ def get_connection_datafile_local(config: dict[str, Any], connection_name: str)
676
704
  return "Connection not found"
677
705
 
678
706
 
679
- def run_tests(client: TinyB, project: Project, pipe_name: Optional[str] = None) -> None:
707
+ def run_tests(client: TinyB, project: Project, pipe_name: Optional[str] = None) -> Optional[str]:
680
708
  try:
681
- run_tests_common(name=(pipe_name,) if pipe_name else (), project=project, client=client)
709
+ return run_tests_common(name=(pipe_name,) if pipe_name else (), project=project, client=client)
682
710
  except SystemExit as e:
683
711
  raise Exception(e.args[0])
684
712
 
@@ -31,14 +31,17 @@ Steps:
31
31
  8. Copy: [name] - [description] - Depends on: [resources]
32
32
  9. Generate mock data: [datasource_name]
33
33
  10. Append existing fixture: [fixture_pathname] - Target: [datasource_name]
34
+ 11. Delete file: [file_pathname] - [reason for deletion]
34
35
 
35
36
  <dev_notes>
36
37
  You can skip steps where resources will not be created or updated.
37
38
  Always add 'Create secrets' as the FIRST step if any secrets/environment variables are required for the implementation. This step should include ALL required secrets at once.
38
39
  Always add 'Generate mock data' step if a landing datasource was created without providing a fixture file.
39
40
  Always add 'Append existing fixture' step if a landing datasource was created after providing a fixture file.
41
+ Always add 'Delete file' step when removing datafiles, fixtures, or any other project files.
40
42
  Solve the specific user request, do not add extra steps that are not related to the user request.
41
43
  Reuse the existing resources if possible.
44
+ If a plan only has one step, skip the plan and go directly to the next tool needed.
42
45
  </dev_notes>
43
46
 
44
47
  Resource dependencies:
@@ -609,6 +612,7 @@ test_instructions = """
609
612
  - MANDATORY: Before creating the test, analyze the fixture files that the tables of the endpoint are using so you can create relevant tests.
610
613
  - IMPORTANT: expected_result field should always be an empty string, because it will be filled by the `create_test` tool.
611
614
  - If the endpoint does not have parameters, you can omit parameters and generate a single test.
615
+ - If some tests are skipped, it is because some test names do not match any pipe name. Rename the test file to match the pipe name.
612
616
  - The format of the test file is the following:
613
617
  <test_file_format>
614
618
  - name: kpis_single_day
@@ -735,7 +739,7 @@ You are an interactive CLI tool that helps users with data engineering tasks. Us
735
739
 
736
740
  # Tools
737
741
  You have access to the following tools:
738
- 1. `create_datafile` - Create a datafile (datasource, endpoint, materialized, sink, copy, connection) in the project folder. Confirmation will be asked by the tool before creating the file.
742
+ 1. `datafile` - Create datafiles and remove files (datasource, endpoint, materialized, sink, copy, connection, fixtures) in the project folder. Confirmation will be asked by the tool before creating or removing the file.
739
743
  2. `plan` - Plan the creation or update of resources.
740
744
  3. `build` - Build the project.
741
745
  4. `deploy` - Deploy the project to Tinybird Cloud.
@@ -753,14 +757,14 @@ You have access to the following tools:
753
757
  16. `diff_resource` - Diff the content of a resource in Tinybird Cloud vs Tinybird Local vs Project local file.
754
758
  17. `rename_datafile_or_fixture` - Rename a datafile or fixture.
755
759
 
756
- # When creating or updating datafiles:
757
- 1. Use `plan` tool to plan the creation, update or rename of resources.
758
- 2. If the user confirms the plan, go from 3 to 7 steps until all the resources are created, updated or skipped.
759
- 3. Without asking, use the `create_datafile` tool to create the datafile, because it will ask for confirmation before creating the file.
760
- 4. Check the result of the `create_datafile` tool to see if the datafile was created successfully.
761
- 5. If the datafile was created successfully, report the result to the user.
762
- 6. If the datafile was not created, finish the process and just wait for a new user prompt.
763
- 7. If the datafile was created successfully, but the built failed, try to fix the error and repeat the process.
760
+ # When creating, updating, or deleting files:
761
+ 1. Use `plan` tool to plan the creation, update, rename, or deletion of resources.
762
+ 2. If the user confirms the plan, go from 3 to 7 steps until all the resources are created, updated, deleted, or skipped.
763
+ 3. Without asking, use the `create_datafile` or `remove_file` tool to create or remove the file, because it will ask for confirmation before creating or removing the file.
764
+ 4. Check the result of the `create_datafile` or `remove_file` tool to see if the file was created or removed successfully.
765
+ 5. If the file was created or removed successfully, report the result to the user.
766
+ 6. If the file was not created or removed, finish the process and just wait for a new user prompt.
767
+ 7. If the file was created or removed successfully, but the build failed, try to fix the error and repeat the process.
764
768
 
765
769
  # When creating a landing datasource given a .ndjson file:
766
770
  - If the user does not specify anything about the desired schema, create a schema like this (sorting key not needed in this case)
@@ -5,8 +5,7 @@ from pydantic_ai.usage import Usage
5
5
  from tinybird.tb.modules.agent.animations import ThinkingAnimation
6
6
  from tinybird.tb.modules.agent.models import create_model
7
7
  from tinybird.tb.modules.agent.prompts import test_instructions, tests_files_prompt, tone_and_style_instructions
8
- from tinybird.tb.modules.agent.tools.test import create_tests as create_tests_tool
9
- from tinybird.tb.modules.agent.tools.test import run_tests as run_tests_tool
8
+ from tinybird.tb.modules.agent.tools.test import create_tests, remove_test, rename_test, run_tests
10
9
  from tinybird.tb.modules.agent.utils import TinybirdAgentContext
11
10
  from tinybird.tb.modules.project import Project
12
11
 
@@ -42,13 +41,17 @@ You can do the following:
42
41
  - Create new test files.
43
42
  - Update existing test files.
44
43
  - Run tests.
44
+ - Rename test files.
45
+ - Remove test files.
45
46
  """,
46
47
  tone_and_style_instructions,
47
48
  test_instructions,
48
49
  ],
49
50
  tools=[
50
- Tool(create_tests_tool, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
51
- Tool(run_tests_tool, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
51
+ Tool(create_tests, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
52
+ Tool(run_tests, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
53
+ Tool(rename_test, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
54
+ Tool(remove_test, docstring_format="google", require_parameter_descriptions=True, takes_ctx=True),
52
55
  ],
53
56
  )
54
57
 
@@ -1,4 +1,5 @@
1
1
  import os
2
+ from typing import Optional
2
3
 
3
4
  import click
4
5
  from pydantic_ai import RunContext
@@ -8,6 +9,7 @@ from tinybird.tb.modules.agent.utils import (
8
9
  TinybirdAgentContext,
9
10
  copy_fixture_to_project_folder_if_needed,
10
11
  show_confirmation,
12
+ show_env_options,
11
13
  show_input,
12
14
  )
13
15
  from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_pretty_table
@@ -15,14 +17,14 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager
15
17
 
16
18
 
17
19
  def append_file(
18
- ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_pathname: str, cloud: bool = False
20
+ ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_pathname: str, cloud: Optional[bool] = None
19
21
  ) -> str:
20
22
  """Append a fixture file to a datasource
21
23
 
22
24
  Args:
23
25
  datasource_name: Name of the datasource to append fixture to
24
26
  fixture_pathname: Path to the fixture file to append
25
- cloud: Whether to append the fixture to the cloud or local environment. Optional.
27
+ cloud: Whether to append the fixture to the cloud or local environment. If None (user didn't specify), will ask user to clarify. Defaults to local (False) in dangerous skip permissions mode.
26
28
 
27
29
  Returns:
28
30
  str: Message indicating the success or failure of the appending
@@ -37,6 +39,19 @@ def append_file(
37
39
 
38
40
  fixture_path = fixture_path_or_error
39
41
  fixture_pathname = os.path.relpath(fixture_path, ctx.deps.folder)
42
+
43
+ # Handle cloud parameter - ask user if uncertain and not in dangerous skip mode
44
+ if cloud is None:
45
+ if ctx.deps.dangerously_skip_permissions:
46
+ # Default to local when in dangerous skip mode
47
+ cloud = False
48
+ else:
49
+ # Ask the user to choose execution mode
50
+ cloud = show_env_options(ctx)
51
+ if cloud is None:
52
+ ctx.deps.thinking_animation.start()
53
+ return "Append operation cancelled by user."
54
+
40
55
  cloud_or_local = "Cloud" if cloud else "Local"
41
56
  confirmation = show_confirmation(
42
57
  title=f"Append fixture {fixture_pathname} to datasource '{datasource_name}' in Tinybird {cloud_or_local}?",
@@ -69,20 +84,33 @@ def append_file(
69
84
 
70
85
 
71
86
  def append_url(
72
- ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_url: str, cloud: bool = False
87
+ ctx: RunContext[TinybirdAgentContext], datasource_name: str, fixture_url: str, cloud: Optional[bool] = None
73
88
  ) -> str:
74
89
  """Append existing fixture to a datasource
75
90
 
76
91
  Args:
77
92
  datasource_name: Name of the datasource to append fixture to
78
93
  fixture_url: external url to the fixture file to append
79
- cloud: Whether to append the fixture to the cloud or local environment. Optional.
94
+ cloud: Whether to append the fixture to the cloud or local environment. If None (user didn't specify), will ask user to clarify. Defaults to local (False) in dangerous skip permissions mode.
80
95
 
81
96
  Returns:
82
97
  str: Message indicating the success or failure of the appending
83
98
  """
84
99
  try:
85
100
  ctx.deps.thinking_animation.stop()
101
+
102
+ # Handle cloud parameter - ask user if uncertain and not in dangerous skip mode
103
+ if cloud is None:
104
+ if ctx.deps.dangerously_skip_permissions:
105
+ # Default to local when in dangerous skip mode
106
+ cloud = False
107
+ else:
108
+ # Ask the user to choose execution mode
109
+ cloud = show_env_options(ctx)
110
+ if cloud is None:
111
+ ctx.deps.thinking_animation.start()
112
+ return "Append operation cancelled by user."
113
+
86
114
  cloud_or_local = "Cloud" if cloud else "Local"
87
115
  confirmation = show_confirmation(
88
116
  title=f"Append URL {fixture_url} to datasource '{datasource_name}' in Tinybird {cloud_or_local}?",
@@ -115,10 +143,13 @@ def append_url(
115
143
 
116
144
 
117
145
  def handle_quarantine_error(
118
- ctx: RunContext[TinybirdAgentContext], error_message: str, datasource_name: str, cloud: bool = False
146
+ ctx: RunContext[TinybirdAgentContext], error_message: str, datasource_name: str, cloud: Optional[bool] = None
119
147
  ) -> str:
120
148
  try:
121
149
  if "in quarantine" in error_message:
150
+ # Default to local if cloud is None for error handling
151
+ if cloud is None:
152
+ cloud = False
122
153
  cloud_or_local = "Cloud" if cloud else "Local"
123
154
  click.echo(FeedbackManager.highlight(message=f"» Looking for errors in {datasource_name}_quarantine..."))
124
155
  query = f"select * from {datasource_name}_quarantine order by insertion_date desc limit 5 FORMAT JSON"
@@ -191,3 +191,81 @@ def rename_datafile_or_fixture(ctx: RunContext[TinybirdAgentContext], path: str,
191
191
  click.echo(FeedbackManager.error(message=e))
192
192
  ctx.deps.thinking_animation.start()
193
193
  return f"Error renaming {path} to {new_path}: {e}"
194
+
195
+
196
+ def remove_file(ctx: RunContext[TinybirdAgentContext], path: str) -> str:
197
+ """Removes a datafile or fixture from the project folder and rebuilds the project if needed
198
+
199
+ Args:
200
+ path (str): The path to the file to remove. Required.
201
+
202
+ Returns:
203
+ str: If the resource was removed successfully.
204
+ """
205
+ try:
206
+ ctx.deps.thinking_animation.stop()
207
+ path = path.removeprefix("/")
208
+ full_path = Path(ctx.deps.folder) / path
209
+
210
+ if not full_path.exists():
211
+ click.echo(FeedbackManager.error(message=f"Error: File {path} not found"))
212
+ ctx.deps.thinking_animation.start()
213
+ return f"Error: File {path} not found (double check the file path)"
214
+
215
+ confirmation = show_confirmation(
216
+ title=f"Delete '{path}'?",
217
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
218
+ )
219
+
220
+ if confirmation == "review":
221
+ feedback = show_input(ctx.deps.workspace_name)
222
+ ctx.deps.thinking_animation.start()
223
+ return f"User did not confirm the proposed changes and gave the following feedback: {feedback}"
224
+
225
+ click.echo(FeedbackManager.highlight(message=f"» Removing {path}..."))
226
+
227
+ # Check if it's a datafile that requires project rebuild
228
+ is_datafile = full_path.suffix in (".connection", ".datasource", ".pipe")
229
+
230
+ # Remove the file
231
+ full_path.unlink()
232
+
233
+ # Check for corresponding .sql file (for fixtures)
234
+ sql_file_path = full_path.with_suffix(".sql")
235
+ sql_file_removed = False
236
+ if sql_file_path.exists():
237
+ sql_file_path.unlink()
238
+ sql_file_removed = True
239
+
240
+ # Rebuild project if it's a datafile
241
+ if is_datafile:
242
+ ctx.deps.build_project(test=False, silent=True, load_fixtures=False)
243
+
244
+ success_message = f"✓ {path} removed"
245
+ if sql_file_removed:
246
+ success_message += f" (and {sql_file_path.name})"
247
+
248
+ click.echo(FeedbackManager.success(message=success_message))
249
+ ctx.deps.thinking_animation.start()
250
+
251
+ result_message = f"Removed {path}"
252
+ if sql_file_removed:
253
+ result_message += f" and {sql_file_path.name}"
254
+ if is_datafile:
255
+ result_message += ". Project built successfully."
256
+ else:
257
+ result_message += "."
258
+
259
+ return result_message
260
+ except AgentRunCancelled as e:
261
+ raise e
262
+ except CLIBuildException as e:
263
+ ctx.deps.thinking_animation.stop()
264
+ click.echo(FeedbackManager.error(message=e))
265
+ ctx.deps.thinking_animation.start()
266
+ return f"Error building project: {e}. If the error is related to another resource, fix it and try again."
267
+ except Exception as e:
268
+ ctx.deps.thinking_animation.stop()
269
+ click.echo(FeedbackManager.error(message=e))
270
+ ctx.deps.thinking_animation.start()
271
+ return f"Error removing {path}: {e}"
@@ -6,13 +6,28 @@ from tinybird.tb.modules.exceptions import CLIDeploymentException
6
6
  from tinybird.tb.modules.feedback_manager import FeedbackManager
7
7
 
8
8
 
9
- def deploy(ctx: RunContext[TinybirdAgentContext]) -> str:
10
- """Deploy the project"""
9
+ def deploy(ctx: RunContext[TinybirdAgentContext], allow_destructive_operations: bool = False) -> str:
10
+ """Deploy the project
11
+
12
+ Args:
13
+ allow_destructive_operations (bool): Set to true if a datasource, pipe or connection file has been deleted locally.
14
+ Optional. Default is False.
15
+
16
+ Returns:
17
+ str: The result of the deployment
18
+ """
11
19
  try:
12
20
  ctx.deps.thinking_animation.stop()
21
+
22
+ if allow_destructive_operations:
23
+ click.echo(
24
+ FeedbackManager.warning(message="Destructive operations flag is enabled due to a file deleted recently")
25
+ )
26
+
27
+ click.echo()
13
28
  confirmation = show_confirmation(
14
29
  title="Deploy the project?",
15
- skip_confirmation=ctx.deps.dangerously_skip_permissions,
30
+ skip_confirmation=False,
16
31
  )
17
32
 
18
33
  if confirmation == "review":
@@ -21,7 +36,7 @@ def deploy(ctx: RunContext[TinybirdAgentContext]) -> str:
21
36
  return f"User did not confirm deployment and gave the following feedback: {feedback}"
22
37
 
23
38
  click.echo(FeedbackManager.highlight(message="» Deploying project..."))
24
- ctx.deps.deploy_project()
39
+ ctx.deps.deploy_project(allow_destructive_operations=allow_destructive_operations)
25
40
  click.echo(FeedbackManager.success(message="✓ Project deployed successfully"))
26
41
  ctx.deps.thinking_animation.start()
27
42
  return "Project deployed successfully"
@@ -35,7 +35,7 @@ def execute_query(
35
35
  ctx: RunContext[TinybirdAgentContext],
36
36
  query: str,
37
37
  task: str,
38
- cloud: bool = False,
38
+ cloud: Optional[bool] = None,
39
39
  script: Optional[str] = None,
40
40
  export_format: Optional[str] = None,
41
41
  ):
@@ -44,7 +44,7 @@ def execute_query(
44
44
  Args:
45
45
  query (str): The query to execute. Required.
46
46
  task (str): The purpose of the query. Required.
47
- cloud (bool): Whether to execute the query on cloud or local. Optional.
47
+ cloud (bool): Whether to execute the query on cloud or local. If None (user didn't specify), will ask user to clarify. Defaults to local (False) in dangerous skip permissions mode.
48
48
  script (str): Python script using plotext to render the query results as a chart. The script will have access to 'data' (list of dicts), 'meta' (list of column info dicts), 'terminal_width' and 'terminal_height' variables. Always use plt.theme("clear") for transparent background and plt.plot_size(terminal_width, terminal_height) for proper sizing. For bar charts, use the simple versions: plt.simple_bar(), plt.simple_multiple_bar(), and plt.simple_stacked_bar(). Optional.
49
49
  export_format (str): The format to export the query results to. Options: csv, json, ndjson. Optional.
50
50
 
@@ -61,6 +61,19 @@ def execute_query(
61
61
  if query.lower().startswith(forbidden_command):
62
62
  return f"Error executing query: {forbidden_command} is not allowed."
63
63
 
64
+ # Handle cloud parameter - ask user if uncertain and not in dangerous skip mode
65
+ if cloud is None:
66
+ if ctx.deps.dangerously_skip_permissions:
67
+ # Default to local when in dangerous skip mode
68
+ cloud = False
69
+ else:
70
+ # Ask the user to choose execution mode
71
+ from tinybird.tb.modules.agent.utils import show_env_options
72
+
73
+ cloud = show_env_options(ctx)
74
+ if cloud is None:
75
+ return "Query execution cancelled by user."
76
+
64
77
  cloud_or_local = "cloud" if cloud else "local"
65
78
  ctx.deps.thinking_animation.stop()
66
79
 
@@ -1,19 +1,21 @@
1
+ from typing import Optional
2
+
1
3
  import click
2
4
  from pydantic_ai import RunContext
3
5
 
4
- from tinybird.tb.modules.agent.utils import TinybirdAgentContext
6
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_env_options
5
7
  from tinybird.tb.modules.feedback_manager import FeedbackManager
6
8
 
7
9
 
8
10
  def get_endpoint_stats(
9
- ctx: RunContext[TinybirdAgentContext], endpoint_name: str, interval_days: int = 1, cloud: bool = False
11
+ ctx: RunContext[TinybirdAgentContext], endpoint_name: str, interval_days: int = 1, cloud: Optional[bool] = None
10
12
  ):
11
13
  """Get stats for an endpoint:
12
14
 
13
15
  Args:
14
16
  endpoint_name (str): The name of the endpoint to get stats for. Required.
15
17
  interval_days (int): The number of days to get stats for. Optional.
16
- cloud (bool): Whether to get stats from cloud or local. Optional.
18
+ cloud (bool): Whether to get stats from cloud or local. If None (user didn't specify), will ask user to clarify. Defaults to local (False) in dangerous skip permissions mode.
17
19
 
18
20
  Returns:
19
21
  str: The result of the stats.
@@ -25,6 +27,17 @@ def get_endpoint_stats(
25
27
  pipe_stats = "tinybird.pipe_stats"
26
28
  date_column = "date"
27
29
 
30
+ # Handle cloud parameter - ask user if uncertain and not in dangerous skip mode
31
+ if cloud is None:
32
+ if ctx.deps.dangerously_skip_permissions:
33
+ # Default to local when in dangerous skip mode
34
+ cloud = False
35
+ else:
36
+ # Ask the user to choose execution mode
37
+ cloud = show_env_options(ctx)
38
+ if cloud is None:
39
+ return "Get endpoint stats operation cancelled by user."
40
+
28
41
  days = "day" if interval_days == 1 else "days"
29
42
  cloud_or_local = "cloud" if cloud else "local"
30
43
  ctx.deps.thinking_animation.stop()
@@ -1,22 +1,35 @@
1
+ from typing import Optional
2
+
1
3
  import click
2
4
  import requests
3
5
  from pydantic_ai import RunContext
4
6
 
5
- from tinybird.tb.modules.agent.utils import TinybirdAgentContext
7
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext, show_env_options
6
8
  from tinybird.tb.modules.feedback_manager import FeedbackManager
7
9
 
8
10
 
9
- def get_openapi_definition(ctx: RunContext[TinybirdAgentContext], endpoint_name: str, cloud: bool = False):
11
+ def get_openapi_definition(ctx: RunContext[TinybirdAgentContext], endpoint_name: str, cloud: Optional[bool] = None):
10
12
  """Get the OpenAPI definition for an endpoint:
11
13
 
12
14
  Args:
13
15
  endpoint_name (str): The name of the endpoint to get the OpenAPI definition for. Required.
14
- cloud (bool): Whether to get the OpenAPI definition from cloud or local. Optional.
16
+ cloud (bool): Whether to get the OpenAPI definition from cloud or local. If None (user didn't specify), will ask user to clarify. Defaults to local (False) in dangerous skip permissions mode.
15
17
 
16
18
  Returns:
17
19
  str: The OpenAPI definition for the endpoint.
18
20
  """
19
21
 
22
+ # Handle cloud parameter - ask user if uncertain and not in dangerous skip mode
23
+ if cloud is None:
24
+ if ctx.deps.dangerously_skip_permissions:
25
+ # Default to local when in dangerous skip mode
26
+ cloud = False
27
+ else:
28
+ # Ask the user to choose execution mode
29
+ cloud = show_env_options(ctx)
30
+ if cloud is None:
31
+ return "Get OpenAPI definition operation cancelled by user."
32
+
20
33
  cloud_or_local = "Cloud" if cloud else "Local"
21
34
  ctx.deps.thinking_animation.stop()
22
35
 
@@ -4,7 +4,7 @@ import click
4
4
  import humanfriendly
5
5
  from pydantic_ai import RunContext
6
6
 
7
- from tinybird.tb.modules.agent.utils import TinybirdAgentContext, limit_result_output
7
+ from tinybird.tb.modules.agent.utils import TinybirdAgentContext, limit_result_output, show_env_options
8
8
  from tinybird.tb.modules.common import echo_safe_humanfriendly_tables_format_pretty_table
9
9
  from tinybird.tb.modules.feedback_manager import FeedbackManager
10
10
 
@@ -13,19 +13,29 @@ def request_endpoint(
13
13
  ctx: RunContext[TinybirdAgentContext],
14
14
  endpoint_name: str,
15
15
  params: Optional[dict[str, str]] = None,
16
- cloud: bool = False,
16
+ cloud: Optional[bool] = None,
17
17
  ):
18
18
  """Request an endpoint:
19
19
 
20
20
  Args:
21
21
  endpoint_name (str): The name of the endpoint to request. Required.
22
22
  params (dict): The parameters to pass to the endpoint. Optional.
23
- cloud (bool): Whether to request the endpoint on cloud or local. Optional.
23
+ cloud (bool): Whether to request the endpoint on cloud or local. If None (user didn't specify), will ask user to clarify. Defaults to local (False) in dangerous skip permissions mode.
24
24
 
25
25
  Returns:
26
26
  str: The result of the query.
27
27
  """
28
28
  try:
29
+ # Handle cloud parameter - ask user if uncertain and not in dangerous skip mode
30
+ if cloud is None:
31
+ if ctx.deps.dangerously_skip_permissions:
32
+ # Default to local when in dangerous skip mode
33
+ cloud = False
34
+ else:
35
+ # Ask the user to choose execution mode
36
+ cloud = show_env_options(ctx)
37
+ if cloud is None:
38
+ return "Endpoint request cancelled by user."
29
39
  cloud_or_local = "cloud" if cloud else "local"
30
40
  ctx.deps.thinking_animation.stop()
31
41
  with_params = f" with params {params}" if params else ""
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from pathlib import Path
2
3
  from typing import Optional
3
4
 
@@ -122,7 +123,6 @@ def run_tests(ctx: RunContext[TinybirdAgentContext], pipe_name: Optional[str] =
122
123
  return f"User did not confirm the proposed changes and gave the following feedback: {feedback}"
123
124
 
124
125
  test_output = ctx.deps.run_tests(pipe_name=pipe_name)
125
- click.echo(test_output)
126
126
  ctx.deps.thinking_animation.start()
127
127
  if pipe_name:
128
128
  return f"Tests for '{pipe_name}' endpoint in {path} and ran successfully\n{test_output}"
@@ -146,3 +146,111 @@ def run_tests(ctx: RunContext[TinybirdAgentContext], pipe_name: Optional[str] =
146
146
  error_message = error_message.replace(test_exit_code, "")
147
147
  ctx.deps.thinking_animation.start()
148
148
  return f"Error running tests: {error_message}"
149
+
150
+
151
+ def rename_test(ctx: RunContext[TinybirdAgentContext], path: str, new_path: str) -> str:
152
+ """Renames a test file.
153
+
154
+ Args:
155
+ path (str): The path to the test file to rename. Required.
156
+ new_path (str): The new path to the test file. Required.
157
+
158
+ Returns:
159
+ str: Result of the rename operation.
160
+ """
161
+ try:
162
+ ctx.deps.thinking_animation.stop()
163
+ confirmation = show_confirmation(
164
+ title=f"Rename '{path}' to '{new_path}'?",
165
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
166
+ )
167
+
168
+ if confirmation == "review":
169
+ feedback = show_input(ctx.deps.workspace_name)
170
+ ctx.deps.thinking_animation.start()
171
+ return f"User did not confirm the proposed changes and gave the following feedback: {feedback}"
172
+
173
+ click.echo(FeedbackManager.highlight(message=f"» Renaming test file {path} to {new_path}..."))
174
+ old_path_full = Path(ctx.deps.folder) / path.removeprefix("/")
175
+ new_path_full = Path(ctx.deps.folder) / new_path.removeprefix("/")
176
+
177
+ if not old_path_full.exists():
178
+ click.echo(FeedbackManager.error(message=f"Error: Test file {path} not found"))
179
+ ctx.deps.thinking_animation.start()
180
+ return f"Error: Test file {path} not found (double check the file path)"
181
+
182
+ if new_path_full.exists():
183
+ click.echo(FeedbackManager.error(message=f"Error: Test file {new_path} already exists"))
184
+ ctx.deps.thinking_animation.start()
185
+ return f"Error: Test file {new_path} already exists"
186
+
187
+ # Ensure new file has .yaml extension for test files
188
+ if new_path_full.suffix != ".yaml":
189
+ new_path_full = new_path_full.with_suffix(".yaml")
190
+ new_path = str(new_path_full.relative_to(ctx.deps.folder))
191
+
192
+ parent_path = new_path_full.parent
193
+ parent_path.mkdir(parents=True, exist_ok=True)
194
+ os.rename(old_path_full, new_path_full)
195
+
196
+ click.echo(FeedbackManager.success(message=f"✓ {new_path} created"))
197
+ ctx.deps.thinking_animation.start()
198
+ return f"Renamed test file from {path} to {new_path}"
199
+ except AgentRunCancelled as e:
200
+ raise e
201
+ except FileNotFoundError:
202
+ ctx.deps.thinking_animation.start()
203
+ click.echo(FeedbackManager.error(message=f"Error: Test file {path} not found"))
204
+ return f"Error: Test file {path} not found (double check the file path)"
205
+ except Exception as e:
206
+ ctx.deps.thinking_animation.stop()
207
+ click.echo(FeedbackManager.error(message=e))
208
+ ctx.deps.thinking_animation.start()
209
+ return f"Error renaming test file {path} to {new_path}: {e}"
210
+
211
+
212
+ def remove_test(ctx: RunContext[TinybirdAgentContext], path: str) -> str:
213
+ """Removes a test file from the project folder
214
+
215
+ Args:
216
+ path (str): The path to the test file to remove. Required.
217
+
218
+ Returns:
219
+ str: If the test file was removed successfully.
220
+ """
221
+ try:
222
+ ctx.deps.thinking_animation.stop()
223
+ path = path.removeprefix("/")
224
+ full_path = Path(ctx.deps.folder) / path
225
+
226
+ if not full_path.exists():
227
+ click.echo(FeedbackManager.error(message=f"Error: Test file {path} not found"))
228
+ ctx.deps.thinking_animation.start()
229
+ return f"Error: Test file {path} not found (double check the file path)"
230
+
231
+ confirmation = show_confirmation(
232
+ title=f"Delete '{path}'?",
233
+ skip_confirmation=ctx.deps.dangerously_skip_permissions,
234
+ )
235
+
236
+ if confirmation == "review":
237
+ feedback = show_input(ctx.deps.workspace_name)
238
+ ctx.deps.thinking_animation.start()
239
+ return f"User did not confirm the proposed changes and gave the following feedback: {feedback}"
240
+
241
+ click.echo(FeedbackManager.highlight(message=f"» Removing {path}..."))
242
+
243
+ # Remove the test file
244
+ full_path.unlink()
245
+
246
+ click.echo(FeedbackManager.success(message=f"✓ {path} removed"))
247
+ ctx.deps.thinking_animation.start()
248
+
249
+ return f"Removed test file {path}."
250
+ except AgentRunCancelled as e:
251
+ raise e
252
+ except Exception as e:
253
+ ctx.deps.thinking_animation.stop()
254
+ click.echo(FeedbackManager.error(message=e))
255
+ ctx.deps.thinking_animation.start()
256
+ return f"Error removing test file {path}: {e}"
@@ -43,7 +43,7 @@ class TinybirdAgentContext(BaseModel):
43
43
  explore_data: Callable[[str], str]
44
44
  build_project: Callable[..., None]
45
45
  build_project_test: Callable[..., None]
46
- deploy_project: Callable[[], None]
46
+ deploy_project: Callable[..., None]
47
47
  deploy_check_project: Callable[[], None]
48
48
  mock_data: Callable[..., list[dict[str, Any]]]
49
49
  append_data_local: Callable[..., None]
@@ -743,6 +743,30 @@ def show_confirmation(title: str, skip_confirmation: bool = False, show_review:
743
743
  raise AgentRunCancelled(f"User cancelled the operation: {title}")
744
744
 
745
745
 
746
+ def show_env_options(ctx: "RunContext[TinybirdAgentContext]") -> Optional[bool]:
747
+ """Show environment options for user to choose between local and cloud.
748
+
749
+ Args:
750
+ ctx: The run context containing TinybirdAgentContext
751
+
752
+ Returns:
753
+ bool: True for cloud, False for local, None if cancelled
754
+ """
755
+
756
+ ctx.deps.thinking_animation.stop()
757
+ click.echo(FeedbackManager.highlight(message="» Agent is uncertain about target environment"))
758
+
759
+ choice = show_options(
760
+ options=["Tinybird Local", f"Tinybird Cloud ({ctx.deps.host})"], title="Where should this query be executed?"
761
+ )
762
+ ctx.deps.thinking_animation.start()
763
+
764
+ if choice is None:
765
+ return None
766
+
767
+ return choice.startswith("Tinybird Cloud")
768
+
769
+
746
770
  def copy_fixture_to_project_folder_if_needed(
747
771
  ctx: RunContext[TinybirdAgentContext], fixture_pathname: str
748
772
  ) -> Union[Path, str]:
@@ -188,7 +188,7 @@ def update_test(pipe: str, project: Project, client: TinyB) -> None:
188
188
  cleanup_test_workspace(client, project.folder)
189
189
 
190
190
 
191
- def run_tests(name: Tuple[str, ...], project: Project, client: TinyB) -> None:
191
+ def run_tests(name: Tuple[str, ...], project: Project, client: TinyB) -> Optional[str]:
192
192
  full_error = ""
193
193
  try:
194
194
  load_secrets(project=project, client=client)
@@ -203,19 +203,30 @@ def run_tests(name: Tuple[str, ...], project: Project, client: TinyB) -> None:
203
203
  endpoints if len(endpoints) > 0 else glob.glob(f"{project.path}/tests/**/*.y*ml", recursive=True)
204
204
  )
205
205
 
206
- def run_test(test_file) -> Optional[str]:
206
+ def run_test(test_file) -> Tuple[Optional[str], int, int, bool]:
207
207
  test_file_path = Path(test_file)
208
- click.echo(FeedbackManager.info(message=f"* {test_file_path.stem}{test_file_path.suffix}"))
209
208
  test_file_content = parse_tests(test_file_path.read_text())
209
+ total_tests = len(test_file_content)
210
+
211
+ # Check if pipe exists before processing any tests
212
+ try:
213
+ client._req(f"/v0/pipes/{test_file_path.stem}")
214
+ click.echo(FeedbackManager.info(message=f"* {test_file_path.stem}{test_file_path.suffix}"))
215
+ except Exception:
216
+ # Entire test file skipped because pipe doesn't exist
217
+ click.echo(FeedbackManager.info(message=f"* {test_file_path.stem}{test_file_path.suffix}"))
218
+ click.echo(
219
+ FeedbackManager.warning(message=f"✗ All tests skipped ({test_file_path.stem}.pipe not found)")
220
+ )
221
+ return None, total_tests, total_tests, True # True indicates file was skipped
222
+
210
223
  test_file_errors = ""
224
+ skipped_count = 0
225
+
211
226
  for test in test_file_content:
212
227
  try:
213
228
  test_params = test["parameters"].split("?")[1] if "?" in test["parameters"] else test["parameters"]
214
- response = None
215
- try:
216
- response = get_pipe_data(client, pipe_name=test_file_path.stem, test_params=test_params)
217
- except Exception:
218
- continue
229
+ response = get_pipe_data(client, pipe_name=test_file_path.stem, test_params=test_params)
219
230
 
220
231
  expected_result = response.text
221
232
  if response.status_code >= 400:
@@ -237,23 +248,45 @@ def run_tests(name: Tuple[str, ...], project: Project, client: TinyB) -> None:
237
248
  except Exception as e:
238
249
  test_file_errors += f"✗ {test['name']} failed\n** Output and expected output are different: \n{e}"
239
250
  click.echo(FeedbackManager.error(message=test_file_errors))
240
- return test_file_errors
241
- return None
251
+ return test_file_errors, skipped_count, total_tests, False
252
+ return None, skipped_count, total_tests, False # False indicates file was not skipped
242
253
 
243
254
  failed_tests_count = 0
255
+ skipped_files_count = 0
256
+ total_tests_count = 0
244
257
  test_count = len(test_files)
245
-
258
+ output = ""
246
259
  for test_file in test_files:
247
- if run_test_error := run_test(test_file):
260
+ run_test_error, skipped_count, individual_tests_count, file_skipped = run_test(test_file)
261
+ total_tests_count += individual_tests_count
262
+
263
+ if file_skipped:
264
+ skipped_files_count += 1
265
+ elif run_test_error:
248
266
  full_error += f"\n{run_test_error}"
249
267
  failed_tests_count += 1
250
268
 
269
+ runnable_files_count = test_count - skipped_files_count
270
+ passed_files_count = runnable_files_count - failed_tests_count
271
+
251
272
  if failed_tests_count:
252
- error = f"\n✗ {test_count - failed_tests_count}/{test_count} passed"
273
+ error = f"\n✗ {passed_files_count}/{runnable_files_count} passed"
274
+ if skipped_files_count > 0:
275
+ error += f" ({skipped_files_count} skipped)"
276
+ output += f"{error}\n"
253
277
  click.echo(FeedbackManager.error(message=error))
254
278
  sys_exit("test_error", full_error)
255
279
  else:
256
- click.echo(FeedbackManager.success(message=f"\n✓ {test_count}/{test_count} passed"))
280
+ if runnable_files_count == 0:
281
+ success_message = "\n✓ No tests to run"
282
+ else:
283
+ success_message = f"\n✓ {runnable_files_count}/{runnable_files_count} passed"
284
+ if skipped_files_count > 0:
285
+ success_message += f" ({skipped_files_count} skipped)"
286
+ message_color = FeedbackManager.success if runnable_files_count > 0 else FeedbackManager.warning
287
+ click.echo(message_color(message=success_message))
288
+ output += f"{success_message}\n"
289
+ return output
257
290
  except Exception as e:
258
291
  raise CLITestException(FeedbackManager.error(message=str(e)))
259
292
  finally:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 0.0.1.dev272
3
+ Version: 0.0.1.dev274
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -17,7 +17,7 @@ tinybird/datafile/exceptions.py,sha256=8rw2umdZjtby85QbuRKFO5ETz_eRHwUY5l7eHsy1w
17
17
  tinybird/datafile/parse_connection.py,sha256=tRyn2Rpr1TeWet5BXmMoQgaotbGdYep1qiTak_OqC5E,1825
18
18
  tinybird/datafile/parse_datasource.py,sha256=ssW8QeFSgglVFi3sDZj_HgkJiTJ2069v2JgqnH3CkDE,1825
19
19
  tinybird/datafile/parse_pipe.py,sha256=xf4m0Tw44QWJzHzAm7Z7FwUoUUtr7noMYjU1NiWnX0k,3880
20
- tinybird/tb/__cli__.py,sha256=s5oRxeoOkXoJmiNFK4NqIPzdBDisEOHAWlbA-u4cJlg,247
20
+ tinybird/tb/__cli__.py,sha256=Ua6pj01kXp3GMMZoliQZuqwIh1eCDW2n3F6DKULSfMk,247
21
21
  tinybird/tb/check_pypi.py,sha256=Gp0HkHHDFMSDL6nxKlOY51z7z1Uv-2LRexNTZSHHGmM,552
22
22
  tinybird/tb/cli.py,sha256=FdDFEIayjmsZEVsVSSvRiVYn_FHOVg_zWQzchnzfWho,1008
23
23
  tinybird/tb/client.py,sha256=IQRaInDjOwr9Fzaz3_xXc3aUGqh94tM2lew7IZbB9eM,53733
@@ -63,13 +63,13 @@ tinybird/tb/modules/sink.py,sha256=dK2s__my0ePIUYrqBzhPSgdWN9rbpvP1G4dT7DJzz80,3
63
63
  tinybird/tb/modules/table.py,sha256=4XrtjM-N0zfNtxVkbvLDQQazno1EPXnxTyo7llivfXk,11035
64
64
  tinybird/tb/modules/telemetry.py,sha256=T9gtsQffWqG_4hRBaUJPzOfMkPwz7mH-R6Bn1XRYViA,11482
65
65
  tinybird/tb/modules/test.py,sha256=O2-mS4uMU6nPi7yWPpWzshAgOlYKiGS-tkM12pXQGMI,1906
66
- tinybird/tb/modules/test_common.py,sha256=YZwAdSfYVXdvArfTc9tH-2QBOhb_XbnJ3eKvyXTJuEM,12717
66
+ tinybird/tb/modules/test_common.py,sha256=kZ503FFXZwUkZ8zCGZKjBXmRKfF6ooqohkuFhzNRX48,14442
67
67
  tinybird/tb/modules/token.py,sha256=ZhW_o7XCr90wJRhMN6816vyo_TVfnzPXyIhrhzQ7oZ0,13807
68
68
  tinybird/tb/modules/watch.py,sha256=No0bK1M1_3CYuMaIgylxf7vYFJ72lTJe3brz6xQ-mJo,8819
69
69
  tinybird/tb/modules/workspace.py,sha256=Q_8HcxMsNg8QG9aBlwcWS2umrDP5IkTIHqqz3sfmGuc,11341
70
70
  tinybird/tb/modules/workspace_members.py,sha256=5JdkJgfuEwbq-t6vxkBhYwgsiTDxF790wsa6Xfif9nk,8608
71
71
  tinybird/tb/modules/agent/__init__.py,sha256=i3oe3vDIWWPaicdCM0zs7D7BJ1W0k7th93ooskHAV00,54
72
- tinybird/tb/modules/agent/agent.py,sha256=4nLBw3Z9uyl1SrziUN9i6IDlGIER2oU5tW_jLJd7MwY,29935
72
+ tinybird/tb/modules/agent/agent.py,sha256=MjLpl2uNASKJiKH74U9HgvQk4h8gxii5u2KR1nJAXx4,31096
73
73
  tinybird/tb/modules/agent/animations.py,sha256=4WOC5_2BracttmMCrV0H91tXfWcUzQHBUaIJc5FA7tE,3490
74
74
  tinybird/tb/modules/agent/banner.py,sha256=l6cO5Fi7lbVKp-GsBP8jf3IkjOWxg2jpAt9NBCy0WR8,4085
75
75
  tinybird/tb/modules/agent/command_agent.py,sha256=NTzgb9qnuG-gDpk87VijKs1UUMukJPaJI5UiZtRWUoQ,2864
@@ -77,26 +77,26 @@ tinybird/tb/modules/agent/compactor.py,sha256=BK5AxZFhrp3xWnsRnYaleiYoIWtVNc-_m6
77
77
  tinybird/tb/modules/agent/explore_agent.py,sha256=HkzKmggfSMz7S3RSeKnZXufq-z_U0tTQJpF7JfNIaGQ,3504
78
78
  tinybird/tb/modules/agent/memory.py,sha256=vBewB_64L_wHoT4tLT6UX2uxcHwSY880QZ26F9rPqXs,3793
79
79
  tinybird/tb/modules/agent/models.py,sha256=IAxqlnHy8c2OeSnDrrSp2Mg38W9_r0GsDM87Wv-YNfM,925
80
- tinybird/tb/modules/agent/prompts.py,sha256=0ApEajQhiPUNmsW9XmaHaUTwUaj5IePryp5FmJ_GiMY,34561
81
- tinybird/tb/modules/agent/testing_agent.py,sha256=mjR9OJ_KzGnjCnjRxp5-sf01LSDkO4-QqknTbkZ-R-Q,2752
82
- tinybird/tb/modules/agent/utils.py,sha256=0hYhpJkmqmtcAuE4R96kzkhLN4LfvpJkxqaghLRclao,31045
80
+ tinybird/tb/modules/agent/prompts.py,sha256=PZ-WymaU1Dni-jh_xk8P_DO4mRtSN7Bv7tfoZ89nzJs,35066
81
+ tinybird/tb/modules/agent/testing_agent.py,sha256=AtwtJViH7805i7djyBgDb7SSUtDyJnw0TWJu6lBFsrg,2953
82
+ tinybird/tb/modules/agent/utils.py,sha256=U1s6_tGynArRA42GvDr5qz2acxVVKfO8NwBLMngyqSI,31788
83
83
  tinybird/tb/modules/agent/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
84
84
  tinybird/tb/modules/agent/tools/analyze.py,sha256=CR5LXg4fou-zYEksqnjpJ0icvxJVoKnTctoI1NRvqCM,3873
85
- tinybird/tb/modules/agent/tools/append.py,sha256=xZVNkES0-qGjUrOfMLTn3kZ8JBm2OSMfLk_IZwUTRUo,6558
85
+ tinybird/tb/modules/agent/tools/append.py,sha256=6uByExBpb9rVzB0tebWyLFbfkjEPSNxIGFbZrJTaGVI,8056
86
86
  tinybird/tb/modules/agent/tools/build.py,sha256=Hm-xDAP9ckMiKquT-DmDg5H0yxZefLOaWKANyoVSaEQ,846
87
- tinybird/tb/modules/agent/tools/create_datafile.py,sha256=ne5KezxhvreXuXTB569omjnE5soQyZaFlv9L17DWqXU,7725
88
- tinybird/tb/modules/agent/tools/deploy.py,sha256=2hKj6LMYiDea6ventsBjE6ArECGIryTdo3X-LYo5oZI,1248
87
+ tinybird/tb/modules/agent/tools/datafile.py,sha256=LvfDtEdgEWAeirTcx7yO6mfYshEuhWr2M1hoISIbg-g,10641
88
+ tinybird/tb/modules/agent/tools/deploy.py,sha256=uDhg5VHC5nEMMBt5LXpcicVepGz67R3p-_5QN45vCFE,1807
89
89
  tinybird/tb/modules/agent/tools/deploy_check.py,sha256=2Wr9hQfKPlhqhumOv5TNl_xFctvdq_DHZ2dI2h_LggY,1048
90
90
  tinybird/tb/modules/agent/tools/diff_resource.py,sha256=_9xHcDzCTKk_E1wKQbuktVqV6U9sA0kqYaBxWvtliX0,2613
91
- tinybird/tb/modules/agent/tools/execute_query.py,sha256=PUMSfnpTHxdbrlry4qH2c2fOdkRHhLmpZInslSOKFNU,8169
92
- tinybird/tb/modules/agent/tools/get_endpoint_stats.py,sha256=LiEK6ToyPDW2aI8ijclzuwdYAcFmwH-TyjqdFEzQWAc,1689
93
- tinybird/tb/modules/agent/tools/get_openapi_definition.py,sha256=mjIVVXtgvTs5LzOR8Bp4jB1XhLVMysHrHXawkErFdt8,2282
91
+ tinybird/tb/modules/agent/tools/execute_query.py,sha256=59Z_9Rl1NPRxMI3e-rIG8kfKRWgDgFt4j2GYO_Tzu7w,8845
92
+ tinybird/tb/modules/agent/tools/get_endpoint_stats.py,sha256=r2FrXg1L1s_Llr1tPdJ6k_gu6qw7qLsAXOkbz3eTk1g,2307
93
+ tinybird/tb/modules/agent/tools/get_openapi_definition.py,sha256=4TIMO2XzHBMhpt9zIWRfjjPZbThT8r_iPS4CVHcItE0,2904
94
94
  tinybird/tb/modules/agent/tools/mock.py,sha256=RvdsKIr0vKEs91GuK5vKg0fDj8SI-cdcX4XqgvnSwuQ,4508
95
95
  tinybird/tb/modules/agent/tools/plan.py,sha256=2KHLNkr2f1RfkbAR4mCVsv94LGosXd8-ky7v6BB1OtQ,985
96
- tinybird/tb/modules/agent/tools/request_endpoint.py,sha256=fK8qeCVWQvSbqVyuE71Yvm5-EFTvsDYADpKHmwblbUI,3356
96
+ tinybird/tb/modules/agent/tools/request_endpoint.py,sha256=xseEDQez2xfnPWNOoGnRmHB2KR9WLCx_q-vzS6NtaOY,3972
97
97
  tinybird/tb/modules/agent/tools/run_command.py,sha256=XjPDTTzkba9GOQBDiSTwddluyXkguVhxvXnRaC8m-Zc,1657
98
98
  tinybird/tb/modules/agent/tools/secret.py,sha256=UbF9YIW4zh5qdF7qCeMhbhsDt_2qdjjntJE1e8HSUG0,4292
99
- tinybird/tb/modules/agent/tools/test.py,sha256=CbGak_coopCTtqHoPWy-BwgLMIyEyeO34NTNkv18au4,6041
99
+ tinybird/tb/modules/agent/tools/test.py,sha256=4XuEWVHLOTSO51Z9xJ08dTjk0j3IWY_JlPtSBO5aaUs,10373
100
100
  tinybird/tb/modules/datafile/build.py,sha256=NFKBrusFLU0WJNCXePAFWiEDuTaXpwc0lHlOQWEJ43s,51117
101
101
  tinybird/tb/modules/datafile/build_common.py,sha256=2yNdxe49IMA9wNvl25NemY2Iaz8L66snjOdT64dm1is,4511
102
102
  tinybird/tb/modules/datafile/build_datasource.py,sha256=Ra8pVQBDafbFRUKlhpgohhTsRyp_ADKZJVG8Gd69idY,17227
@@ -117,8 +117,8 @@ tinybird/tb_cli_modules/config.py,sha256=IsgdtFRnUrkY8-Zo32lmk6O7u3bHie1QCxLwgp4
117
117
  tinybird/tb_cli_modules/exceptions.py,sha256=pmucP4kTF4irIt7dXiG-FcnI-o3mvDusPmch1L8RCWk,3367
118
118
  tinybird/tb_cli_modules/regions.py,sha256=QjsL5H6Kg-qr0aYVLrvb1STeJ5Sx_sjvbOYO0LrEGMk,166
119
119
  tinybird/tb_cli_modules/telemetry.py,sha256=Hh2Io8ZPROSunbOLuMvuIFU4TqwWPmQTqal4WS09K1A,10449
120
- tinybird-0.0.1.dev272.dist-info/METADATA,sha256=Ait5uLqtYO9yjOWIF4F7i4ULQE8rxaqqsyrjfYpxyyc,1763
121
- tinybird-0.0.1.dev272.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
122
- tinybird-0.0.1.dev272.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
123
- tinybird-0.0.1.dev272.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
124
- tinybird-0.0.1.dev272.dist-info/RECORD,,
120
+ tinybird-0.0.1.dev274.dist-info/METADATA,sha256=AQKOg2W-tA_h-i8h1vfz2fzxThxcaz-LKFJmBB-KJXU,1763
121
+ tinybird-0.0.1.dev274.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
122
+ tinybird-0.0.1.dev274.dist-info/entry_points.txt,sha256=LwdHU6TfKx4Qs7BqqtaczEZbImgU7Abe9Lp920zb_fo,43
123
+ tinybird-0.0.1.dev274.dist-info/top_level.txt,sha256=VqqqEmkAy7UNaD8-V51FCoMMWXjLUlR0IstvK7tJYVY,54
124
+ tinybird-0.0.1.dev274.dist-info/RECORD,,