sqlsaber 0.26.0__py3-none-any.whl → 0.27.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.
Potentially problematic release.
This version of sqlsaber might be problematic. Click here for more details.
- sqlsaber/application/__init__.py +1 -0
- sqlsaber/application/auth_setup.py +164 -0
- sqlsaber/application/db_setup.py +223 -0
- sqlsaber/application/model_selection.py +98 -0
- sqlsaber/application/prompts.py +115 -0
- sqlsaber/cli/auth.py +22 -50
- sqlsaber/cli/commands.py +11 -0
- sqlsaber/cli/database.py +24 -85
- sqlsaber/cli/display.py +1 -1
- sqlsaber/cli/interactive.py +140 -124
- sqlsaber/cli/models.py +18 -28
- sqlsaber/cli/onboarding.py +325 -0
- sqlsaber/config/api_keys.py +2 -2
- {sqlsaber-0.26.0.dist-info → sqlsaber-0.27.0.dist-info}/METADATA +1 -1
- {sqlsaber-0.26.0.dist-info → sqlsaber-0.27.0.dist-info}/RECORD +18 -12
- {sqlsaber-0.26.0.dist-info → sqlsaber-0.27.0.dist-info}/WHEEL +0 -0
- {sqlsaber-0.26.0.dist-info → sqlsaber-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlsaber-0.26.0.dist-info → sqlsaber-0.27.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
"""Interactive onboarding flow for first-time SQLSaber users."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
from rich.console import Console
|
|
6
|
+
from rich.panel import Panel
|
|
7
|
+
|
|
8
|
+
from sqlsaber.cli.models import ModelManager
|
|
9
|
+
from sqlsaber.config.api_keys import APIKeyManager
|
|
10
|
+
from sqlsaber.config.auth import AuthConfigManager
|
|
11
|
+
from sqlsaber.config.database import DatabaseConfigManager
|
|
12
|
+
|
|
13
|
+
console = Console()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def needs_onboarding(database_arg: str | None = None) -> bool:
|
|
17
|
+
"""Check if user needs onboarding.
|
|
18
|
+
|
|
19
|
+
Onboarding is needed if:
|
|
20
|
+
- No database is configured AND no database connection string provided via CLI
|
|
21
|
+
"""
|
|
22
|
+
# If user provided a database argument, skip onboarding
|
|
23
|
+
if database_arg:
|
|
24
|
+
return False
|
|
25
|
+
|
|
26
|
+
# Check if databases are configured
|
|
27
|
+
db_manager = DatabaseConfigManager()
|
|
28
|
+
has_db = db_manager.has_databases()
|
|
29
|
+
|
|
30
|
+
return not has_db
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def welcome_screen() -> None:
|
|
34
|
+
"""Display welcome screen to new users."""
|
|
35
|
+
banner = """
|
|
36
|
+
███████ ██████ ██ ███████ █████ ██████ ███████ ██████
|
|
37
|
+
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
38
|
+
███████ ██ ██ ██ ███████ ███████ ██████ █████ ██████
|
|
39
|
+
██ ██ ▄▄ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
40
|
+
███████ ██████ ███████ ███████ ██ ██ ██████ ███████ ██ ██
|
|
41
|
+
▀▀
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
console.print(Panel.fit(banner, style="bold blue"))
|
|
45
|
+
console.print()
|
|
46
|
+
|
|
47
|
+
welcome_message = """
|
|
48
|
+
[bold]Welcome to SQLsaber! 🎉[/bold]
|
|
49
|
+
|
|
50
|
+
SQLsaber is an agentic SQL assistant that lets you query your database using natural language.
|
|
51
|
+
|
|
52
|
+
Let's get you set up in just a few steps.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
console.print(Panel(welcome_message.strip(), border_style="blue", padding=(1, 2)))
|
|
56
|
+
console.print()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
async def setup_database_guided() -> str | None:
|
|
60
|
+
"""Guide user through database setup.
|
|
61
|
+
|
|
62
|
+
Returns the name of the configured database or None if cancelled.
|
|
63
|
+
"""
|
|
64
|
+
from sqlsaber.application.db_setup import (
|
|
65
|
+
build_config,
|
|
66
|
+
collect_db_input,
|
|
67
|
+
save_database,
|
|
68
|
+
test_connection,
|
|
69
|
+
)
|
|
70
|
+
from sqlsaber.application.prompts import AsyncPrompter
|
|
71
|
+
|
|
72
|
+
console.print("━" * 80, style="dim")
|
|
73
|
+
console.print("[bold cyan]Step 1 of 2: Database Connection[/bold cyan]")
|
|
74
|
+
console.print("━" * 80, style="dim")
|
|
75
|
+
console.print()
|
|
76
|
+
|
|
77
|
+
try:
|
|
78
|
+
# Ask for connection name
|
|
79
|
+
prompter = AsyncPrompter()
|
|
80
|
+
name = await prompter.text(
|
|
81
|
+
"What would you like to name this connection?",
|
|
82
|
+
default="mydb",
|
|
83
|
+
validate=lambda x: bool(x.strip()) or "Name cannot be empty",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
if name is None:
|
|
87
|
+
return None
|
|
88
|
+
|
|
89
|
+
name = name.strip()
|
|
90
|
+
|
|
91
|
+
# Check if name already exists
|
|
92
|
+
db_manager = DatabaseConfigManager()
|
|
93
|
+
if db_manager.get_database(name):
|
|
94
|
+
console.print(
|
|
95
|
+
f"[yellow]Database connection '{name}' already exists.[/yellow]"
|
|
96
|
+
)
|
|
97
|
+
return name
|
|
98
|
+
|
|
99
|
+
# Collect database input (simplified - no SSL in onboarding)
|
|
100
|
+
db_input = await collect_db_input(
|
|
101
|
+
prompter=prompter, name=name, db_type="postgresql", include_ssl=False
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if db_input is None:
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
# Build config
|
|
108
|
+
db_config = build_config(db_input)
|
|
109
|
+
|
|
110
|
+
# Test the connection
|
|
111
|
+
console.print(f"[dim]Testing connection to '{name}'...[/dim]")
|
|
112
|
+
connection_success = await test_connection(db_config, db_input.password)
|
|
113
|
+
|
|
114
|
+
if not connection_success:
|
|
115
|
+
retry = await prompter.confirm(
|
|
116
|
+
"Would you like to try again with different settings?", default=True
|
|
117
|
+
)
|
|
118
|
+
if retry:
|
|
119
|
+
return await setup_database_guided()
|
|
120
|
+
else:
|
|
121
|
+
console.print(
|
|
122
|
+
"[yellow]You can add a database later using 'saber db add'[/yellow]"
|
|
123
|
+
)
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
# Save the configuration
|
|
127
|
+
try:
|
|
128
|
+
save_database(db_manager, db_config, db_input.password)
|
|
129
|
+
console.print(f"[green]✓ Connection to '{name}' successful![/green]")
|
|
130
|
+
console.print()
|
|
131
|
+
return name
|
|
132
|
+
except Exception as e:
|
|
133
|
+
console.print(f"[bold red]Error saving database:[/bold red] {e}")
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
except KeyboardInterrupt:
|
|
137
|
+
console.print("\n[yellow]Setup cancelled.[/yellow]")
|
|
138
|
+
return None
|
|
139
|
+
except Exception as e:
|
|
140
|
+
console.print(f"[bold red]Unexpected error:[/bold red] {e}")
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
async def select_model_for_provider(provider: str) -> str | None:
|
|
145
|
+
"""Fetch and let user select a model for the given provider.
|
|
146
|
+
|
|
147
|
+
Returns the selected model ID or None if cancelled/failed.
|
|
148
|
+
"""
|
|
149
|
+
from sqlsaber.application.model_selection import choose_model, fetch_models
|
|
150
|
+
from sqlsaber.application.prompts import AsyncPrompter
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
console.print()
|
|
154
|
+
console.print(f"[dim]Fetching available {provider.title()} models...[/dim]")
|
|
155
|
+
|
|
156
|
+
model_manager = ModelManager()
|
|
157
|
+
models = await fetch_models(model_manager, providers=[provider])
|
|
158
|
+
|
|
159
|
+
if not models:
|
|
160
|
+
console.print(
|
|
161
|
+
f"[yellow]Could not fetch models for {provider}. Using default.[/yellow]"
|
|
162
|
+
)
|
|
163
|
+
# Use provider-specific default or fallback to Anthropic
|
|
164
|
+
default_model_id = ModelManager.RECOMMENDED_MODELS.get(
|
|
165
|
+
provider, ModelManager.DEFAULT_MODEL
|
|
166
|
+
)
|
|
167
|
+
# Format it properly if we have a recommended model for this provider
|
|
168
|
+
if provider in ModelManager.RECOMMENDED_MODELS:
|
|
169
|
+
return f"{provider}:{ModelManager.RECOMMENDED_MODELS[provider]}"
|
|
170
|
+
return default_model_id
|
|
171
|
+
|
|
172
|
+
prompter = AsyncPrompter()
|
|
173
|
+
console.print()
|
|
174
|
+
selected_model = await choose_model(
|
|
175
|
+
prompter, models, restrict_provider=provider, use_search_filter=True
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return selected_model
|
|
179
|
+
|
|
180
|
+
except KeyboardInterrupt:
|
|
181
|
+
console.print("\n[yellow]Model selection cancelled.[/yellow]")
|
|
182
|
+
return None
|
|
183
|
+
except Exception as e:
|
|
184
|
+
console.print(f"[yellow]Error selecting model: {e}. Using default.[/yellow]")
|
|
185
|
+
# Fallback to provider default
|
|
186
|
+
if provider in ModelManager.RECOMMENDED_MODELS:
|
|
187
|
+
return f"{provider}:{ModelManager.RECOMMENDED_MODELS[provider]}"
|
|
188
|
+
return ModelManager.DEFAULT_MODEL
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
async def setup_auth_guided() -> tuple[bool, str | None]:
|
|
192
|
+
"""Guide user through auth setup.
|
|
193
|
+
|
|
194
|
+
Returns tuple of (success: bool, selected_model: str | None).
|
|
195
|
+
"""
|
|
196
|
+
from sqlsaber.application.auth_setup import setup_auth
|
|
197
|
+
from sqlsaber.application.prompts import AsyncPrompter
|
|
198
|
+
|
|
199
|
+
console.print("━" * 80, style="dim")
|
|
200
|
+
console.print("[bold cyan]Step 2 of 2: Authentication[/bold cyan]")
|
|
201
|
+
console.print("━" * 80, style="dim")
|
|
202
|
+
console.print()
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
# Run auth setup
|
|
206
|
+
prompter = AsyncPrompter()
|
|
207
|
+
auth_manager = AuthConfigManager()
|
|
208
|
+
api_key_manager = APIKeyManager()
|
|
209
|
+
|
|
210
|
+
success, provider = await setup_auth(
|
|
211
|
+
prompter=prompter,
|
|
212
|
+
auth_manager=auth_manager,
|
|
213
|
+
api_key_manager=api_key_manager,
|
|
214
|
+
allow_oauth=True,
|
|
215
|
+
default_provider="anthropic",
|
|
216
|
+
run_oauth_in_thread=True,
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
if not success:
|
|
220
|
+
console.print(
|
|
221
|
+
"[yellow]You can set it up later using 'saber auth setup'[/yellow]"
|
|
222
|
+
)
|
|
223
|
+
console.print()
|
|
224
|
+
return False, None
|
|
225
|
+
|
|
226
|
+
# If auth configured but we don't know the provider (already configured case)
|
|
227
|
+
if provider is None:
|
|
228
|
+
console.print()
|
|
229
|
+
return True, None
|
|
230
|
+
|
|
231
|
+
# Select model for this provider
|
|
232
|
+
selected_model = await select_model_for_provider(provider)
|
|
233
|
+
if selected_model:
|
|
234
|
+
model_manager = ModelManager()
|
|
235
|
+
model_manager.set_model(selected_model)
|
|
236
|
+
console.print(f"[green]✓ Model set to: {selected_model}[/green]")
|
|
237
|
+
console.print()
|
|
238
|
+
return True, selected_model
|
|
239
|
+
|
|
240
|
+
except KeyboardInterrupt:
|
|
241
|
+
console.print("\n[yellow]Setup cancelled.[/yellow]")
|
|
242
|
+
console.print()
|
|
243
|
+
return False, None
|
|
244
|
+
except Exception as e:
|
|
245
|
+
console.print(f"[bold red]Unexpected error:[/bold red] {e}")
|
|
246
|
+
console.print()
|
|
247
|
+
return False, None
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def success_screen(
|
|
251
|
+
database_name: str | None, auth_configured: bool, model_name: str | None = None
|
|
252
|
+
) -> None:
|
|
253
|
+
"""Display success screen after onboarding."""
|
|
254
|
+
console.print("━" * 80, style="dim")
|
|
255
|
+
console.print("[bold green]You're all set! 🚀[/bold green]")
|
|
256
|
+
console.print("━" * 80, style="dim")
|
|
257
|
+
console.print()
|
|
258
|
+
|
|
259
|
+
if database_name and auth_configured:
|
|
260
|
+
console.print(
|
|
261
|
+
f"[green]✓ Database '{database_name}' connected and ready to use[/green]"
|
|
262
|
+
)
|
|
263
|
+
console.print("[green]✓ Authentication configured[/green]")
|
|
264
|
+
if model_name:
|
|
265
|
+
console.print(f"[green]✓ Model: {model_name}[/green]")
|
|
266
|
+
elif database_name:
|
|
267
|
+
console.print(
|
|
268
|
+
f"[green]✓ Database '{database_name}' connected and ready to use[/green]"
|
|
269
|
+
)
|
|
270
|
+
console.print(
|
|
271
|
+
"[yellow]⚠ AI authentication not configured - you'll be prompted when needed[/yellow]"
|
|
272
|
+
)
|
|
273
|
+
elif auth_configured:
|
|
274
|
+
console.print("[green]✓ AI authentication configured[/green]")
|
|
275
|
+
if model_name:
|
|
276
|
+
console.print(f"[green]✓ Model: {model_name}[/green]")
|
|
277
|
+
console.print(
|
|
278
|
+
"[yellow]⚠ No database configured - you'll need to provide one via -d flag[/yellow]"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
console.print()
|
|
282
|
+
console.print("[dim]Starting interactive session...[/dim]")
|
|
283
|
+
console.print()
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
async def run_onboarding() -> bool:
|
|
287
|
+
"""Run the complete onboarding flow.
|
|
288
|
+
|
|
289
|
+
Returns True if onboarding completed successfully (at least database configured),
|
|
290
|
+
False if user cancelled or onboarding failed.
|
|
291
|
+
"""
|
|
292
|
+
try:
|
|
293
|
+
# Welcome screen
|
|
294
|
+
welcome_screen()
|
|
295
|
+
|
|
296
|
+
# Database setup
|
|
297
|
+
database_name = await setup_database_guided()
|
|
298
|
+
|
|
299
|
+
# If user cancelled database setup, exit
|
|
300
|
+
if database_name is None:
|
|
301
|
+
console.print("[yellow]Database setup is required to continue.[/yellow]")
|
|
302
|
+
console.print(
|
|
303
|
+
"[dim]You can also provide a connection string using: saber -d <connection-string>[/dim]"
|
|
304
|
+
)
|
|
305
|
+
return False
|
|
306
|
+
|
|
307
|
+
# Auth setup
|
|
308
|
+
auth_configured, model_name = await setup_auth_guided()
|
|
309
|
+
|
|
310
|
+
# Show success screen
|
|
311
|
+
success_screen(database_name, auth_configured, model_name)
|
|
312
|
+
|
|
313
|
+
return True
|
|
314
|
+
|
|
315
|
+
except KeyboardInterrupt:
|
|
316
|
+
console.print("\n[yellow]Onboarding cancelled.[/yellow]")
|
|
317
|
+
console.print(
|
|
318
|
+
"[dim]You can run setup commands manually:[/dim]\n"
|
|
319
|
+
"[dim] - saber db add <name> # Add database connection[/dim]\n"
|
|
320
|
+
"[dim] - saber auth setup # Configure authentication[/dim]"
|
|
321
|
+
)
|
|
322
|
+
sys.exit(0)
|
|
323
|
+
except Exception as e:
|
|
324
|
+
console.print(f"[bold red]Onboarding failed:[/bold red] {e}")
|
|
325
|
+
return False
|
sqlsaber/config/api_keys.py
CHANGED
|
@@ -19,7 +19,7 @@ class APIKeyManager:
|
|
|
19
19
|
|
|
20
20
|
def get_api_key(self, provider: str) -> str | None:
|
|
21
21
|
"""Get API key for the specified provider using cascading logic."""
|
|
22
|
-
env_var_name = self.
|
|
22
|
+
env_var_name = self.get_env_var_name(provider)
|
|
23
23
|
service_name = self._get_service_name(provider)
|
|
24
24
|
|
|
25
25
|
# 1. Check environment variable first
|
|
@@ -41,7 +41,7 @@ class APIKeyManager:
|
|
|
41
41
|
# 3. Prompt user for API key
|
|
42
42
|
return self._prompt_and_store_key(provider, env_var_name, service_name)
|
|
43
43
|
|
|
44
|
-
def
|
|
44
|
+
def get_env_var_name(self, provider: str) -> str:
|
|
45
45
|
"""Get the expected environment variable name for a provider."""
|
|
46
46
|
# Normalize aliases to canonical provider keys
|
|
47
47
|
key = providers.canonical(provider) or provider
|
|
@@ -4,19 +4,25 @@ sqlsaber/agents/__init__.py,sha256=qYI6rLY4q5AbF47vXH5RVoM08-yQjymBSaePh4lFIW4,1
|
|
|
4
4
|
sqlsaber/agents/base.py,sha256=40-MKEoz5rGrqVIylV1U2DaAUSPFcC75ohRin4E3-kk,2668
|
|
5
5
|
sqlsaber/agents/mcp.py,sha256=Pn8tdDRUEVLYQyEi5nHRp9MKNePwHVVoeNI-uqWcr0Y,757
|
|
6
6
|
sqlsaber/agents/pydantic_ai_agent.py,sha256=wBxKz0pjOkL-HI-TXV6B67bczZNgu7k26Rr3w5usR3o,10064
|
|
7
|
+
sqlsaber/application/__init__.py,sha256=KY_-d5nEdQyAwNOsK5r-f7Tb69c63XbuEkHPeLpJal8,84
|
|
8
|
+
sqlsaber/application/auth_setup.py,sha256=aIZ4vyfdjO5zCcMy4k4FmqjeGgruypPIRacaOJDuVRY,5080
|
|
9
|
+
sqlsaber/application/db_setup.py,sha256=VipoXACDVs8auYs7XeuNYrUWjVHpxqxWphx9yUl1_2I,6826
|
|
10
|
+
sqlsaber/application/model_selection.py,sha256=eRWga3vRpoyyTmw0BsVDRvUtyUnxvP8plAQ2Nv7tN3o,3163
|
|
11
|
+
sqlsaber/application/prompts.py,sha256=4rMGcWpYJbNWPMzqVWseUMx0nwvXOkWS6GaTAJ5mhfc,3473
|
|
7
12
|
sqlsaber/cli/__init__.py,sha256=qVSLVJLLJYzoC6aj6y9MFrzZvAwc4_OgxU9DlkQnZ4M,86
|
|
8
|
-
sqlsaber/cli/auth.py,sha256=
|
|
9
|
-
sqlsaber/cli/commands.py,sha256=
|
|
13
|
+
sqlsaber/cli/auth.py,sha256=Hz7nuHdX1iMr2UR1VVtu4EAvBYYIhsgthluqpiOXH2A,6064
|
|
14
|
+
sqlsaber/cli/commands.py,sha256=R63HF4P0JZwbGlI3TimkEUg3OUaFXYkVvLw1VIy0UFI,8516
|
|
10
15
|
sqlsaber/cli/completers.py,sha256=g-hLDq5fiBx7gg8Bte1Lq8GU-ZxCYVs4dcPsmHPIcK4,6574
|
|
11
|
-
sqlsaber/cli/database.py,sha256=
|
|
12
|
-
sqlsaber/cli/display.py,sha256=
|
|
13
|
-
sqlsaber/cli/interactive.py,sha256=
|
|
16
|
+
sqlsaber/cli/database.py,sha256=6SdxNuP8w-e2nbV937q4hxfAIwKu1MqoirogD7USaMU,10639
|
|
17
|
+
sqlsaber/cli/display.py,sha256=JQBSdLC_a3nkmJ4QfLMhhMPCYY-otS2eXF1XrfEe19E,17045
|
|
18
|
+
sqlsaber/cli/interactive.py,sha256=RG4DJN9lprfKi60eomxVt-w3gTsWoqbfEcfO36iibXM,13694
|
|
14
19
|
sqlsaber/cli/memory.py,sha256=OufHFJFwV0_GGn7LvKRTJikkWhV1IwNIUDOxFPHXOaQ,7794
|
|
15
|
-
sqlsaber/cli/models.py,sha256=
|
|
20
|
+
sqlsaber/cli/models.py,sha256=v_wrJWV5NXiM3zGLSMyNJf4YhgozK6hnf76Ua8HvysE,8480
|
|
21
|
+
sqlsaber/cli/onboarding.py,sha256=5p-rHG2eGM-7t9yg_kNDrXpY13jr__dlFII3K69VtKc,11414
|
|
16
22
|
sqlsaber/cli/streaming.py,sha256=YViLCxUv-7WN5TCphLYtAR02HXvuHYuPttGGDZKDUKU,6921
|
|
17
23
|
sqlsaber/cli/threads.py,sha256=zVlbOuD3GjjEVNebXwANKeKt4I_Lunf6itiBUL0TaKA,12877
|
|
18
24
|
sqlsaber/config/__init__.py,sha256=olwC45k8Nc61yK0WmPUk7XHdbsZH9HuUAbwnmKe3IgA,100
|
|
19
|
-
sqlsaber/config/api_keys.py,sha256=
|
|
25
|
+
sqlsaber/config/api_keys.py,sha256=7X63B-ow66GZWzYYgc_7f1T26F7gRhfyYQM4RpdkCpI,3647
|
|
20
26
|
sqlsaber/config/auth.py,sha256=b5qB2h1doXyO9Bn8z0CcL8LAR2jF431gGXBGKLgTmtQ,2756
|
|
21
27
|
sqlsaber/config/database.py,sha256=Yec6_0wdzq-ADblMNnbgvouYCimYOY_DWHT9oweaISc,11449
|
|
22
28
|
sqlsaber/config/oauth_flow.py,sha256=A3bSXaBLzuAfXV2ZPA94m9NV33c2MyL6M4ii9oEkswQ,10291
|
|
@@ -45,8 +51,8 @@ sqlsaber/tools/enums.py,sha256=CH32mL-0k9ZA18911xLpNtsgpV6tB85TktMj6uqGz54,411
|
|
|
45
51
|
sqlsaber/tools/instructions.py,sha256=X-x8maVkkyi16b6Tl0hcAFgjiYceZaSwyWTfmrvx8U8,9024
|
|
46
52
|
sqlsaber/tools/registry.py,sha256=HWOQMsNIdL4XZS6TeNUyrL-5KoSDH6PHsWd3X66o-18,3211
|
|
47
53
|
sqlsaber/tools/sql_tools.py,sha256=2xLD_pkd0t8wKndQAKIr4c9UpWzVWeHbAFpkwo5j4kY,9954
|
|
48
|
-
sqlsaber-0.
|
|
49
|
-
sqlsaber-0.
|
|
50
|
-
sqlsaber-0.
|
|
51
|
-
sqlsaber-0.
|
|
52
|
-
sqlsaber-0.
|
|
54
|
+
sqlsaber-0.27.0.dist-info/METADATA,sha256=-QcrEttuC3vwUmNdtjHzA_laIfTkcMGoghmSCvNn3nM,7138
|
|
55
|
+
sqlsaber-0.27.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
56
|
+
sqlsaber-0.27.0.dist-info/entry_points.txt,sha256=qEbOB7OffXPFgyJc7qEIJlMEX5RN9xdzLmWZa91zCQQ,162
|
|
57
|
+
sqlsaber-0.27.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
58
|
+
sqlsaber-0.27.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|