sovaria-cli 0.8.3__tar.gz → 0.8.4__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.
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/PKG-INFO +1 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/__init__.py +1 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/ai_credits.py +5 -3
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/ai_data.py +1 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/api_versions.py +8 -2
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/auth_product.py +5 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/billing.py +5 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/builds.py +9 -2
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/chrono.py +5 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/crashes.py +8 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/data.py +5 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/db.py +6 -2
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/distribute.py +8 -2
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/hosting.py +12 -4
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/iam.py +11 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/keys.py +9 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/knowledge.py +3 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/login.py +12 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/messaging.py +7 -4
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/policy.py +3 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/push.py +3 -2
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/queues.py +8 -2
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/realtime.py +3 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/scope.py +3 -3
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/storage.py +8 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/vector.py +5 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/display.py +11 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/premium_display.py +15 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/pyproject.toml +1 -1
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/.gitignore +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/README.md +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/__init__.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/__init__.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/admin.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/alerts.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/analytics.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/api.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/audit.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/auth_enterprise.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/auth_settings.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/branches.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/collections.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/compliance.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/connect.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/context.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/dashboard.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/deploy.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/env.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/experiments.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/fn_platform.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/frontends.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/gen.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/governance.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/import_cmd.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/init_cmd.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/inspect.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/integrations.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/link_cmd.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/local.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/logs.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/mcp.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/metrics.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/oidc_providers.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/org.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/org_key.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/orgs_projects.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/passkeys.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/perf.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/project.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/remote_config.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/rules.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/schema.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/secrets.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/shell.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/sim.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/simulate.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/templates.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/test_cmd.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/commands/wallet.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/local/docker-compose.yml +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/local/initdb/001_axis_tables.sql +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/local/initdb/002_axis_base_schema.sql +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/local/initdb/003_guard_policies.sql +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/local/initdb/010_local_stack.sql +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/main.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/__init__.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/api_client.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/api_helpers.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/auth_store.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/config.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/forge_local.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/importers.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/local_function_server.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/local_stack.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/log_tail.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/loopback_host.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/macho_symbols.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/migrations.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/mobile_types.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/output_mode.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/project_context.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/schema_introspect.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/scope_picker.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/sim_offline.py +0 -0
- {sovaria_cli-0.8.3 → sovaria_cli-0.8.4}/apps/cli/src/utils/theme.py +0 -0
|
@@ -15,7 +15,7 @@ from urllib.parse import quote
|
|
|
15
15
|
import typer
|
|
16
16
|
|
|
17
17
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
18
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
18
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
19
19
|
|
|
20
20
|
app = typer.Typer(help="Sovariq credits and limits (credits, never dollars)", no_args_is_help=True)
|
|
21
21
|
limits_app = typer.Typer(help="The organization's Sovariq limits: who can use it, caps per member and project",
|
|
@@ -182,8 +182,9 @@ def set_member(user_id: str = typer.Argument(..., help="The member's user id"),
|
|
|
182
182
|
|
|
183
183
|
|
|
184
184
|
@limits_app.command("remove-member")
|
|
185
|
-
def remove_member(user_id: str = typer.Argument(...), profile: str | None = _PROFILE, org_id: str | None = _ORG) -> None:
|
|
185
|
+
def remove_member(user_id: str = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG) -> None:
|
|
186
186
|
"""Remove a member's own cap (the default for members applies again)."""
|
|
187
|
+
confirm_destructive(yes, f"Remove {user_id} from Sovariq credit sharing?")
|
|
187
188
|
org = _org(profile, org_id)
|
|
188
189
|
_call("DELETE", f"/api/v1/billing/ai-limits/{_seg(org)}/members/{_seg(user_id)}", profile=profile, org_id=org)
|
|
189
190
|
typer.echo(f"{user_id}: own cap removed.")
|
|
@@ -200,8 +201,9 @@ def set_project(project: str = typer.Argument(..., help="The project id"), day:
|
|
|
200
201
|
|
|
201
202
|
|
|
202
203
|
@limits_app.command("remove-project")
|
|
203
|
-
def remove_project(project: str = typer.Argument(...), profile: str | None = _PROFILE, org_id: str | None = _ORG) -> None:
|
|
204
|
+
def remove_project(project: str = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG) -> None:
|
|
204
205
|
"""Remove a project's Sovariq cap."""
|
|
206
|
+
confirm_destructive(yes, f"Remove project {project} from Sovariq credit sharing?")
|
|
205
207
|
org = _org(profile, org_id)
|
|
206
208
|
_call("DELETE", f"/api/v1/billing/ai-limits/{_seg(org)}/projects/{_seg(project)}", profile=profile, org_id=org)
|
|
207
209
|
typer.echo(f"{project}: cap removed.")
|
|
@@ -322,7 +322,7 @@ def gateway_usage(days: int = typer.Option(7, "--days", min=1, max=90), as_json:
|
|
|
322
322
|
|
|
323
323
|
|
|
324
324
|
@gateway_app.command("cache-clear")
|
|
325
|
-
def gateway_cache_clear(profile: str | None = _PROFILE, org_id: str | None = _ORG, project_id: str | None = _PROJECT) -> None:
|
|
325
|
+
def gateway_cache_clear( yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG, project_id: str | None = _PROJECT) -> None:
|
|
326
326
|
print_json(_call("POST", "/api/v1/sovariq/gateway/cache/clear", profile=profile, org_id=org_id, project_id=project_id))
|
|
327
327
|
|
|
328
328
|
|
|
@@ -19,7 +19,12 @@ from urllib.parse import quote
|
|
|
19
19
|
import typer
|
|
20
20
|
|
|
21
21
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
22
|
-
from apps.cli.src.utils.display import
|
|
22
|
+
from apps.cli.src.utils.display import (
|
|
23
|
+
confirm_destructive,
|
|
24
|
+
handle_cli_error,
|
|
25
|
+
print_json,
|
|
26
|
+
print_success,
|
|
27
|
+
)
|
|
23
28
|
from apps.cli.src.utils.output_mode import wants_json
|
|
24
29
|
|
|
25
30
|
app = typer.Typer(help="API versions: pin function code for app builds already shipped (X-Sovaria-Api-Version)")
|
|
@@ -171,8 +176,9 @@ def retire(
|
|
|
171
176
|
|
|
172
177
|
|
|
173
178
|
@app.command("delete")
|
|
174
|
-
def delete_version(name: str, profile: str | None = Profile, org_id: str | None = Org, project_id: str | None = Project,
|
|
179
|
+
def delete_version(name: str, yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = Profile, org_id: str | None = Org, project_id: str | None = Project,
|
|
175
180
|
env: str | None = Env) -> None:
|
|
176
181
|
"""Delete a retired version."""
|
|
182
|
+
confirm_destructive(yes, f"Delete API version {name}?")
|
|
177
183
|
out = _call("DELETE", _path(name), profile=profile, org_id=org_id, project_id=project_id, env=env)
|
|
178
184
|
_done(out, f"Deleted {name}")
|
|
@@ -11,7 +11,7 @@ import typer
|
|
|
11
11
|
|
|
12
12
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
13
13
|
from apps.cli.src.utils.config import get_scope
|
|
14
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
14
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
15
15
|
from apps.cli.src.utils.premium_display import print_error_panel, run_with_spinner
|
|
16
16
|
|
|
17
17
|
app = typer.Typer(help="Manage product users within an org/project scope")
|
|
@@ -129,12 +129,14 @@ def ban_user(
|
|
|
129
129
|
@app.command("delete")
|
|
130
130
|
def delete_user(
|
|
131
131
|
user_id: str = typer.Argument(..., help="Product user id to delete"),
|
|
132
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
132
133
|
profile: str | None = typer.Option(None, "--profile"),
|
|
133
134
|
org_id: str | None = typer.Option(None, "--org"),
|
|
134
135
|
project_id: str | None = typer.Option(None, "--project"),
|
|
135
136
|
env_id: str | None = typer.Option(None, "--env"),
|
|
136
137
|
) -> None:
|
|
137
138
|
"""Delete a product user credential in scope."""
|
|
139
|
+
confirm_destructive(yes, f"Delete app user {user_id} and their sessions?")
|
|
138
140
|
safe_user_id = quote(user_id, safe="")
|
|
139
141
|
try:
|
|
140
142
|
result = call_api(
|
|
@@ -230,12 +232,14 @@ def add_roles(
|
|
|
230
232
|
def remove_roles(
|
|
231
233
|
user_id: str = typer.Argument(..., help="Product user id"),
|
|
232
234
|
roles: list[str] = typer.Argument(..., help="Roles to take away"),
|
|
235
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
233
236
|
profile: str | None = typer.Option(None, "--profile"),
|
|
234
237
|
org_id: str | None = typer.Option(None, "--org"),
|
|
235
238
|
project_id: str | None = typer.Option(None, "--project"),
|
|
236
239
|
env_id: str | None = typer.Option(None, "--env"),
|
|
237
240
|
) -> None:
|
|
238
241
|
"""Take roles away from a product user. The change applies to their requests within 20 seconds (usually at once), even on the token they already have."""
|
|
242
|
+
confirm_destructive(yes, f"Remove roles from app user {user_id}?")
|
|
239
243
|
try:
|
|
240
244
|
result = _change_roles(user_id, [], roles, profile, org_id, project_id, env_id)
|
|
241
245
|
except CLIRequestError as exc:
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
import typer
|
|
6
6
|
|
|
7
7
|
from apps.cli.src.utils.api_helpers import premium_api, premium_mutate
|
|
8
|
-
from apps.cli.src.utils.display import handle_command_error
|
|
8
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_command_error
|
|
9
9
|
from apps.cli.src.utils.project_context import resolve_org_id
|
|
10
10
|
|
|
11
11
|
app = typer.Typer(help="Billing and subscription commands (Ledger engine)")
|
|
@@ -106,9 +106,11 @@ def set_subscription(
|
|
|
106
106
|
def cancel_subscription(
|
|
107
107
|
org_id: str = typer.Argument(..., help="Organisation ID"),
|
|
108
108
|
immediate: bool = typer.Option(False, "--immediate", help="Cancel immediately"),
|
|
109
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
109
110
|
profile: str | None = typer.Option(None, "--profile"),
|
|
110
111
|
) -> None:
|
|
111
112
|
"""Cancel an organisation subscription."""
|
|
113
|
+
confirm_destructive(yes, f"Cancel the subscription of organization {org_id}?")
|
|
112
114
|
from urllib.parse import quote
|
|
113
115
|
|
|
114
116
|
params = {"immediate": "true"} if immediate else None
|
|
@@ -461,9 +463,11 @@ def account_get(
|
|
|
461
463
|
@account_app.command("cancel")
|
|
462
464
|
def account_cancel(
|
|
463
465
|
account_id: str = typer.Argument(..., help="Billing account ID"),
|
|
466
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
464
467
|
profile: str | None = typer.Option(None, "--profile"),
|
|
465
468
|
) -> None:
|
|
466
469
|
"""Cancel a billing account subscription."""
|
|
470
|
+
confirm_destructive(yes, f"Cancel billing account {account_id}?")
|
|
467
471
|
from urllib.parse import quote
|
|
468
472
|
|
|
469
473
|
premium_api(
|
|
@@ -28,7 +28,13 @@ import typer
|
|
|
28
28
|
import yaml
|
|
29
29
|
|
|
30
30
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
31
|
-
from apps.cli.src.utils.display import
|
|
31
|
+
from apps.cli.src.utils.display import (
|
|
32
|
+
confirm_destructive,
|
|
33
|
+
handle_cli_error,
|
|
34
|
+
print_json,
|
|
35
|
+
print_success,
|
|
36
|
+
print_warning,
|
|
37
|
+
)
|
|
32
38
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
33
39
|
|
|
34
40
|
app = typer.Typer(help="App builds: check schema, rules, functions and tests, then open them as a pull request")
|
|
@@ -120,9 +126,10 @@ def show(build_id: str = typer.Argument(...), profile: str | None = _PROFILE, or
|
|
|
120
126
|
|
|
121
127
|
|
|
122
128
|
@app.command("delete")
|
|
123
|
-
def delete(build_id: str = typer.Argument(...), profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
129
|
+
def delete(build_id: str = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
124
130
|
project_id: str | None = _PROJECT, env_id: str | None = _ENV) -> None:
|
|
125
131
|
"""Forget a build (an opened pull request stays on GitHub)."""
|
|
132
|
+
confirm_destructive(yes, f"Delete build {build_id}?")
|
|
126
133
|
_call("DELETE", f"/api/v1/fn/builds/{quote(build_id, safe='')}", profile=profile, org_id=org_id,
|
|
127
134
|
project_id=project_id, env_id=env_id)
|
|
128
135
|
print_success(f"Build {build_id} deleted")
|
|
@@ -7,7 +7,7 @@ from urllib.parse import quote
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from apps.cli.src.utils.api_helpers import premium_api, premium_mutate
|
|
10
|
-
from apps.cli.src.utils.display import print_json
|
|
10
|
+
from apps.cli.src.utils.display import confirm_destructive, print_json
|
|
11
11
|
|
|
12
12
|
app = typer.Typer(help="Chrono timeline and snapshot operations")
|
|
13
13
|
|
|
@@ -71,9 +71,11 @@ def timeline_spawn(
|
|
|
71
71
|
@app.command("timeline-delete")
|
|
72
72
|
def timeline_delete(
|
|
73
73
|
name: str = typer.Argument(...),
|
|
74
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
74
75
|
profile: str | None = typer.Option(None, "--profile"),
|
|
75
76
|
) -> None:
|
|
76
77
|
"""Delete a timeline."""
|
|
78
|
+
confirm_destructive(yes, f"Delete timeline {name} and its snapshots?")
|
|
77
79
|
premium_mutate(
|
|
78
80
|
f"Deleting timeline '{name}'…",
|
|
79
81
|
success="Deleted",
|
|
@@ -267,9 +269,11 @@ def snapshot_restore(
|
|
|
267
269
|
@app.command("snapshot-delete")
|
|
268
270
|
def snapshot_delete(
|
|
269
271
|
snapshot_id: int = typer.Argument(...),
|
|
272
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
270
273
|
profile: str | None = typer.Option(None, "--profile"),
|
|
271
274
|
) -> None:
|
|
272
275
|
"""Forget a snapshot (its commit stays in the history)."""
|
|
276
|
+
confirm_destructive(yes, f"Delete snapshot {snapshot_id}?")
|
|
273
277
|
premium_mutate("Deleting the snapshot…", success="Deleted", title="Snapshot", method="DELETE",
|
|
274
278
|
path=f"/api/v1/chrono/snapshots/{snapshot_id}", profile=profile)
|
|
275
279
|
|
|
@@ -24,7 +24,12 @@ import typer
|
|
|
24
24
|
|
|
25
25
|
from apps.cli.src.utils import macho_symbols
|
|
26
26
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
27
|
-
from apps.cli.src.utils.display import
|
|
27
|
+
from apps.cli.src.utils.display import (
|
|
28
|
+
confirm_destructive,
|
|
29
|
+
handle_cli_error,
|
|
30
|
+
print_json,
|
|
31
|
+
print_success,
|
|
32
|
+
)
|
|
28
33
|
from apps.cli.src.utils.output_mode import wants_json
|
|
29
34
|
|
|
30
35
|
app = typer.Typer(help="App crash reports grouped into issues (from sov.crashes in your app)")
|
|
@@ -291,10 +296,12 @@ def debug_files(
|
|
|
291
296
|
@app.command("debug-files-delete")
|
|
292
297
|
def debug_files_delete(
|
|
293
298
|
file_id: str,
|
|
299
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
294
300
|
profile: str | None = typer.Option(None, "--profile"), org_id: str | None = typer.Option(None, "--org"),
|
|
295
301
|
project_id: str | None = typer.Option(None, "--project"), env_id: str | None = typer.Option(None, "--env"),
|
|
296
302
|
) -> None:
|
|
297
303
|
"""Delete one debug file."""
|
|
304
|
+
confirm_destructive(yes, f"Delete debug file {file_id}?")
|
|
298
305
|
try:
|
|
299
306
|
call_api(method="DELETE", path=f"/api/v1/crashes/debug-files/{quote(file_id, safe='')}", **_opts(profile, org_id, project_id, env_id))
|
|
300
307
|
except CLIRequestError as exc:
|
|
@@ -8,7 +8,7 @@ from urllib.parse import quote
|
|
|
8
8
|
import typer
|
|
9
9
|
|
|
10
10
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api, parse_json_arg
|
|
11
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
11
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
12
12
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
13
13
|
|
|
14
14
|
app = typer.Typer(help="Atlas-Doc and Atlas-Rel operations")
|
|
@@ -82,12 +82,14 @@ def doc_get(
|
|
|
82
82
|
def doc_delete(
|
|
83
83
|
collection: str = typer.Argument(...),
|
|
84
84
|
document_id: str = typer.Argument(...),
|
|
85
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
85
86
|
profile: str | None = typer.Option(None, "--profile"),
|
|
86
87
|
org_id: str | None = typer.Option(None, "--org"),
|
|
87
88
|
project_id: str | None = typer.Option(None, "--project"),
|
|
88
89
|
env_id: str | None = typer.Option(None, "--env"),
|
|
89
90
|
) -> None:
|
|
90
91
|
"""Delete a document."""
|
|
92
|
+
confirm_destructive(yes, f"Delete document {collection}/{document_id}?")
|
|
91
93
|
try:
|
|
92
94
|
path = _safe_doc_path(collection, document_id)
|
|
93
95
|
result = call_api(
|
|
@@ -212,12 +214,14 @@ def table_create(
|
|
|
212
214
|
@app.command("table-drop")
|
|
213
215
|
def table_drop(
|
|
214
216
|
table_name: str = typer.Argument(...),
|
|
217
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
215
218
|
profile: str | None = typer.Option(None, "--profile"),
|
|
216
219
|
org_id: str | None = typer.Option(None, "--org"),
|
|
217
220
|
project_id: str | None = typer.Option(None, "--project"),
|
|
218
221
|
env_id: str | None = typer.Option(None, "--env"),
|
|
219
222
|
) -> None:
|
|
220
223
|
"""Drop a relational table."""
|
|
224
|
+
confirm_destructive(yes, f"Drop table {table_name} and all its rows?")
|
|
221
225
|
try:
|
|
222
226
|
result = call_api(
|
|
223
227
|
method="POST",
|
|
@@ -18,9 +18,8 @@ Commands:
|
|
|
18
18
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
|
-
import re
|
|
22
|
-
|
|
23
21
|
import json
|
|
22
|
+
import re
|
|
24
23
|
from datetime import UTC, datetime, timedelta
|
|
25
24
|
from pathlib import Path
|
|
26
25
|
|
|
@@ -28,6 +27,7 @@ import typer
|
|
|
28
27
|
|
|
29
28
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api, parse_json_arg
|
|
30
29
|
from apps.cli.src.utils.display import (
|
|
30
|
+
confirm_destructive,
|
|
31
31
|
handle_cli_error,
|
|
32
32
|
print_banner,
|
|
33
33
|
print_error,
|
|
@@ -714,12 +714,14 @@ def column_add(
|
|
|
714
714
|
def column_drop(
|
|
715
715
|
table: str = typer.Argument(...),
|
|
716
716
|
name: str = typer.Argument(..., help="Column name"),
|
|
717
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
717
718
|
profile: str | None = typer.Option(None, "--profile"),
|
|
718
719
|
org_id: str | None = typer.Option(None, "--org"),
|
|
719
720
|
project_id: str | None = typer.Option(None, "--project"),
|
|
720
721
|
env_id: str | None = typer.Option(None, "--env"),
|
|
721
722
|
) -> None:
|
|
722
723
|
"""Drop a column from a table."""
|
|
724
|
+
confirm_destructive(yes, f"Drop column {name} from {table} (its data goes too)?")
|
|
723
725
|
try:
|
|
724
726
|
result = call_api(
|
|
725
727
|
method="POST",
|
|
@@ -773,12 +775,14 @@ def fk_drop(
|
|
|
773
775
|
column: str = typer.Argument(...),
|
|
774
776
|
ref_table: str = typer.Option(..., "--ref-table"),
|
|
775
777
|
ref_column: str = typer.Option(..., "--ref-column"),
|
|
778
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
776
779
|
profile: str | None = typer.Option(None, "--profile"),
|
|
777
780
|
org_id: str | None = typer.Option(None, "--org"),
|
|
778
781
|
project_id: str | None = typer.Option(None, "--project"),
|
|
779
782
|
env_id: str | None = typer.Option(None, "--env"),
|
|
780
783
|
) -> None:
|
|
781
784
|
"""Drop a foreign key."""
|
|
785
|
+
confirm_destructive(yes, f"Drop the foreign key on {table}.{column}?")
|
|
782
786
|
try:
|
|
783
787
|
result = call_api(
|
|
784
788
|
method="POST",
|
|
@@ -22,7 +22,12 @@ import typer
|
|
|
22
22
|
|
|
23
23
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
24
24
|
from apps.cli.src.utils.config import get_scope
|
|
25
|
-
from apps.cli.src.utils.display import
|
|
25
|
+
from apps.cli.src.utils.display import (
|
|
26
|
+
confirm_destructive,
|
|
27
|
+
handle_cli_error,
|
|
28
|
+
print_json,
|
|
29
|
+
print_success,
|
|
30
|
+
)
|
|
26
31
|
from apps.cli.src.utils.output_mode import wants_json
|
|
27
32
|
|
|
28
33
|
app = typer.Typer(help="Tester builds (APK, AAB, IPA): upload, share with tester groups, invite testers")
|
|
@@ -128,8 +133,9 @@ def testers_add(emails: list[str] = typer.Argument(...), groups: str = typer.Opt
|
|
|
128
133
|
|
|
129
134
|
|
|
130
135
|
@testers_app.command("remove")
|
|
131
|
-
def testers_remove(tester_id: str, profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
136
|
+
def testers_remove(tester_id: str, yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
132
137
|
project_id: str | None = _PROJECT) -> None:
|
|
138
|
+
confirm_destructive(yes, f"Remove tester {tester_id}?")
|
|
133
139
|
_call("DELETE", f"{_base(profile, project_id)}/testers/{quote(tester_id, safe='')}", profile=profile, org_id=org_id,
|
|
134
140
|
project_id=project_id)
|
|
135
141
|
print_success("Tester removed; their install links no longer work")
|
|
@@ -28,7 +28,12 @@ import typer
|
|
|
28
28
|
|
|
29
29
|
from apps.cli.src.utils.api_client import CLIRequestError, build_request_context, call_api
|
|
30
30
|
from apps.cli.src.utils.config import get_api_url
|
|
31
|
-
from apps.cli.src.utils.display import
|
|
31
|
+
from apps.cli.src.utils.display import (
|
|
32
|
+
confirm_destructive,
|
|
33
|
+
handle_cli_error,
|
|
34
|
+
print_json,
|
|
35
|
+
print_success,
|
|
36
|
+
)
|
|
32
37
|
from apps.cli.src.utils.output_mode import wants_json
|
|
33
38
|
|
|
34
39
|
app = typer.Typer(help="Web hosting: deploy static sites (Vite, React, Next export…) to <site>.sovaria.app")
|
|
@@ -382,10 +387,11 @@ def channels_deploy(
|
|
|
382
387
|
|
|
383
388
|
|
|
384
389
|
@channels_app.command("delete")
|
|
385
|
-
def channels_delete(channel: str, site: str | None = typer.Option(None, "--site"), profile: str | None = typer.Option(None, "--profile"),
|
|
390
|
+
def channels_delete(channel: str, site: str | None = typer.Option(None, "--site"), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = typer.Option(None, "--profile"),
|
|
386
391
|
org_id: str | None = typer.Option(None, "--org"), project_id: str | None = typer.Option(None, "--project"),
|
|
387
392
|
env_id: str | None = typer.Option(None, "--env")) -> None:
|
|
388
393
|
"""Delete a preview channel now."""
|
|
394
|
+
confirm_destructive(yes, f"Delete channel {channel}?")
|
|
389
395
|
name = _site_of(site, _load_config())
|
|
390
396
|
try:
|
|
391
397
|
call_api(method="DELETE", path=f"{_BASE}/sites/{quote(name, safe='')}/channels/{quote(channel, safe='')}",
|
|
@@ -453,9 +459,10 @@ def domains_verify(domain_id: str, profile: str | None = typer.Option(None, "--p
|
|
|
453
459
|
|
|
454
460
|
|
|
455
461
|
@domains_app.command("remove")
|
|
456
|
-
def domains_remove(domain_id: str, profile: str | None = typer.Option(None, "--profile"), org_id: str | None = typer.Option(None, "--org"),
|
|
462
|
+
def domains_remove(domain_id: str, yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = typer.Option(None, "--profile"), org_id: str | None = typer.Option(None, "--org"),
|
|
457
463
|
project_id: str | None = typer.Option(None, "--project"), env_id: str | None = typer.Option(None, "--env")) -> None:
|
|
458
464
|
"""Disconnect a domain."""
|
|
465
|
+
confirm_destructive(yes, f"Remove domain {domain_id}?")
|
|
459
466
|
org, _ = _org_and_project(profile, org_id, project_id, env_id)
|
|
460
467
|
try:
|
|
461
468
|
call_api(method="DELETE", path=f"/api/v1/governance/tenants/{quote(org, safe='')}/domains/{quote(domain_id, safe='')}",
|
|
@@ -502,9 +509,10 @@ def tokens_create(name: str = typer.Option("CI", "--name"), site: str | None = t
|
|
|
502
509
|
|
|
503
510
|
|
|
504
511
|
@tokens_app.command("revoke")
|
|
505
|
-
def tokens_revoke(token_id: str, site: str | None = typer.Option(None, "--site"), profile: str | None = typer.Option(None, "--profile"),
|
|
512
|
+
def tokens_revoke(token_id: str, site: str | None = typer.Option(None, "--site"), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = typer.Option(None, "--profile"),
|
|
506
513
|
org_id: str | None = typer.Option(None, "--org"), project_id: str | None = typer.Option(None, "--project"),
|
|
507
514
|
env_id: str | None = typer.Option(None, "--env")) -> None:
|
|
515
|
+
confirm_destructive(yes, f"Revoke deploy token {token_id}?")
|
|
508
516
|
name = _site_of(site, _load_config())
|
|
509
517
|
try:
|
|
510
518
|
call_api(method="DELETE", path=f"{_BASE}/sites/{quote(name, safe='')}/tokens/{quote(token_id, safe='')}",
|
|
@@ -8,7 +8,13 @@ import typer
|
|
|
8
8
|
|
|
9
9
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api, parse_json_arg
|
|
10
10
|
from apps.cli.src.utils.api_helpers import premium_api, premium_mutate
|
|
11
|
-
from apps.cli.src.utils.display import
|
|
11
|
+
from apps.cli.src.utils.display import (
|
|
12
|
+
confirm_destructive,
|
|
13
|
+
handle_cli_error,
|
|
14
|
+
handle_command_error,
|
|
15
|
+
print_json,
|
|
16
|
+
print_success,
|
|
17
|
+
)
|
|
12
18
|
from apps.cli.src.utils.project_context import resolve_org_id
|
|
13
19
|
|
|
14
20
|
app = typer.Typer(help="Organisations, members, and project listings")
|
|
@@ -239,9 +245,11 @@ def org_set_plan(
|
|
|
239
245
|
def member_remove(
|
|
240
246
|
org_id: str = typer.Argument(..., help="Organisation ID"),
|
|
241
247
|
user_id: str = typer.Argument(..., help="User node ID"),
|
|
248
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
242
249
|
profile: str | None = typer.Option(None, "--profile"),
|
|
243
250
|
) -> None:
|
|
244
251
|
"""Remove a member from an organisation."""
|
|
252
|
+
confirm_destructive(yes, f"Remove {user_id} from organization {org_id}?")
|
|
245
253
|
premium_api(
|
|
246
254
|
"Updating organisation…",
|
|
247
255
|
success="Updated",
|
|
@@ -417,12 +425,14 @@ def roles_matrix_add(
|
|
|
417
425
|
@roles_matrix_app.command("remove")
|
|
418
426
|
def roles_matrix_remove(
|
|
419
427
|
name: str = typer.Argument(..., help="Role to remove (users holding it lose it)"),
|
|
428
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
420
429
|
profile: str | None = typer.Option(None, "--profile"),
|
|
421
430
|
org_id: str | None = typer.Option(None, "--org"),
|
|
422
431
|
project_id: str | None = typer.Option(None, "--project"),
|
|
423
432
|
env_id: str | None = typer.Option(None, "--env"),
|
|
424
433
|
) -> None:
|
|
425
434
|
"""Remove one app role, keeping the others."""
|
|
435
|
+
confirm_destructive(yes, f"Remove role {name} from the role matrix?")
|
|
426
436
|
scope = {"profile": profile, "org_id": org_id, "project_id": project_id, "env_id": env_id}
|
|
427
437
|
try:
|
|
428
438
|
roles = _matrix_lists(scope)
|
|
@@ -7,7 +7,13 @@ from urllib.parse import quote
|
|
|
7
7
|
import typer
|
|
8
8
|
|
|
9
9
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
10
|
-
from apps.cli.src.utils.display import
|
|
10
|
+
from apps.cli.src.utils.display import (
|
|
11
|
+
confirm_destructive,
|
|
12
|
+
handle_cli_error,
|
|
13
|
+
print_json,
|
|
14
|
+
print_success,
|
|
15
|
+
print_warning,
|
|
16
|
+
)
|
|
11
17
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
12
18
|
|
|
13
19
|
app = typer.Typer(help="Project API keys (anon / service_role / custom)")
|
|
@@ -125,12 +131,14 @@ def ensure_defaults(
|
|
|
125
131
|
@app.command("revoke")
|
|
126
132
|
def revoke_key(
|
|
127
133
|
key_id: str = typer.Argument(..., help="API key ID"),
|
|
134
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
128
135
|
profile: str | None = typer.Option(None, "--profile"),
|
|
129
136
|
org_id: str | None = typer.Option(None, "--org"),
|
|
130
137
|
project_id: str | None = typer.Option(None, "--project"),
|
|
131
138
|
env_id: str | None = typer.Option(None, "--env"),
|
|
132
139
|
) -> None:
|
|
133
140
|
"""Revoke an API key."""
|
|
141
|
+
confirm_destructive(yes, f"Revoke API key {key_id}? Apps using it stop working.")
|
|
134
142
|
try:
|
|
135
143
|
result = call_api(
|
|
136
144
|
method="DELETE",
|
|
@@ -11,7 +11,7 @@ from urllib.parse import quote
|
|
|
11
11
|
import typer
|
|
12
12
|
|
|
13
13
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api, parse_json_arg, stream_events
|
|
14
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
14
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
15
15
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
16
16
|
|
|
17
17
|
app = typer.Typer(help="Knowledge bases (RAG): add documents, search them with citations")
|
|
@@ -122,8 +122,10 @@ def documents(name: str = typer.Argument(...), limit: int = typer.Option(50, "--
|
|
|
122
122
|
@app.command("rm-doc")
|
|
123
123
|
def remove_document(name: str = typer.Argument(...), document_id: str = typer.Argument(...),
|
|
124
124
|
org_id: str | None = _ORG, project_id: str | None = _PROJECT, env_id: str | None = _ENV,
|
|
125
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
125
126
|
profile: str | None = _PROFILE) -> None:
|
|
126
127
|
"""Remove one document (all its sections)."""
|
|
128
|
+
confirm_destructive(yes, f"Remove document {document_id} from knowledge base {name}?")
|
|
127
129
|
print_json(_call("DELETE", f"{_KB}/{_seg(name)}/documents/{_seg(document_id)}", org_id=org_id,
|
|
128
130
|
project_id=project_id, env_id=env_id, profile=profile))
|
|
129
131
|
|
|
@@ -48,7 +48,12 @@ from apps.cli.src.utils.config import (
|
|
|
48
48
|
get_api_url,
|
|
49
49
|
get_console_url,
|
|
50
50
|
)
|
|
51
|
-
from apps.cli.src.utils.display import
|
|
51
|
+
from apps.cli.src.utils.display import (
|
|
52
|
+
confirm_destructive,
|
|
53
|
+
handle_cli_error,
|
|
54
|
+
print_success,
|
|
55
|
+
print_warning,
|
|
56
|
+
)
|
|
52
57
|
from apps.cli.src.utils.premium_display import (
|
|
53
58
|
format_http_error,
|
|
54
59
|
print_brand_panel,
|
|
@@ -701,9 +706,11 @@ def list_sessions(
|
|
|
701
706
|
@app.command("sessions-revoke")
|
|
702
707
|
def revoke_session(
|
|
703
708
|
session_id: str = typer.Argument(..., help="Session id (refresh token JTI)"),
|
|
709
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
704
710
|
profile: str | None = typer.Option(None, "--profile"),
|
|
705
711
|
) -> None:
|
|
706
712
|
"""Revoke one active session."""
|
|
713
|
+
confirm_destructive(yes, f"Sign out session {session_id}?")
|
|
707
714
|
from urllib.parse import quote
|
|
708
715
|
|
|
709
716
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
@@ -726,9 +733,11 @@ def revoke_session(
|
|
|
726
733
|
|
|
727
734
|
@app.command("sessions-revoke-others")
|
|
728
735
|
def revoke_other_sessions(
|
|
736
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
729
737
|
profile: str | None = typer.Option(None, "--profile"),
|
|
730
738
|
) -> None:
|
|
731
739
|
"""Revoke all sessions except the current one."""
|
|
740
|
+
confirm_destructive(yes, "Sign out every other session of your account?")
|
|
732
741
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
733
742
|
|
|
734
743
|
try:
|
|
@@ -1098,12 +1107,14 @@ def event_hooks_create(
|
|
|
1098
1107
|
@event_hooks_app.command("delete")
|
|
1099
1108
|
def event_hooks_delete(
|
|
1100
1109
|
hook_id: str = typer.Argument(..., help="Auth event hook ID"),
|
|
1110
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
1101
1111
|
profile: str | None = typer.Option(None, "--profile"),
|
|
1102
1112
|
org_id: str | None = typer.Option(None, "--org"),
|
|
1103
1113
|
project_id: str | None = typer.Option(None, "--project"),
|
|
1104
1114
|
env_id: str | None = typer.Option(None, "--env"),
|
|
1105
1115
|
) -> None:
|
|
1106
1116
|
"""Delete an auth event hook (owners and admins only)."""
|
|
1117
|
+
confirm_destructive(yes, f"Delete auth event hook {hook_id}?")
|
|
1107
1118
|
from urllib.parse import quote
|
|
1108
1119
|
|
|
1109
1120
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
@@ -9,7 +9,7 @@ import typer
|
|
|
9
9
|
|
|
10
10
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
11
11
|
from apps.cli.src.utils.config import get_scope
|
|
12
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
12
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
13
13
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
14
14
|
|
|
15
15
|
app = typer.Typer(help="Messages to app users over push and email: templates, audiences, sends, delivery log")
|
|
@@ -76,9 +76,10 @@ def template_save(
|
|
|
76
76
|
|
|
77
77
|
|
|
78
78
|
@app.command("template-delete")
|
|
79
|
-
def template_delete(name: str = typer.Argument(...), profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
79
|
+
def template_delete(name: str = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
80
80
|
project_id: str | None = _PROJECT, env_id: str | None = _ENV) -> None:
|
|
81
81
|
"""Delete a template."""
|
|
82
|
+
confirm_destructive(yes, f"Delete message template {name}?")
|
|
82
83
|
_call("DELETE", f"{_base(profile, project_id)}/templates/{quote(name, safe='')}", profile=profile, org_id=org_id,
|
|
83
84
|
project_id=project_id, env_id=env_id)
|
|
84
85
|
print_json({"deleted": name})
|
|
@@ -178,9 +179,10 @@ def send(
|
|
|
178
179
|
|
|
179
180
|
|
|
180
181
|
@app.command("cancel")
|
|
181
|
-
def cancel(send_id: str = typer.Argument(...), profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
182
|
+
def cancel(send_id: str = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
182
183
|
project_id: str | None = _PROJECT, env_id: str | None = _ENV) -> None:
|
|
183
184
|
"""Cancel a scheduled or running send (recipients not reached yet get nothing)."""
|
|
185
|
+
confirm_destructive(yes, f"Cancel send {send_id}?")
|
|
184
186
|
print_json(_call("POST", f"{_base(profile, project_id)}/sends/{quote(send_id, safe='')}/cancel", body={}, profile=profile,
|
|
185
187
|
org_id=org_id, project_id=project_id, env_id=env_id))
|
|
186
188
|
|
|
@@ -261,9 +263,10 @@ def in_app_save(
|
|
|
261
263
|
|
|
262
264
|
|
|
263
265
|
@app.command("in-app-delete")
|
|
264
|
-
def in_app_delete(name: str = typer.Argument(...), profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
266
|
+
def in_app_delete(name: str = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
265
267
|
project_id: str | None = _PROJECT, env_id: str | None = _ENV) -> None:
|
|
266
268
|
"""Delete an in-app message."""
|
|
269
|
+
confirm_destructive(yes, f"Delete in-app message {name}?")
|
|
267
270
|
_call("DELETE", f"{_base(profile, project_id)}/in-app/{quote(name, safe='')}", profile=profile, org_id=org_id,
|
|
268
271
|
project_id=project_id, env_id=env_id)
|
|
269
272
|
typer.echo(f"Deleted {name}")
|
|
@@ -9,7 +9,7 @@ import typer
|
|
|
9
9
|
|
|
10
10
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api, parse_json_arg
|
|
11
11
|
from apps.cli.src.utils.api_helpers import premium_api, premium_mutate
|
|
12
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
12
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
13
13
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
14
14
|
from apps.cli.src.utils.project_context import resolve_scope
|
|
15
15
|
|
|
@@ -166,12 +166,14 @@ def upsert_policy(
|
|
|
166
166
|
@app.command("delete")
|
|
167
167
|
def delete_policy(
|
|
168
168
|
policy_id: str = typer.Argument(...),
|
|
169
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
169
170
|
profile: str | None = typer.Option(None, "--profile"),
|
|
170
171
|
org_id: str | None = typer.Option(None, "--org"),
|
|
171
172
|
project_id: str | None = typer.Option(None, "--project"),
|
|
172
173
|
env_id: str | None = typer.Option(None, "--env"),
|
|
173
174
|
) -> None:
|
|
174
175
|
"""Delete (deactivate) a policy."""
|
|
176
|
+
confirm_destructive(yes, f"Delete policy {policy_id}? What it allows stops working at once.")
|
|
175
177
|
safe_id = quote(policy_id, safe="")
|
|
176
178
|
premium_mutate(
|
|
177
179
|
"Deleting policy…",
|
|
@@ -15,7 +15,7 @@ import typer
|
|
|
15
15
|
|
|
16
16
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
17
17
|
from apps.cli.src.utils.config import get_scope
|
|
18
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
18
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
19
19
|
|
|
20
20
|
app = typer.Typer(help="Push notifications: providers, web push keys, sends")
|
|
21
21
|
_ORG = typer.Option(None, "--org")
|
|
@@ -94,8 +94,9 @@ def sends(limit: int = typer.Option(20, "--limit", min=1, max=200), profile: str
|
|
|
94
94
|
|
|
95
95
|
|
|
96
96
|
@app.command("cancel")
|
|
97
|
-
def cancel(send_id: str = typer.Argument(...), profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
97
|
+
def cancel(send_id: str = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = _PROFILE, org_id: str | None = _ORG,
|
|
98
98
|
project_id: str | None = _PROJECT, env_id: str | None = _ENV) -> None:
|
|
99
99
|
"""Cancel a queued or running large send."""
|
|
100
|
+
confirm_destructive(yes, f"Cancel push send {send_id}?")
|
|
100
101
|
print_json(_call("POST", f"{_base(profile, project_id)}/sends/{quote(send_id, safe='')}/cancel", body={}, profile=profile,
|
|
101
102
|
org_id=org_id, project_id=project_id, env_id=env_id))
|
|
@@ -19,7 +19,12 @@ from urllib.parse import quote
|
|
|
19
19
|
import typer
|
|
20
20
|
|
|
21
21
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api
|
|
22
|
-
from apps.cli.src.utils.display import
|
|
22
|
+
from apps.cli.src.utils.display import (
|
|
23
|
+
confirm_destructive,
|
|
24
|
+
handle_cli_error,
|
|
25
|
+
print_json,
|
|
26
|
+
print_success,
|
|
27
|
+
)
|
|
23
28
|
from apps.cli.src.utils.output_mode import wants_json
|
|
24
29
|
|
|
25
30
|
app = typer.Typer(help="Message queues: send, read with a visibility timeout, archive, dead letters, metrics, consumers")
|
|
@@ -264,9 +269,10 @@ def archive(name: str, msg_ids: list[int] = typer.Argument(...), profile: str |
|
|
|
264
269
|
|
|
265
270
|
|
|
266
271
|
@app.command("delete-messages")
|
|
267
|
-
def delete_messages(name: str, msg_ids: list[int] = typer.Argument(...), profile: str | None = Profile,
|
|
272
|
+
def delete_messages(name: str, msg_ids: list[int] = typer.Argument(...), yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = Profile,
|
|
268
273
|
org_id: str | None = Org, project_id: str | None = Project, env: str | None = Env) -> None:
|
|
269
274
|
"""Delete messages for good."""
|
|
275
|
+
confirm_destructive(yes, f"Delete these messages from queue {name}?")
|
|
270
276
|
out = _call("POST", _path(name, "delete"), body={"msg_ids": msg_ids}, profile=profile, org_id=org_id,
|
|
271
277
|
project_id=project_id, env=env)
|
|
272
278
|
print_success(f"Deleted {len(out.get('deleted') or [])} of {len(msg_ids)}")
|
|
@@ -10,7 +10,7 @@ import typer
|
|
|
10
10
|
from apps.cli.src.utils.api_client import CLIRequestError, parse_json_arg
|
|
11
11
|
from apps.cli.src.utils.api_helpers import premium_api
|
|
12
12
|
from apps.cli.src.utils.config import get_api_url, get_scope
|
|
13
|
-
from apps.cli.src.utils.display import handle_cli_error
|
|
13
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error
|
|
14
14
|
from apps.cli.src.utils.premium_display import print_brand_panel, print_error_panel
|
|
15
15
|
|
|
16
16
|
app = typer.Typer(help="Realtime stream and E2EE operations")
|
|
@@ -302,12 +302,14 @@ def e2ee_revoke(
|
|
|
302
302
|
channel: str = typer.Argument(...),
|
|
303
303
|
key_id: str | None = typer.Option(None, "--key-id"),
|
|
304
304
|
reason: str | None = typer.Option(None, "--reason"),
|
|
305
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
305
306
|
profile: str | None = typer.Option(None, "--profile"),
|
|
306
307
|
org_id: str | None = typer.Option(None, "--org"),
|
|
307
308
|
project_id: str | None = typer.Option(None, "--project"),
|
|
308
309
|
env_id: str | None = typer.Option(None, "--env"),
|
|
309
310
|
) -> None:
|
|
310
311
|
"""Revoke an E2EE key."""
|
|
312
|
+
confirm_destructive(yes, f"Revoke end-to-end encryption keys of channel {channel}?")
|
|
311
313
|
premium_api(
|
|
312
314
|
"Revoking E2EE key…",
|
|
313
315
|
success="E2EE key revoked",
|
|
@@ -4,10 +4,10 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import typer
|
|
6
6
|
|
|
7
|
-
from apps.cli.src.utils.api_helpers import require_login
|
|
8
7
|
from apps.cli.src.utils.api_client import CLIRequestError
|
|
9
|
-
from apps.cli.src.utils.
|
|
8
|
+
from apps.cli.src.utils.api_helpers import require_login
|
|
10
9
|
from apps.cli.src.utils.config import clear_scope, get_scope, set_scope
|
|
10
|
+
from apps.cli.src.utils.display import handle_cli_error, print_success
|
|
11
11
|
from apps.cli.src.utils.premium_display import print_next_steps, print_scope_card
|
|
12
12
|
from apps.cli.src.utils.scope_picker import interactive_pick_scope, print_access_catalog
|
|
13
13
|
|
|
@@ -53,7 +53,7 @@ def set_scope_cmd(
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
@app.command("clear")
|
|
56
|
-
def clear_scope_cmd(profile: str | None = typer.Option(None, "--profile")) -> None:
|
|
56
|
+
def clear_scope_cmd( yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),profile: str | None = typer.Option(None, "--profile")) -> None:
|
|
57
57
|
"""Clear default scope for the active profile."""
|
|
58
58
|
clear_scope(profile)
|
|
59
59
|
print_success("Default scope cleared")
|
|
@@ -24,7 +24,12 @@ import httpx
|
|
|
24
24
|
import typer
|
|
25
25
|
|
|
26
26
|
from apps.cli.src.utils.api_client import CLIRequestError, build_request_context, call_api
|
|
27
|
-
from apps.cli.src.utils.display import
|
|
27
|
+
from apps.cli.src.utils.display import (
|
|
28
|
+
confirm_destructive,
|
|
29
|
+
handle_cli_error,
|
|
30
|
+
print_json,
|
|
31
|
+
print_success,
|
|
32
|
+
)
|
|
28
33
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
29
34
|
|
|
30
35
|
app = typer.Typer(help="Object storage management")
|
|
@@ -323,9 +328,11 @@ def delete_object(
|
|
|
323
328
|
project_id: str | None = typer.Option(None, "--project", help="Project ID"),
|
|
324
329
|
org_id: str | None = typer.Option(None, "--org"),
|
|
325
330
|
env_id: str | None = typer.Option(None, "--env"),
|
|
331
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
326
332
|
profile: str | None = typer.Option(None, "--profile"),
|
|
327
333
|
) -> None:
|
|
328
334
|
"""Delete an object from a bucket."""
|
|
335
|
+
confirm_destructive(yes, f"Delete {bucket}/{object_path}?")
|
|
329
336
|
try:
|
|
330
337
|
call_api(
|
|
331
338
|
method="DELETE",
|
|
@@ -8,7 +8,7 @@ from urllib.parse import quote
|
|
|
8
8
|
import typer
|
|
9
9
|
|
|
10
10
|
from apps.cli.src.utils.api_client import CLIRequestError, call_api, parse_json_arg
|
|
11
|
-
from apps.cli.src.utils.display import handle_cli_error, print_json
|
|
11
|
+
from apps.cli.src.utils.display import confirm_destructive, handle_cli_error, print_json
|
|
12
12
|
from apps.cli.src.utils.premium_display import run_with_spinner
|
|
13
13
|
|
|
14
14
|
app = typer.Typer(help="Vector search and embeddings: collections and SQL tables")
|
|
@@ -371,9 +371,11 @@ def delete_record(
|
|
|
371
371
|
org_id: str | None = typer.Option(None, "--org"),
|
|
372
372
|
project_id: str | None = typer.Option(None, "--project"),
|
|
373
373
|
env_id: str | None = typer.Option(None, "--env"),
|
|
374
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
374
375
|
profile: str | None = typer.Option(None, "--profile"),
|
|
375
376
|
) -> None:
|
|
376
377
|
"""Delete one record from a vector collection."""
|
|
378
|
+
confirm_destructive(yes, f"Delete record {record_id} from {collection}?")
|
|
377
379
|
try:
|
|
378
380
|
result = call_api(
|
|
379
381
|
method="DELETE",
|
|
@@ -398,9 +400,11 @@ def delete_collection(
|
|
|
398
400
|
org_id: str | None = typer.Option(None, "--org"),
|
|
399
401
|
project_id: str | None = typer.Option(None, "--project"),
|
|
400
402
|
env_id: str | None = typer.Option(None, "--env"),
|
|
403
|
+
yes: bool = typer.Option(False, "--yes", "-y", help="Don't ask"),
|
|
401
404
|
profile: str | None = typer.Option(None, "--profile"),
|
|
402
405
|
) -> None:
|
|
403
406
|
"""Delete an entire vector collection."""
|
|
407
|
+
confirm_destructive(yes, f"Delete vector collection {collection} and every record in it?")
|
|
404
408
|
try:
|
|
405
409
|
result = call_api(
|
|
406
410
|
method="DELETE",
|
|
@@ -100,3 +100,14 @@ def handle_command_error(exc: Exception) -> None:
|
|
|
100
100
|
|
|
101
101
|
print_error_panel(str(exc))
|
|
102
102
|
raise typer.Exit(1) from exc
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def confirm_destructive(yes: bool, question: str) -> None:
|
|
106
|
+
"""Ask before a destructive command in an interactive terminal; ``--yes`` skips it. Scripts and CI (no terminal)
|
|
107
|
+
run as they always did, so adding the question to a command that never asked breaks nobody's automation."""
|
|
108
|
+
import sys
|
|
109
|
+
|
|
110
|
+
if yes or not (sys.stdin.isatty() and sys.stdout.isatty()):
|
|
111
|
+
return
|
|
112
|
+
if not typer.confirm(f"{question} This can't be undone.", default=False):
|
|
113
|
+
raise typer.Exit(1)
|
|
@@ -209,6 +209,20 @@ def _detail_text(detail: Any) -> str:
|
|
|
209
209
|
# ── Smart response rendering ──────────────────────────────────────────────────
|
|
210
210
|
|
|
211
211
|
|
|
212
|
+
def readable_times(value: Any, key: str = "") -> Any:
|
|
213
|
+
"""Unix times in ``*_at`` fields as UTC dates for people (``expires_at 1793962538.2`` read as nothing);
|
|
214
|
+
--json output keeps the numbers."""
|
|
215
|
+
if isinstance(value, dict):
|
|
216
|
+
return {k: readable_times(v, str(k)) for k, v in value.items()}
|
|
217
|
+
if isinstance(value, list):
|
|
218
|
+
return [readable_times(v, key) for v in value]
|
|
219
|
+
if key.endswith("_at") and isinstance(value, (int, float)) and not isinstance(value, bool) and 1e9 <= value < 1e11:
|
|
220
|
+
from datetime import UTC, datetime
|
|
221
|
+
|
|
222
|
+
return datetime.fromtimestamp(value, UTC).strftime("%Y-%m-%d %H:%M UTC")
|
|
223
|
+
return value
|
|
224
|
+
|
|
225
|
+
|
|
212
226
|
def print_smart(
|
|
213
227
|
payload: Any,
|
|
214
228
|
*,
|
|
@@ -223,6 +237,7 @@ def print_smart(
|
|
|
223
237
|
if payload is None:
|
|
224
238
|
_console.print("[dim](empty response)[/]")
|
|
225
239
|
return
|
|
240
|
+
payload = readable_times(payload)
|
|
226
241
|
|
|
227
242
|
if isinstance(payload, list) and not payload:
|
|
228
243
|
title_lower = (title or "").lower()
|
|
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
|
|
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
|