ibee-cli 0.4.0__tar.gz → 0.4.2__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.
Files changed (45) hide show
  1. {ibee_cli-0.4.0/src/ibee_cli.egg-info → ibee_cli-0.4.2}/PKG-INFO +17 -21
  2. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/README.md +15 -19
  3. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/pyproject.toml +2 -2
  4. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/__init__.py +1 -1
  5. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/block_storage.py +1 -6
  6. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/buckets.py +4 -4
  7. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/cdn.py +4 -4
  8. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/gpus.py +2 -2
  9. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/load_balancers.py +3 -3
  10. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/networking.py +2 -2
  11. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/reserved_ips.py +12 -20
  12. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/secrets.py +4 -6
  13. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/vm_lifecycle.py +4 -4
  14. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/vms.py +3 -3
  15. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/helpers.py +6 -50
  16. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/main.py +2 -2
  17. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/render.py +1 -1
  18. {ibee_cli-0.4.0 → ibee_cli-0.4.2/src/ibee_cli.egg-info}/PKG-INFO +17 -21
  19. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/requires.txt +1 -1
  20. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_billing_preflight.py +73 -4
  21. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_block_storage_cdn_commands.py +4 -5
  22. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_cli.py +1 -1
  23. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_foundations.py +41 -13
  24. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_networking_parity_commands.py +11 -10
  25. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_object_storage_commands.py +1 -1
  26. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_review_fixes.py +4 -3
  27. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_secret_store_commands.py +6 -6
  28. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_vm_parity_commands.py +3 -3
  29. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/LICENSE +0 -0
  30. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/setup.cfg +0 -0
  31. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/__init__.py +0 -0
  32. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/billing.py +0 -0
  33. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/compute.py +0 -0
  34. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/console.py +0 -0
  35. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/firewalls.py +0 -0
  36. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/commands/ops.py +0 -0
  37. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli/context.py +0 -0
  38. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/SOURCES.txt +0 -0
  39. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/dependency_links.txt +0 -0
  40. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/entry_points.txt +0 -0
  41. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/src/ibee_cli.egg-info/top_level.txt +0 -0
  42. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_cloud_vm_commands.py +0 -0
  43. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_networking_commands.py +0 -0
  44. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_parity_alignment.py +0 -0
  45. {ibee_cli-0.4.0 → ibee_cli-0.4.2}/tests/test_vm_lifecycle_commands.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ibee-cli
3
- Version: 0.4.0
3
+ Version: 0.4.2
4
4
  Summary: Official command-line interface for the IBEE Solutions cloud platform
5
5
  Author-email: IBEE Solutions <support@ibee.ai>
6
6
  License-Expression: MIT
@@ -13,7 +13,7 @@ Classifier: Environment :: Console
13
13
  Requires-Python: >=3.10
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
- Requires-Dist: ibee<0.5.0,>=0.4.0
16
+ Requires-Dist: ibee<0.5.0,>=0.4.2
17
17
  Requires-Dist: httpx>=0.27
18
18
  Requires-Dist: typer>=0.12
19
19
  Requires-Dist: click>=8.1
@@ -305,15 +305,15 @@ an interactive hidden prompt, or pipe one line to `--password-stdin`.
305
305
  Bucket and VM placement are automatic when `--site-id` is omitted. Use
306
306
  `ibee compute sites` and pass `--site-id` only when placement must be pinned.
307
307
 
308
- Billable creates send one product request. The public gateway performs the
309
- authoritative, fail-closed billing decision before routing; a valid denial is
310
- returned without calling the product service. `ibee billing eligibility`
311
- remains available as an optional point-in-time preview, and the global
312
- `--check-billing` flag (or `IBEE_CHECK_BILLING=1`) runs the portal's preflight
313
- before a billable create: the create is sent only when billing answers
314
- `allowed: true`, otherwise the portal's explanation is printed (with top-up
315
- guidance when adding credits in the portal can resolve it) and the command
316
- exits 1. The preflight never reserves funds.
308
+ All product writes reach the upstream API for fresh Billing and lifecycle decisions.
309
+ The CLI never queries billing eligibility or calculates an admission estimate automatically.
310
+ Legacy `--check-billing`, `--preflight-billing`, NAT `--preflight`,
311
+ `--billing-check/--no-billing-check`, and `IBEE_CHECK_BILLING` are deprecated no-ops.
312
+ Structural, workspace, state, scope and destructive-action checks remain in place.
313
+ `ibee billing eligibility` is an explicit diagnostic query: `allowed: false` is data (exit 0);
314
+ the explicit `--require` option retains exit 1 on denial.
315
+ It supports `REVOKE_CREDENTIAL` and `SECURITY_RECOVERY`.
316
+ Upstream errors retain their classification; suspension messages do not infer who suspended an organization.
317
317
 
318
318
  Load balancers take backends, routing, policy, health checks and L7 rules either
319
319
  as flags (`--backend TYPE:TARGET:PORT[:WEIGHT][:tls]`, `--rule
@@ -406,7 +406,7 @@ on) controls the read-only checks that need the current state.
406
406
  - **NAT gateways** exist only in `nat_gateway` VPCs (one per VPC; `nat create`
407
407
  prints the existing one instead of sending a create). A Reserved IP must be in
408
408
  the VPC's site, unattached and `reserved`. `--preflight` (or `--check-billing`)
409
- checks NAT-GATEWAY eligibility first. `nat delete` shows how many NAT VMs lose
409
+ is a deprecated no-op; upstream decides admission. `nat delete` shows how many NAT VMs lose
410
410
  outbound internet and how many forwarding rules are deleted before it asks;
411
411
  `--ip-action reserve` of a platform-assigned IP needs `--billing-catalog` (the
412
412
  RESERVED-IP SKU); deletion is refused while a virtual IP holds a Reserved IP.
@@ -422,8 +422,8 @@ on) controls the read-only checks that need the current state.
422
422
  253 characters (`--reverse-dns ""` clears it); `release` is refused while
423
423
  attached; `attach` is for unattached IPs (`move` for an IP on another VM,
424
424
  `--detach-from-service` for one on a NAT gateway or virtual IP); an attach to a VM
425
- outside a VPC points at `reserved-ips convert`. `convert` runs the RESERVED-IP
426
- billing check first (`--no-billing-check` skips it; it needs `billing.read`).
425
+ outside a VPC points at `reserved-ips convert`. The API decides admission for
426
+ `convert`.
427
427
  - **Firewalls**: group names are unique in any case (1-120 characters) and
428
428
  customers cannot create default groups. Rules: tcp and udp need `--port` (22 or
429
429
  8000-8080), icmp and any take none; sources are IPv4 addresses or CIDRs (a bare
@@ -488,8 +488,7 @@ for confirmation, the read-only checks run first (`--no-check-state` skips them)
488
488
  timeout). `purge` needs `--mode`; `--mode all` asks first; only the selector for the
489
489
  mode is accepted (`--path` 1-30, https URLs only; `--hostname`, `--tag`, `--prefix`
490
490
  1-100). A purge the CDN reports as failed exits 1.
491
- - `--check-billing` (or `--preflight-billing`) checks OBJECTST-STD before a bucket or
492
- S3 credential create and CUSTOMDO-STD before a custom domain.
491
+ - `--check-billing` and `--preflight-billing` are deprecated no-ops for storage and CDN.
493
492
  - `block-storage create --vm-type`/`--delete-on-termination`, the Block Storage delete
494
493
  idempotency key, `cdn cache-policies` and `cdn metrics` are not yet part of the
495
494
  published API contract; behaviour may change. Block Storage plans, bucket emptying,
@@ -520,10 +519,7 @@ inputs first.
520
519
  values are refused. In `patch-value`, a JSON `null` deletes that key. `--cas` is an
521
520
  integer >= 0; a check-and-set mismatch is reported as a conflict and never retried.
522
521
  `list` sends a trimmed `--query` (at most 128 characters) only when it is not blank.
523
- - **Billing**: `stores create` and `secrets create` check SECRETMA-STD eligibility
524
- first, as the portal does (`--no-billing-check` turns it off; the global
525
- `--check-billing` forces it on). When the token lacks `billing.read` the check is
526
- skipped with a warning, because the API still enforces billing on the create.
522
+ - **Billing**: store and secret writes use upstream admission. Both legacy billing-check flags are no-ops.
527
523
  - **Batch create**: every item follows the `secrets create` rules and the error names
528
524
  its index. Duplicate names are reported (the API skips them). A file with more than
529
525
  500 secrets or over 64 KiB is sent as consecutive requests and the results are
@@ -564,7 +560,7 @@ inputs first.
564
560
  | `-o`, `--output` | `IBEE_OUTPUT` | `table`, `json`, `yaml` or `id` |
565
561
  | `--json` | | Same as `-o json` |
566
562
  | `--yes`, `-y` | `IBEE_ASSUME_YES=1` | Answer yes to every confirmation |
567
- | `--check-billing` | `IBEE_CHECK_BILLING=1` | Billing preflight before billable creates |
563
+ | `--check-billing` | `IBEE_CHECK_BILLING=1` | Deprecated no-op; upstream decides admission |
568
564
 
569
565
  Without `-o`, each command keeps its usual format (tables for most lists, JSON
570
566
  for single resources and operation results). `-o yaml` prints YAML and `-o id`
@@ -280,15 +280,15 @@ an interactive hidden prompt, or pipe one line to `--password-stdin`.
280
280
  Bucket and VM placement are automatic when `--site-id` is omitted. Use
281
281
  `ibee compute sites` and pass `--site-id` only when placement must be pinned.
282
282
 
283
- Billable creates send one product request. The public gateway performs the
284
- authoritative, fail-closed billing decision before routing; a valid denial is
285
- returned without calling the product service. `ibee billing eligibility`
286
- remains available as an optional point-in-time preview, and the global
287
- `--check-billing` flag (or `IBEE_CHECK_BILLING=1`) runs the portal's preflight
288
- before a billable create: the create is sent only when billing answers
289
- `allowed: true`, otherwise the portal's explanation is printed (with top-up
290
- guidance when adding credits in the portal can resolve it) and the command
291
- exits 1. The preflight never reserves funds.
283
+ All product writes reach the upstream API for fresh Billing and lifecycle decisions.
284
+ The CLI never queries billing eligibility or calculates an admission estimate automatically.
285
+ Legacy `--check-billing`, `--preflight-billing`, NAT `--preflight`,
286
+ `--billing-check/--no-billing-check`, and `IBEE_CHECK_BILLING` are deprecated no-ops.
287
+ Structural, workspace, state, scope and destructive-action checks remain in place.
288
+ `ibee billing eligibility` is an explicit diagnostic query: `allowed: false` is data (exit 0);
289
+ the explicit `--require` option retains exit 1 on denial.
290
+ It supports `REVOKE_CREDENTIAL` and `SECURITY_RECOVERY`.
291
+ Upstream errors retain their classification; suspension messages do not infer who suspended an organization.
292
292
 
293
293
  Load balancers take backends, routing, policy, health checks and L7 rules either
294
294
  as flags (`--backend TYPE:TARGET:PORT[:WEIGHT][:tls]`, `--rule
@@ -381,7 +381,7 @@ on) controls the read-only checks that need the current state.
381
381
  - **NAT gateways** exist only in `nat_gateway` VPCs (one per VPC; `nat create`
382
382
  prints the existing one instead of sending a create). A Reserved IP must be in
383
383
  the VPC's site, unattached and `reserved`. `--preflight` (or `--check-billing`)
384
- checks NAT-GATEWAY eligibility first. `nat delete` shows how many NAT VMs lose
384
+ is a deprecated no-op; upstream decides admission. `nat delete` shows how many NAT VMs lose
385
385
  outbound internet and how many forwarding rules are deleted before it asks;
386
386
  `--ip-action reserve` of a platform-assigned IP needs `--billing-catalog` (the
387
387
  RESERVED-IP SKU); deletion is refused while a virtual IP holds a Reserved IP.
@@ -397,8 +397,8 @@ on) controls the read-only checks that need the current state.
397
397
  253 characters (`--reverse-dns ""` clears it); `release` is refused while
398
398
  attached; `attach` is for unattached IPs (`move` for an IP on another VM,
399
399
  `--detach-from-service` for one on a NAT gateway or virtual IP); an attach to a VM
400
- outside a VPC points at `reserved-ips convert`. `convert` runs the RESERVED-IP
401
- billing check first (`--no-billing-check` skips it; it needs `billing.read`).
400
+ outside a VPC points at `reserved-ips convert`. The API decides admission for
401
+ `convert`.
402
402
  - **Firewalls**: group names are unique in any case (1-120 characters) and
403
403
  customers cannot create default groups. Rules: tcp and udp need `--port` (22 or
404
404
  8000-8080), icmp and any take none; sources are IPv4 addresses or CIDRs (a bare
@@ -463,8 +463,7 @@ for confirmation, the read-only checks run first (`--no-check-state` skips them)
463
463
  timeout). `purge` needs `--mode`; `--mode all` asks first; only the selector for the
464
464
  mode is accepted (`--path` 1-30, https URLs only; `--hostname`, `--tag`, `--prefix`
465
465
  1-100). A purge the CDN reports as failed exits 1.
466
- - `--check-billing` (or `--preflight-billing`) checks OBJECTST-STD before a bucket or
467
- S3 credential create and CUSTOMDO-STD before a custom domain.
466
+ - `--check-billing` and `--preflight-billing` are deprecated no-ops for storage and CDN.
468
467
  - `block-storage create --vm-type`/`--delete-on-termination`, the Block Storage delete
469
468
  idempotency key, `cdn cache-policies` and `cdn metrics` are not yet part of the
470
469
  published API contract; behaviour may change. Block Storage plans, bucket emptying,
@@ -495,10 +494,7 @@ inputs first.
495
494
  values are refused. In `patch-value`, a JSON `null` deletes that key. `--cas` is an
496
495
  integer >= 0; a check-and-set mismatch is reported as a conflict and never retried.
497
496
  `list` sends a trimmed `--query` (at most 128 characters) only when it is not blank.
498
- - **Billing**: `stores create` and `secrets create` check SECRETMA-STD eligibility
499
- first, as the portal does (`--no-billing-check` turns it off; the global
500
- `--check-billing` forces it on). When the token lacks `billing.read` the check is
501
- skipped with a warning, because the API still enforces billing on the create.
497
+ - **Billing**: store and secret writes use upstream admission. Both legacy billing-check flags are no-ops.
502
498
  - **Batch create**: every item follows the `secrets create` rules and the error names
503
499
  its index. Duplicate names are reported (the API skips them). A file with more than
504
500
  500 secrets or over 64 KiB is sent as consecutive requests and the results are
@@ -539,7 +535,7 @@ inputs first.
539
535
  | `-o`, `--output` | `IBEE_OUTPUT` | `table`, `json`, `yaml` or `id` |
540
536
  | `--json` | | Same as `-o json` |
541
537
  | `--yes`, `-y` | `IBEE_ASSUME_YES=1` | Answer yes to every confirmation |
542
- | `--check-billing` | `IBEE_CHECK_BILLING=1` | Billing preflight before billable creates |
538
+ | `--check-billing` | `IBEE_CHECK_BILLING=1` | Deprecated no-op; upstream decides admission |
543
539
 
544
540
  Without `-o`, each command keeps its usual format (tables for most lists, JSON
545
541
  for single resources and operation results). `-o yaml` prints YAML and `-o id`
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ibee-cli"
7
- version = "0.4.0"
7
+ version = "0.4.2"
8
8
  description = "Official command-line interface for the IBEE Solutions cloud platform"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -18,7 +18,7 @@ classifiers = [
18
18
  "Environment :: Console",
19
19
  ]
20
20
  dependencies = [
21
- "ibee>=0.4.0,<0.5.0",
21
+ "ibee>=0.4.2,<0.5.0",
22
22
  "httpx>=0.27",
23
23
  "typer>=0.12",
24
24
  "click>=8.1",
@@ -1,3 +1,3 @@
1
1
  """IBEE Solutions command-line interface."""
2
2
 
3
- __version__ = "0.4.0"
3
+ __version__ = "0.4.2"
@@ -47,7 +47,6 @@ from ..helpers import (
47
47
  idempotency_key_option,
48
48
  load_json_input,
49
49
  poll_interval_option,
50
- preflight_billing,
51
50
  resolve_idempotency_key,
52
51
  resolve_wait,
53
52
  timeout_option,
@@ -264,7 +263,7 @@ def create_volume(
264
263
 
265
264
  settings, workspace, client = _session(ctx)
266
265
  key = resolve_idempotency_key(idempotency_key, "block-create", name)
267
- # Validate first (exit 2) so a bad request never reaches the billing preflight.
266
+ # Validate first (exit 2) so a bad request never reaches the API.
268
267
  body = build_block_volume_create_body(
269
268
  name=name,
270
269
  size_gb=size_gb,
@@ -277,10 +276,6 @@ def create_volume(
277
276
  vm_type=vm_type,
278
277
  delete_on_termination=delete_on_termination,
279
278
  )
280
- if settings.check_billing:
281
- preflight_billing(
282
- settings, client, workspace, sku_code=body.get("sku_code"), resource_type="block_storage"
283
- )
284
279
  try:
285
280
  with api_hints({400: CREATE_PLAN_HINT}):
286
281
  result = client.block_storage.create_block_volume(
@@ -176,7 +176,7 @@ def create_bucket(
176
176
  preflight: bool = typer.Option(
177
177
  False,
178
178
  "--preflight-billing",
179
- help="Check OBJECTST-STD billing eligibility first (same as the global --check-billing)",
179
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
180
180
  ),
181
181
  ) -> None:
182
182
  """Create a bucket."""
@@ -191,7 +191,7 @@ def create_bucket(
191
191
  object_lock_enabled=bucket_lock,
192
192
  default_retention=retention,
193
193
  tags=tag or None,
194
- preflight_billing=preflight or settings.check_billing,
194
+ preflight_billing=False,
195
195
  )
196
196
  print_json(result, id_field="name")
197
197
 
@@ -353,7 +353,7 @@ def create_credential(
353
353
  preflight: bool = typer.Option(
354
354
  False,
355
355
  "--preflight-billing",
356
- help="Check OBJECTST-STD billing eligibility first (same as the global --check-billing)",
356
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
357
357
  ),
358
358
  ) -> None:
359
359
  """Create an S3 access key; its secret is displayed only once.
@@ -368,7 +368,7 @@ def create_credential(
368
368
  permission_type=permission_type,
369
369
  bucket_scope=bucket_scope,
370
370
  allowed_buckets=allowed_bucket or None,
371
- preflight_billing=preflight or settings.check_billing,
371
+ preflight_billing=False,
372
372
  )
373
373
  # Never print only the ID: the one-time secret would be lost. -o id falls back to JSON.
374
374
  print_structured(result, "yaml" if settings.output == "yaml" else "json", id_field="access_key_id")
@@ -152,7 +152,7 @@ def create_distribution(
152
152
  preflight: bool = typer.Option(
153
153
  False,
154
154
  "--preflight-billing",
155
- help="Check billing eligibility first (same as the global --check-billing)",
155
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
156
156
  ),
157
157
  ) -> None:
158
158
  """Create a CDN distribution for a public bucket.
@@ -171,7 +171,7 @@ def create_distribution(
171
171
  origin_type=origin_type,
172
172
  cache_policy=cache_policy,
173
173
  check_origin_public=check_origin,
174
- preflight_billing=preflight or settings.check_billing,
174
+ preflight_billing=False,
175
175
  ))
176
176
 
177
177
 
@@ -327,7 +327,7 @@ def create_domain(
327
327
  preflight: bool = typer.Option(
328
328
  False,
329
329
  "--preflight-billing",
330
- help="Check CUSTOMDO-STD billing eligibility first (same as the global --check-billing)",
330
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
331
331
  ),
332
332
  ) -> None:
333
333
  """Add a custom domain (billed), then print the CNAME record to create."""
@@ -337,7 +337,7 @@ def create_domain(
337
337
  distribution_id,
338
338
  workspace_id=workspace,
339
339
  domain=domain,
340
- preflight_billing=preflight or settings.check_billing,
340
+ preflight_billing=False,
341
341
  )
342
342
  _print(result)
343
343
  if not settings.structured_output:
@@ -151,7 +151,7 @@ def create_gpu_vm(
151
151
  preflight: bool = typer.Option(
152
152
  False,
153
153
  "--preflight-billing",
154
- help="Check billing eligibility for the plan SKU first (same as the global --check-billing)",
154
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
155
155
  ),
156
156
  wait: bool = typer.Option(False, "--wait", help="Poll until the VM is provisioned"),
157
157
  timeout: Optional[float] = timeout_option(),
@@ -223,7 +223,7 @@ def delete_gpu_vm(
223
223
  reserved_ip_billing_catalog_file: Optional[str] = typer.Option(None, "--reserved-ip-billing-catalog-file"),
224
224
  requested_by: Optional[str] = typer.Option(None, "--requested-by"),
225
225
  preflight: bool = typer.Option(
226
- False, "--preflight-billing", help="With --reserve-public-ip, check billing eligibility for the Reserved IP SKU"
226
+ False, "--preflight-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
227
227
  ),
228
228
  check_state: bool = check_state_option(),
229
229
  wait: bool = typer.Option(False, "--wait", help="Poll until the VM is deleted"),
@@ -275,7 +275,7 @@ def _create(
275
275
  ) -> None:
276
276
  settings, workspace, client = _session(ctx)
277
277
  method = getattr(client.load_balancers, f"create_{layer}load_balancer")
278
- lb = method(workspace_id=workspace, check_billing=check_billing or settings.check_billing,
278
+ lb = method(workspace_id=workspace, check_billing=False,
279
279
  **{key: value for key, value in fields.items() if value is not None})
280
280
  print_json(lb, id_field=ID_FIELD)
281
281
 
@@ -333,7 +333,7 @@ def create_l4_load_balancer(
333
333
  None, "--tls", help="TLS JSON object (managed certificates only; default for tls_passthrough)"
334
334
  ),
335
335
  check_billing: bool = typer.Option(
336
- False, "--check-billing", help="Check LOADBALA-STD billing eligibility first (same as the global option)"
336
+ False, "--check-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
337
337
  ),
338
338
  ) -> None:
339
339
  """Create an L4 load balancer (TCP or TLS passthrough)."""
@@ -402,7 +402,7 @@ def create_l7_load_balancer(
402
402
  ),
403
403
  rules: Optional[str] = typer.Option(None, "--rules", help="Rules JSON array"),
404
404
  check_billing: bool = typer.Option(
405
- False, "--check-billing", help="Check LOADBALA-STD billing eligibility first (same as the global option)"
405
+ False, "--check-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
406
406
  ),
407
407
  ) -> None:
408
408
  """Create an L7 load balancer (HTTP or HTTPS)."""
@@ -529,7 +529,7 @@ def create_nat_gateway(
529
529
  ),
530
530
  preflight: bool = typer.Option(
531
531
  False, "--preflight", "--preflight-billing",
532
- help="Check NAT-GATEWAY billing eligibility first (also set by the global --check-billing)",
532
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
533
533
  ),
534
534
  check_state: bool = check_state_option(),
535
535
  ) -> None:
@@ -561,7 +561,7 @@ def create_nat_gateway(
561
561
  reserved_public_ip_id=reserved_public_ip_id,
562
562
  name=name,
563
563
  billing_catalog=catalog,
564
- preflight_billing=preflight or settings.check_billing,
564
+ preflight_billing=False,
565
565
  check_state=False,
566
566
  )
567
567
  print_json(gateway, id_field="nat_gateway_id")
@@ -2,7 +2,7 @@
2
2
 
3
3
  The Python SDK applies the portal's rules: label and reverse-DNS formats, no release
4
4
  while attached, attach versus move, no detach of a converted address that is still
5
- the VM's active IP, and the RESERVED-IP billing preflight for conversions.
5
+ the VM's active IP, and upstream admission for conversions.
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -60,7 +60,7 @@ def reserve_ip(
60
60
  None, "--billing-catalog-file", metavar="PATH", help="Read --billing-catalog from a JSON file"
61
61
  ),
62
62
  check_billing: bool = typer.Option(
63
- False, "--check-billing", help="Check RESERVED-IP billing eligibility first (same as the global option)"
63
+ False, "--check-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
64
64
  ),
65
65
  ) -> None:
66
66
  """Reserve a new public IP address (billed while reserved)."""
@@ -72,7 +72,7 @@ def reserve_ip(
72
72
  site_id=site_id,
73
73
  label=label,
74
74
  billing_catalog=catalog,
75
- check_billing=check_billing or settings.check_billing,
75
+ check_billing=False,
76
76
  )
77
77
  print_json(reserved, id_field=ID_FIELD)
78
78
 
@@ -93,7 +93,7 @@ def convert_vm_public_ip(
93
93
  billing_check: bool = typer.Option(
94
94
  True,
95
95
  "--billing-check/--no-billing-check",
96
- help="Check RESERVED-IP billing eligibility first (default: on; needs the billing.read scope)",
96
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
97
97
  ),
98
98
  ) -> None:
99
99
  """Keep a VM's current public IPv4 as a Reserved IP (VMs outside a VPC).
@@ -103,22 +103,14 @@ def convert_vm_public_ip(
103
103
 
104
104
  _settings, workspace, client = _session(ctx)
105
105
  catalog = load_json_input(billing_catalog, billing_catalog_file, "billing-catalog")
106
- try:
107
- reserved = client.reserved_ips.convert_vm_public_ip_to_reserved_ip(
108
- workspace_id=workspace,
109
- vm_id=vm_id,
110
- site_id=site_id,
111
- label=label,
112
- billing_catalog=catalog,
113
- billing_check=billing_check,
114
- )
115
- except ForbiddenError as exc:
116
- text = str(getattr(exc, "message", "") or "")
117
- if billing_check and (getattr(exc, "required_scope", None) == "billing.read" or "billing.read" in text):
118
- exc.cli_hint = ( # type: ignore[attr-defined]
119
- "The RESERVED-IP billing check needs the billing.read scope; grant it or pass --no-billing-check."
120
- )
121
- raise
106
+ reserved = client.reserved_ips.convert_vm_public_ip_to_reserved_ip(
107
+ workspace_id=workspace,
108
+ vm_id=vm_id,
109
+ site_id=site_id,
110
+ label=label,
111
+ billing_catalog=catalog,
112
+ billing_check=False,
113
+ )
122
114
  print_json(reserved, id_field=ID_FIELD)
123
115
 
124
116
 
@@ -278,8 +278,7 @@ def create_store(
278
278
  billing_check: bool = typer.Option(
279
279
  True,
280
280
  "--billing-check/--no-billing-check",
281
- help="Check SECRETMA-STD billing eligibility first, as the portal does (default: on). "
282
- "Skipped with a warning when the token lacks billing.read",
281
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
283
282
  ),
284
283
  if_exists: IfExists = typer.Option(
285
284
  IfExists.ERROR,
@@ -299,7 +298,7 @@ def create_store(
299
298
  workspace_id=workspace,
300
299
  name=body["name"],
301
300
  description=body.get("description"),
302
- preflight_billing=bool(billing_check or settings.check_billing),
301
+ preflight_billing=False,
303
302
  )
304
303
  except ConflictError as exc:
305
304
  if if_exists is not IfExists.REUSE or type(exc) is not ConflictError:
@@ -997,8 +996,7 @@ def create_secret(
997
996
  billing_check: bool = typer.Option(
998
997
  True,
999
998
  "--billing-check/--no-billing-check",
1000
- help="Check SECRETMA-STD billing eligibility first, as the portal does (default: on). "
1001
- "Skipped with a warning when the token lacks billing.read",
999
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
1002
1000
  ),
1003
1001
  ) -> None:
1004
1002
  """Create a secret in an active store."""
@@ -1016,7 +1014,7 @@ def create_secret(
1016
1014
  workspace_id=workspace,
1017
1015
  secret_name=secret_name,
1018
1016
  value=secret_value,
1019
- preflight_billing=bool(billing_check or settings.check_billing),
1017
+ preflight_billing=False,
1020
1018
  )
1021
1019
  if settings.structured_output:
1022
1020
  print_json(result)
@@ -398,7 +398,7 @@ def create_vms(
398
398
  ),
399
399
  )
400
400
  if preflight or getattr(settings, "check_billing", False):
401
- common["preflight_billing"] = True
401
+ common["preflight_billing"] = False
402
402
  results = []
403
403
  for vm_name, key in zip(names, keys):
404
404
  try:
@@ -521,7 +521,7 @@ def delete_vm(
521
521
  kwargs = resolve("reserve" if reserve else "release")
522
522
  if preflight or getattr(settings, "check_billing", False):
523
523
  if kwargs.get("public_ip_action") == "reserve":
524
- kwargs["preflight_billing"] = True
524
+ kwargs["preflight_billing"] = False
525
525
  result = _method(resource, spec, "delete")(
526
526
  vm_id=vm_id,
527
527
  workspace_id=workspace,
@@ -1041,7 +1041,7 @@ def register_vm_lifecycle(app: typer.Typer, spec: VmCommandSpec) -> None:
1041
1041
  billing_catalog: Optional[str] = typer.Option(None, "--billing-catalog", help=SNAPSHOT_SKU_HELP),
1042
1042
  billing_catalog_file: Optional[str] = typer.Option(None, "--billing-catalog-file", help="File with the SKU JSON"),
1043
1043
  preflight: bool = typer.Option(
1044
- False, "--preflight-billing", help="Check billing eligibility for the SKU first (needs billing.read)"
1044
+ False, "--preflight-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
1045
1045
  ),
1046
1046
  requested_by: Optional[str] = typer.Option(None, "--requested-by"),
1047
1047
  check_state: bool = check_state_option(),
@@ -1065,7 +1065,7 @@ def register_vm_lifecycle(app: typer.Typer, spec: VmCommandSpec) -> None:
1065
1065
  mode=mode,
1066
1066
  selected_data_volume_ids=selected_data_volume_id,
1067
1067
  requested_by=requested_by,
1068
- preflight_billing=True if preflight or getattr(settings, "check_billing", False) else None,
1068
+ preflight_billing=False,
1069
1069
  ),
1070
1070
  )
1071
1071
  snapshot_id = cell(result, "snapshot_set_id") or cell(result, "id")
@@ -103,7 +103,7 @@ def create_vm(
103
103
  ram_mb: Optional[int] = typer.Option(None, "--ram-mb", help="RAM in MB (default: the plan's; must match it)"),
104
104
  disk_gb: Optional[int] = typer.Option(None, "--disk-gb", help="Root disk in GB (default: the plan's; must match it)"),
105
105
  billing_term: Optional[str] = typer.Option(
106
- None, "--billing-term", help="Billing term: " + ", ".join(BILLING_TERMS) + " (default HOURLY, or the plan's first term)"
106
+ None, "--billing-term", help="Billing term sent to the API: " + ", ".join(BILLING_TERMS) + " (default HOURLY, or the plan's first term); admission is decided upstream"
107
107
  ),
108
108
  windows_license: Optional[str] = typer.Option(
109
109
  None,
@@ -150,7 +150,7 @@ def create_vm(
150
150
  preflight: bool = typer.Option(
151
151
  False,
152
152
  "--preflight-billing",
153
- help="Check billing eligibility for the plan SKU first (same as the global --check-billing)",
153
+ help="Deprecated no-op; upstream decides billing and lifecycle admission.",
154
154
  ),
155
155
  wait: bool = typer.Option(False, "--wait", help="Poll until the VM is provisioned"),
156
156
  timeout: Optional[float] = timeout_option(),
@@ -222,7 +222,7 @@ def delete_vm(
222
222
  reserved_ip_billing_catalog_file: Optional[str] = typer.Option(None, "--reserved-ip-billing-catalog-file"),
223
223
  requested_by: Optional[str] = typer.Option(None, "--requested-by"),
224
224
  preflight: bool = typer.Option(
225
- False, "--preflight-billing", help="With --reserve-public-ip, check billing eligibility for the Reserved IP SKU"
225
+ False, "--preflight-billing", help="Deprecated no-op; upstream decides billing and lifecycle admission."
226
226
  ),
227
227
  check_state: bool = check_state_option(),
228
228
  wait: bool = typer.Option(False, "--wait", help="Poll until the VM is deleted"),
@@ -2,8 +2,8 @@
2
2
 
3
3
  Compute writes (VM create/delete/power, access, resize, and volume actions)
4
4
  return `202 + operation_id`; these helpers build idempotency keys, optionally
5
- poll the operation to a terminal state (`--wait`), run the optional billing
6
- preflight (`--check-billing`), ask for confirmation of destructive actions,
5
+ poll the operation to a terminal state (`--wait`), retain deprecated no-op billing
6
+ flags, ask for confirmation of destructive actions,
7
7
  and parse structured values from the command line.
8
8
  """
9
9
 
@@ -549,36 +549,8 @@ def preflight_billing(
549
549
  estimated_cost_minor: Optional[int | float] = None,
550
550
  resource_type: str = "resource",
551
551
  ) -> Any:
552
- """Portal billing preflight before a billable create, when ``--check-billing`` is set.
553
-
554
- Without ``--check-billing`` (or ``IBEE_CHECK_BILLING``) nothing is called, so a
555
- create stays a single request and the API edge alone decides admission. With it,
556
- the create continues only when billing answers ``allowed`` exactly ``true``;
557
- otherwise ``BillingDeniedError`` is raised (printed with the portal wording, exit 1).
558
- """
559
-
560
- if not getattr(settings, "check_billing", False):
561
- return None
562
- billing = getattr(client, "billing", None)
563
- require = getattr(billing, "require_resource_eligibility", None)
564
- if callable(require):
565
- kwargs: dict[str, Any] = {"workspace_id": workspace_id, "resource_type": resource_type}
566
- sku = normalize_sku_code(sku_code)
567
- if sku:
568
- kwargs["sku_code"] = sku
569
- cost = normalize_estimated_cost_minor(estimated_cost_minor)
570
- if cost is not None:
571
- kwargs["estimated_cost_minor"] = cost
572
- return require(**kwargs)
573
- decision = check_billing_eligibility(
574
- client,
575
- workspace_id,
576
- sku_code=sku_code,
577
- estimated_cost_minor=estimated_cost_minor,
578
- )
579
- if _field(decision, "allowed") is not True:
580
- raise BillingDeniedError(decision=decision, create_type=resource_type)
581
- return decision
552
+ """Deprecated compatibility no-op; the mutation endpoint decides admission."""
553
+ return None
582
554
 
583
555
 
584
556
  def preflight_create(
@@ -589,24 +561,8 @@ def preflight_create(
589
561
  sku_code: Optional[str] = None,
590
562
  estimated_cost_minor: Optional[int | float] = None,
591
563
  ) -> Any:
592
- """``preflight_billing`` for a billable create command, building the client on demand.
593
-
594
- Does nothing (and sends nothing) unless ``--check-billing``/``IBEE_CHECK_BILLING`` is set.
595
- """
596
-
597
- if not getattr(settings, "check_billing", False):
598
- return None
599
- from .context import get_client, require_workspace
600
-
601
- workspace = require_workspace(settings)
602
- return preflight_billing(
603
- settings,
604
- client if client is not None else get_client(settings),
605
- workspace,
606
- sku_code=sku_code,
607
- estimated_cost_minor=estimated_cost_minor,
608
- resource_type=resource_type,
609
- )
564
+ """Deprecated compatibility no-op; no client or workspace is resolved."""
565
+ return None
610
566
 
611
567
 
612
568
  # ---------------------------------------------------------------------------
@@ -87,8 +87,8 @@ def main(
87
87
  False,
88
88
  "--check-billing",
89
89
  help=(
90
- "Ask billing before a billable create and stop when it would be denied "
91
- "(env IBEE_CHECK_BILLING=1)"
90
+ "Deprecated no-op; upstream decides mutation admission "
91
+ "(also applies to IBEE_CHECK_BILLING)"
92
92
  ),
93
93
  ),
94
94
  version: bool = typer.Option(
@@ -495,7 +495,7 @@ def _api_error_lines(exc: ApiError) -> list[str]:
495
495
  return [f"Validation failed (422): {message}"]
496
496
  if status == 423 or isinstance(exc, OrganizationSuspendedError):
497
497
  return [
498
- "Organization suspended (423): billing has suspended this organization, "
498
+ "Organization suspended (423): this organization is suspended, "
499
499
  f"so this change is blocked. {message}"
500
500
  ]
501
501
  if status == 429: