aac-cli 0.2.9__tar.gz → 0.2.10__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 (60) hide show
  1. {aac_cli-0.2.9/aac_cli.egg-info → aac_cli-0.2.10}/PKG-INFO +95 -7
  2. {aac_cli-0.2.9 → aac_cli-0.2.10}/README.md +94 -6
  3. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/__init__.py +1 -1
  4. aac_cli-0.2.10/aac_cli/__main__.py +13 -0
  5. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/aeg_cli.py +5 -0
  6. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/agent_cli.py +15 -2
  7. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/agent_health.py +21 -4
  8. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/cli.py +230 -54
  9. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/config.py +145 -35
  10. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/init_cli.py +3 -3
  11. aac_cli-0.2.10/aac_cli/interrupts.py +48 -0
  12. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/profiles.py +114 -59
  13. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/reference.py +13 -4
  14. {aac_cli-0.2.9 → aac_cli-0.2.10/aac_cli.egg-info}/PKG-INFO +95 -7
  15. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli.egg-info/SOURCES.txt +1 -0
  16. aac_cli-0.2.10/launchers/aac +36 -0
  17. aac_cli-0.2.10/launchers/aeg +33 -0
  18. {aac_cli-0.2.9 → aac_cli-0.2.10}/pyproject.toml +2 -2
  19. aac_cli-0.2.9/aac_cli/__main__.py +0 -6
  20. aac_cli-0.2.9/launchers/aac +0 -32
  21. aac_cli-0.2.9/launchers/aeg +0 -29
  22. {aac_cli-0.2.9 → aac_cli-0.2.10}/LICENSE +0 -0
  23. {aac_cli-0.2.9 → aac_cli-0.2.10}/MANIFEST.in +0 -0
  24. {aac_cli-0.2.9 → aac_cli-0.2.10}/NOTICE +0 -0
  25. {aac_cli-0.2.9 → aac_cli-0.2.10}/THIRD_PARTY_NOTICES.md +0 -0
  26. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/__init__.py +0 -0
  27. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/cli.py +0 -0
  28. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/data/action-taken-v1.json +0 -0
  29. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/data/predicates-v1.json +0 -0
  30. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/data/renderer-source.json +0 -0
  31. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/from_sidecar.py +0 -0
  32. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/inputs.py +0 -0
  33. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/model.py +0 -0
  34. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/narrative.py +0 -0
  35. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/provenance.py +0 -0
  36. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/schemas.py +0 -0
  37. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/temporal.py +0 -0
  38. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/theme.py +0 -0
  39. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/timeline_source.py +0 -0
  40. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/_aeg/visualizer.py +0 -0
  41. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/admin_key_pem.py +0 -0
  42. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/aeg_list.py +0 -0
  43. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/agent_config.py +0 -0
  44. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/agent_layout.py +0 -0
  45. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/agent_record.py +0 -0
  46. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/api_key_rotation_state.py +0 -0
  47. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/chain_list.py +0 -0
  48. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/config_render.py +0 -0
  49. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/dev_material.py +0 -0
  50. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/idp_recovery.py +0 -0
  51. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/material_cases.py +0 -0
  52. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/registration_state.py +0 -0
  53. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/secure_files.py +0 -0
  54. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/sso_login.py +0 -0
  55. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/supplied_material.py +0 -0
  56. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli/trace_query.py +0 -0
  57. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli.egg-info/dependency_links.txt +0 -0
  58. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli.egg-info/requires.txt +0 -0
  59. {aac_cli-0.2.9 → aac_cli-0.2.10}/aac_cli.egg-info/top_level.txt +0 -0
  60. {aac_cli-0.2.9 → aac_cli-0.2.10}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: aac-cli
3
- Version: 0.2.9
3
+ Version: 0.2.10
4
4
  Summary: The aac platform CLI — headless operator interface to the AAC control plane (tenant registration, chain audit).
5
5
  Author: Agent Authority Cloud Project
6
6
  License-Expression: Apache-2.0
@@ -741,6 +741,7 @@ aac profile show stage --output table # stored and effective values,
741
741
  aac profile create prod --admin-url https://aac-admin.example.com \
742
742
  --data-plane-url https://aac-data.example.com
743
743
  aac profile update main # edit the built-in baseline
744
+ aac profile update stage --unbind # clear the local tenant association
744
745
  aac profile delete prod # local only; never touches the server
745
746
  ```
746
747
 
@@ -757,6 +758,41 @@ stored value.
757
758
  for it; only `aac tenant register`, `aac init` and `aac sso login` write it.
758
759
  It is your organisation's identifier in the federation, allocated by AAC at
759
760
  registration in the form `tnt-<uuid>`, and it never changes.
761
+ A binding makes that tenant the profile's stored default; it does not grant
762
+ membership or configure an identity provider. Successful sign-in to an existing
763
+ tenant binds the profile; registration binds a newly created tenant.
764
+
765
+ A stale tenant binding affects only that profile. `aac profile list` and
766
+ `aac profile show` mark invalid bindings without printing the invalid values;
767
+ other profiles remain usable. Operational commands reject a selected invalid
768
+ profile with exit 3, even if a tenant flag or environment override is supplied.
769
+ File syntax, profile-name and size limits still apply to the entire file.
770
+
771
+ Use `aac profile update NAME --unbind` to remove the local `tenant_id` and its
772
+ saved hosted-domain metadata. Endpoints, other settings, credentials and the
773
+ server-side tenant are preserved. The flag works without prompts, may accompany
774
+ endpoint flags, and also works on `main` or an already-unbound profile. It
775
+ refuses profiles with unresolved registration or API-key rotation state;
776
+ finish that recovery first. Then bind the profile through registration or
777
+ successful sign-in. Endpoint-only updates and confirmed profile deletion also
778
+ work on stale profiles; reads never rewrite the config file.
779
+
780
+ For an existing tenant, the repair sequence is:
781
+
782
+ ```bash
783
+ aac profile update stage --unbind
784
+ # Bind profile stage to the correct existing tenant:
785
+ aac sso login --profile stage --tenant-id '<TENANT_ID>'
786
+ ```
787
+
788
+ **`aac sso login --profile stage --tenant-id '<TENANT_ID>'` binds profile
789
+ `stage` to that tenant.** Although the verb is `login`, successful authentication
790
+ also saves the tenant association; there is no separate `--bind` flag. Replace
791
+ `<TENANT_ID>` with the intended tenant's canonical ID. An endpoint correction
792
+ or expired session normally does not need
793
+ unbinding. Unbinding is local and does not log out, revoke access, or change
794
+ existing agent records. See the [profile repair guide](https://docs.cascadeauth.com/cli/guide/#repair-a-stale-profile-binding)
795
+ for the exact config fields removed and recovery constraints.
760
796
 
761
797
  `AAC_CLI_HOME` relocates the whole `~/.aac` tree. Use it for a throwaway
762
798
  test home, or to keep separate accounts hard-walled from each other:
@@ -774,13 +810,39 @@ Two credential files can coexist under `~/.aac/credentials/`:
774
810
  (`aac chain`, `aac trust-anchor`) present it. Only a hash of it exists
775
811
  on the server, so a lost key is replaced, never recovered.
776
812
  * `<tenant-id>.session` holds the short-lived session that `aac sso login`
777
- creates. The administration commands (`aac tenant`, `aac sso`) use it
778
- automatically. Sessions cannot be refreshed: when one expires, any
779
- administration command tells you to sign in again, and there is no
780
- refresh token that could be stolen.
813
+ creates. Authenticated administration commands use it automatically;
814
+ public reads such as `aac sso describe` do not use credentials.
815
+ Sessions have no refresh token. You can sign in again before expiry to
816
+ replace the cached session; after expiry, commands that need it ask you
817
+ to sign in again.
781
818
 
782
819
  ## Signing in
783
820
 
821
+ Inspect the available sign-in connections before starting a login:
822
+
823
+ ```bash
824
+ aac sso describe --profile stage # shared/platform candidates
825
+ aac sso describe --profile stage --tenant-id '<TENANT_ID>' # tenant plus shared candidates
826
+ ```
827
+
828
+ `sso describe` reads public metadata from the profile's admin endpoint and
829
+ prints the complete descriptor as JSON on stdout. No credentials or prior
830
+ sign-in are required, and it does not bind a profile or change cached credentials.
831
+ The output includes `connections`, `count`, and, for the tenant route,
832
+ `tenant_id`; connection fields describe issuers, client IDs and available flow
833
+ endpoints. An empty connection list is a successful read.
834
+
835
+ Only an explicit `--tenant-id` selects the tenant descriptor. Omitting it
836
+ always reads shared/platform candidates, even when the profile or the
837
+ `AAC_TENANT_ID` environment variable supplies a tenant. Replace `<TENANT_ID>`
838
+ with the intended tenant's canonical ID. The CLI still checks your local
839
+ configuration and selected profile. If it reports an invalid stored tenant
840
+ binding, clear it with `aac profile update stage --unbind`, then retry.
841
+ Output is JSON only (`--output json` is optional); errors go to stderr.
842
+ See the [command reference](https://docs.cascadeauth.com/cli/reference/aac-sso-describe/).
843
+
844
+ Then sign in using the connection you need:
845
+
784
846
  ```bash
785
847
  aac sso login --profile stage --idp github # developer tenant: GitHub or Google
786
848
  aac sso login --profile prod \
@@ -798,6 +860,15 @@ enterprise tenant signs in through its Microsoft Entra ID connection, named
798
860
  by the connection's exact URL with `--idp-url`; when several connections
799
861
  are registered, the error message lists the exact commands to choose one.
800
862
 
863
+ Sessions last **4 hours by default**. Your administrator may configure a
864
+ different lifetime for the deployment or connection. You can run
865
+ `aac sso login` again before expiry: successful authentication replaces the
866
+ cached session with a new one whose lifetime starts at that login. For example,
867
+ with the four-hour default, signing in at 09:00 gives an expiry of 13:00;
868
+ signing in successfully again at 12:00 gives the new session an expiry of 16:00.
869
+ Use `aac sso whoami --profile NAME --output table` to check the current
870
+ expiration. If the session expires, sign in again; no refresh token is issued.
871
+
801
872
  Sign-in uses the device flow by default, so the browser can be on another
802
873
  machine and headless hosts work. Google connections use a loopback flow
803
874
  instead, because Google's device flow is scope-restricted; `--flow`
@@ -814,11 +885,16 @@ server-returned domain. Existing agents expose read-only identities through
814
885
  or `--field workload-spiffe-id`. A successful scalar is one unquoted line;
815
886
  an unavailable value or unhealthy agent fails with empty stdout. `--field`
816
887
  cannot be combined with explicit `--output`, or with agent `--remote`.
888
+ Scalar profile inspection rejects an invalid stored binding. Structured
889
+ inspection remains available and reports invalid effective IDs as `null` with
890
+ an `error`; invalid list bindings carry `binding_error`, and show bindings carry
891
+ `binding.error`. Their invalid stored tenant IDs are redacted as `null`.
817
892
 
818
893
  Every command that reports a result prints one JSON document to standard
819
894
  output by default, with progress notes and diagnostics on standard error,
820
- so the JSON stays parseable in scripts; `--output table` prints a readable
821
- table instead. The three profile writers are the exception: `aac profile
895
+ so the JSON stays parseable in scripts; where supported, `--output table`
896
+ prints a readable table instead. `aac sso describe` supports JSON only.
897
+ The three profile writers are the exception: `aac profile
822
898
  create`, `update` and `delete` confirm on standard error, leave standard
823
899
  output empty and take no `--output`.
824
900
 
@@ -829,6 +905,18 @@ output empty and take no `--output`.
829
905
  | `2` | Usage error: an invalid flag, value or flag combination. |
830
906
  | `3` | A local configuration or state problem: profile, credential file, cached session or agent. |
831
907
  | `4` | Transport failure: an endpoint could not be reached. |
908
+ | `130` | Interrupted by the operator (Ctrl-C). |
909
+
910
+ Ctrl-C stops `aac` and CLI-owned `aeg` without a traceback, with one concise
911
+ diagnostic on standard error. A remote change may already have committed;
912
+ follow any recovery guidance and retain pending registration or key-operation
913
+ state. The CLI does not roll back or automatically retry a remote change.
914
+
915
+ Walking away from sign-in retains the existing deadlines: device sign-in waits
916
+ for the identity provider's device-code lifetime (300 seconds if absent), then
917
+ exits `1` with instructions to run login again; a PKCE browser sign-in without
918
+ a callback gives up after 300 seconds with exit `3`. HTTP timeouts remain exit
919
+ `4`. These outcomes are distinct from Ctrl-C; there is no shorter patience cap.
832
920
 
833
921
  If AAC asks you to slow down, the command shows the interval to wait. The
834
922
  CLI never replays a change on its own; wait and rerun deliberately.
@@ -714,6 +714,7 @@ aac profile show stage --output table # stored and effective values,
714
714
  aac profile create prod --admin-url https://aac-admin.example.com \
715
715
  --data-plane-url https://aac-data.example.com
716
716
  aac profile update main # edit the built-in baseline
717
+ aac profile update stage --unbind # clear the local tenant association
717
718
  aac profile delete prod # local only; never touches the server
718
719
  ```
719
720
 
@@ -730,6 +731,41 @@ stored value.
730
731
  for it; only `aac tenant register`, `aac init` and `aac sso login` write it.
731
732
  It is your organisation's identifier in the federation, allocated by AAC at
732
733
  registration in the form `tnt-<uuid>`, and it never changes.
734
+ A binding makes that tenant the profile's stored default; it does not grant
735
+ membership or configure an identity provider. Successful sign-in to an existing
736
+ tenant binds the profile; registration binds a newly created tenant.
737
+
738
+ A stale tenant binding affects only that profile. `aac profile list` and
739
+ `aac profile show` mark invalid bindings without printing the invalid values;
740
+ other profiles remain usable. Operational commands reject a selected invalid
741
+ profile with exit 3, even if a tenant flag or environment override is supplied.
742
+ File syntax, profile-name and size limits still apply to the entire file.
743
+
744
+ Use `aac profile update NAME --unbind` to remove the local `tenant_id` and its
745
+ saved hosted-domain metadata. Endpoints, other settings, credentials and the
746
+ server-side tenant are preserved. The flag works without prompts, may accompany
747
+ endpoint flags, and also works on `main` or an already-unbound profile. It
748
+ refuses profiles with unresolved registration or API-key rotation state;
749
+ finish that recovery first. Then bind the profile through registration or
750
+ successful sign-in. Endpoint-only updates and confirmed profile deletion also
751
+ work on stale profiles; reads never rewrite the config file.
752
+
753
+ For an existing tenant, the repair sequence is:
754
+
755
+ ```bash
756
+ aac profile update stage --unbind
757
+ # Bind profile stage to the correct existing tenant:
758
+ aac sso login --profile stage --tenant-id '<TENANT_ID>'
759
+ ```
760
+
761
+ **`aac sso login --profile stage --tenant-id '<TENANT_ID>'` binds profile
762
+ `stage` to that tenant.** Although the verb is `login`, successful authentication
763
+ also saves the tenant association; there is no separate `--bind` flag. Replace
764
+ `<TENANT_ID>` with the intended tenant's canonical ID. An endpoint correction
765
+ or expired session normally does not need
766
+ unbinding. Unbinding is local and does not log out, revoke access, or change
767
+ existing agent records. See the [profile repair guide](https://docs.cascadeauth.com/cli/guide/#repair-a-stale-profile-binding)
768
+ for the exact config fields removed and recovery constraints.
733
769
 
734
770
  `AAC_CLI_HOME` relocates the whole `~/.aac` tree. Use it for a throwaway
735
771
  test home, or to keep separate accounts hard-walled from each other:
@@ -747,13 +783,39 @@ Two credential files can coexist under `~/.aac/credentials/`:
747
783
  (`aac chain`, `aac trust-anchor`) present it. Only a hash of it exists
748
784
  on the server, so a lost key is replaced, never recovered.
749
785
  * `<tenant-id>.session` holds the short-lived session that `aac sso login`
750
- creates. The administration commands (`aac tenant`, `aac sso`) use it
751
- automatically. Sessions cannot be refreshed: when one expires, any
752
- administration command tells you to sign in again, and there is no
753
- refresh token that could be stolen.
786
+ creates. Authenticated administration commands use it automatically;
787
+ public reads such as `aac sso describe` do not use credentials.
788
+ Sessions have no refresh token. You can sign in again before expiry to
789
+ replace the cached session; after expiry, commands that need it ask you
790
+ to sign in again.
754
791
 
755
792
  ## Signing in
756
793
 
794
+ Inspect the available sign-in connections before starting a login:
795
+
796
+ ```bash
797
+ aac sso describe --profile stage # shared/platform candidates
798
+ aac sso describe --profile stage --tenant-id '<TENANT_ID>' # tenant plus shared candidates
799
+ ```
800
+
801
+ `sso describe` reads public metadata from the profile's admin endpoint and
802
+ prints the complete descriptor as JSON on stdout. No credentials or prior
803
+ sign-in are required, and it does not bind a profile or change cached credentials.
804
+ The output includes `connections`, `count`, and, for the tenant route,
805
+ `tenant_id`; connection fields describe issuers, client IDs and available flow
806
+ endpoints. An empty connection list is a successful read.
807
+
808
+ Only an explicit `--tenant-id` selects the tenant descriptor. Omitting it
809
+ always reads shared/platform candidates, even when the profile or the
810
+ `AAC_TENANT_ID` environment variable supplies a tenant. Replace `<TENANT_ID>`
811
+ with the intended tenant's canonical ID. The CLI still checks your local
812
+ configuration and selected profile. If it reports an invalid stored tenant
813
+ binding, clear it with `aac profile update stage --unbind`, then retry.
814
+ Output is JSON only (`--output json` is optional); errors go to stderr.
815
+ See the [command reference](https://docs.cascadeauth.com/cli/reference/aac-sso-describe/).
816
+
817
+ Then sign in using the connection you need:
818
+
757
819
  ```bash
758
820
  aac sso login --profile stage --idp github # developer tenant: GitHub or Google
759
821
  aac sso login --profile prod \
@@ -771,6 +833,15 @@ enterprise tenant signs in through its Microsoft Entra ID connection, named
771
833
  by the connection's exact URL with `--idp-url`; when several connections
772
834
  are registered, the error message lists the exact commands to choose one.
773
835
 
836
+ Sessions last **4 hours by default**. Your administrator may configure a
837
+ different lifetime for the deployment or connection. You can run
838
+ `aac sso login` again before expiry: successful authentication replaces the
839
+ cached session with a new one whose lifetime starts at that login. For example,
840
+ with the four-hour default, signing in at 09:00 gives an expiry of 13:00;
841
+ signing in successfully again at 12:00 gives the new session an expiry of 16:00.
842
+ Use `aac sso whoami --profile NAME --output table` to check the current
843
+ expiration. If the session expires, sign in again; no refresh token is issued.
844
+
774
845
  Sign-in uses the device flow by default, so the browser can be on another
775
846
  machine and headless hosts work. Google connections use a loopback flow
776
847
  instead, because Google's device flow is scope-restricted; `--flow`
@@ -787,11 +858,16 @@ server-returned domain. Existing agents expose read-only identities through
787
858
  or `--field workload-spiffe-id`. A successful scalar is one unquoted line;
788
859
  an unavailable value or unhealthy agent fails with empty stdout. `--field`
789
860
  cannot be combined with explicit `--output`, or with agent `--remote`.
861
+ Scalar profile inspection rejects an invalid stored binding. Structured
862
+ inspection remains available and reports invalid effective IDs as `null` with
863
+ an `error`; invalid list bindings carry `binding_error`, and show bindings carry
864
+ `binding.error`. Their invalid stored tenant IDs are redacted as `null`.
790
865
 
791
866
  Every command that reports a result prints one JSON document to standard
792
867
  output by default, with progress notes and diagnostics on standard error,
793
- so the JSON stays parseable in scripts; `--output table` prints a readable
794
- table instead. The three profile writers are the exception: `aac profile
868
+ so the JSON stays parseable in scripts; where supported, `--output table`
869
+ prints a readable table instead. `aac sso describe` supports JSON only.
870
+ The three profile writers are the exception: `aac profile
795
871
  create`, `update` and `delete` confirm on standard error, leave standard
796
872
  output empty and take no `--output`.
797
873
 
@@ -802,6 +878,18 @@ output empty and take no `--output`.
802
878
  | `2` | Usage error: an invalid flag, value or flag combination. |
803
879
  | `3` | A local configuration or state problem: profile, credential file, cached session or agent. |
804
880
  | `4` | Transport failure: an endpoint could not be reached. |
881
+ | `130` | Interrupted by the operator (Ctrl-C). |
882
+
883
+ Ctrl-C stops `aac` and CLI-owned `aeg` without a traceback, with one concise
884
+ diagnostic on standard error. A remote change may already have committed;
885
+ follow any recovery guidance and retain pending registration or key-operation
886
+ state. The CLI does not roll back or automatically retry a remote change.
887
+
888
+ Walking away from sign-in retains the existing deadlines: device sign-in waits
889
+ for the identity provider's device-code lifetime (300 seconds if absent), then
890
+ exits `1` with instructions to run login again; a PKCE browser sign-in without
891
+ a callback gives up after 300 seconds with exit `3`. HTTP timeouts remain exit
892
+ `4`. These outcomes are distinct from Ctrl-C; there is no shorter patience cap.
805
893
 
806
894
  If AAC asks you to slow down, the command shows the interval to wait. The
807
895
  CLI never replays a change on its own; wait and rerun deliberately.
@@ -10,4 +10,4 @@ entrypoint (`aac_cli.cli:entrypoint`).
10
10
  # test in bin/tests/test_cli_version_parity.py. A literal keeps the
11
11
  # shipped code free of import-time metadata lookups (and their
12
12
  # not-installed failure mode).
13
- __version__ = "0.2.9"
13
+ __version__ = "0.2.10"
@@ -0,0 +1,13 @@
1
+ """`python -m aac_cli` — same entrypoint as the `aac` console script."""
2
+
3
+ import sys
4
+
5
+ try:
6
+ from aac_cli.cli import entrypoint
7
+
8
+ if __name__ == "__main__":
9
+ entrypoint()
10
+ except KeyboardInterrupt:
11
+ # The shared command boundary is not loaded during early imports.
12
+ sys.stderr.write("aac: interrupted\n")
13
+ raise SystemExit(130) from None
@@ -7,6 +7,7 @@ import sys
7
7
  from importlib.resources import files
8
8
 
9
9
  from . import __version__
10
+ from .interrupts import run_command
10
11
 
11
12
 
12
13
  def version_text() -> str:
@@ -15,6 +16,10 @@ def version_text() -> str:
15
16
 
16
17
 
17
18
  def main(argv: list[str] | None = None) -> int:
19
+ return run_command(lambda: _main(argv), prog="aeg")
20
+
21
+
22
+ def _main(argv: list[str] | None = None) -> int:
18
23
  argv = list(sys.argv[1:] if argv is None else argv)
19
24
  if argv == ["--version"]:
20
25
  print(version_text())
@@ -619,13 +619,21 @@ def cmd_agent_list(args: argparse.Namespace) -> int:
619
619
  row["certificates"] = case_for(record.material_source).name
620
620
  tenant = tenant_paths(record.tenant_id) if record.tenant_id else None
621
621
  health = assess(agent, tenant, record)
622
+ row["profile_binding_consistent"] = health.profile_binding_consistent
622
623
  row["next_command"] = next_step(agent, tenant, record, health)
623
624
  rows.append(row)
624
625
  if args.output == "table":
625
626
  if not rows:
626
627
  print(f"no agents under {directory}")
627
628
  for row in rows:
628
- print(f"{row['agent']:<16}: {row.get('certificates', row.get('state', '-'))}")
629
+ binding_note = (
630
+ " (profile binding inconsistent)"
631
+ if row.get("profile_binding_consistent") is False
632
+ else ""
633
+ )
634
+ print(
635
+ f"{row['agent']:<16}: {row.get('certificates', row.get('state', '-'))}{binding_note}"
636
+ )
629
637
  return 0
630
638
  print(json.dumps({"agents": rows}, indent=2))
631
639
  return 0
@@ -708,7 +716,12 @@ def add_agent_parser(nouns: argparse._SubParsersAction) -> None:
708
716
  listing = verbs.add_parser(
709
717
  "list",
710
718
  help="List every agent under the CLI home with its tenant, case and state.",
711
- description="List every agent; an unreadable or unsupported record is reported in that agent's state without hiding the other agents.",
719
+ description=(
720
+ "List every agent; an unreadable or unsupported record is reported in that "
721
+ "agent's state without hiding the other agents. Invalid profile bindings "
722
+ "are reported per agent: JSON includes profile_binding_consistent and the "
723
+ "repair next_command; table output marks inconsistent bindings."
724
+ ),
712
725
  )
713
726
  listing.add_argument(
714
727
  "--output", choices=("json", "table"), default="json", help="Output mode (JSON by default)."
@@ -22,7 +22,13 @@ from aac_cli.agent_layout import (
22
22
  material_inventory,
23
23
  )
24
24
  from aac_cli.agent_record import STEP_MATERIAL, AgentRecord
25
- from aac_cli.config import CliConfigError, cli_home, credentials_path, read_profile_section
25
+ from aac_cli.config import (
26
+ CliConfigError,
27
+ cli_home,
28
+ credentials_path,
29
+ profile_binding_error,
30
+ read_profile_section,
31
+ )
26
32
  from aac_cli.material_cases import MATERIAL_ISSUED_HERE, MATERIAL_SUPPLIED, ca_report_label
27
33
  from aac_cli.secure_files import managed_path_problem
28
34
 
@@ -40,6 +46,7 @@ class CertificateReport:
40
46
  class AgentHealth:
41
47
  setup_complete: bool
42
48
  profile_binding_consistent: bool
49
+ invalid_profile_binding: bool = False
43
50
  missing_files: list[str] = field(default_factory=list)
44
51
  permission_problems: list[str] = field(default_factory=list)
45
52
  certificates: dict[str, CertificateReport] = field(default_factory=dict)
@@ -74,7 +81,9 @@ class AgentHealth:
74
81
  reasons: list[str] = []
75
82
  if not self.setup_complete:
76
83
  reasons.append("setup is incomplete")
77
- if not self.profile_binding_consistent:
84
+ if self.invalid_profile_binding:
85
+ reasons.append("the profile has an invalid stored tenant binding")
86
+ elif not self.profile_binding_consistent:
78
87
  reasons.append("the profile is bound to a different tenant than this agent")
79
88
  reasons.extend(f"missing: {name}" for name in self.missing_files)
80
89
  reasons.extend(self.permission_problems)
@@ -111,10 +120,16 @@ def _certificate_reports(
111
120
 
112
121
  def assess(agent: AgentPaths, tenant: TenantPaths | None, record: AgentRecord) -> AgentHealth:
113
122
  """The offline health assessment both `status` and a completed `init` report."""
114
- profile_tenant = read_profile_section(record.profile).get("tenant_id")
123
+ # Offline inventory diagnoses this agent's profile without allowing one
124
+ # stale association to abort every other agent's inventory. Operational
125
+ # profile resolution stays strict; material paths use the validated record.
126
+ stored = read_profile_section(record.profile, validate_binding=False)
127
+ invalid_binding = profile_binding_error(stored) is not None
115
128
  health = AgentHealth(
116
129
  setup_complete=record.next_step() is None,
117
- profile_binding_consistent=profile_tenant == record.tenant_id,
130
+ profile_binding_consistent=not invalid_binding
131
+ and stored.get("tenant_id") == record.tenant_id,
132
+ invalid_profile_binding=invalid_binding,
118
133
  )
119
134
  if tenant is None:
120
135
  return health
@@ -387,6 +402,8 @@ def next_step(
387
402
  ) -> str | None:
388
403
  """The one next step `status` reports and a refused `init` names; None when there is none."""
389
404
  agent_flag = f"--agent {_quote(agent.name)}"
405
+ if health.invalid_profile_binding:
406
+ return f"aac profile update {_quote(record.profile)} --unbind"
390
407
  if not health.setup_complete:
391
408
  return f"aac init --profile {_quote(record.profile)} {agent_flag}"
392
409
  if tenant is not None: