healthcloud-cli 3.7.1__tar.gz → 3.7.3__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.
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/PKG-INFO +107 -18
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/README.md +106 -17
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/agents/commands.py +79 -10
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/agents/operations.py +149 -24
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/errors.py +43 -33
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/pretty.py +128 -1
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/cli.py +17 -9
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/contracts/mcp-tools.manifest.json +533 -3
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/output.py +13 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/pyproject.toml +1 -1
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/.gitignore +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/hatch_build.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/admins/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/admins/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/admins/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/admins/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/agents/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/agents/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/agents/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/auth.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/compatibility.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/context.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/data_sources/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/data_sources/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/data_sources/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/data_sources/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/data_sources/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/departments/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/departments/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/departments/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/departments/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/departments/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/domain_errors.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/guardrails/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/guardrails/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/guardrails/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/guardrails/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/guardrails/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/organization/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/organization/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/organization/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/organization/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/organization/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/output.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/policies/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/policies/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/policies/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/policies/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/policies/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/registry.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/results.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/session.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/sessions/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/sessions/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/sessions/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/sessions/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/sessions/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/skills/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/skills/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/skills/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/skills/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/skills/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/target.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/teams/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/teams/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/teams/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/teams/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/teams/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/tools/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/tools/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/tools/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/tools/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/tools/projections.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/tuning.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/ui.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/ams/windows_files.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/context.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/contracts/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/main.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/manifest.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/params.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/tenants/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/tenants/commands.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/tenants/group.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/tenants/operations.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/healthcloud_cli/util.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/scripts/__init__.py +0 -0
- {healthcloud_cli-3.7.1 → healthcloud_cli-3.7.3}/scripts/bundle_manifest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: healthcloud-cli
|
|
3
|
-
Version: 3.7.
|
|
3
|
+
Version: 3.7.3
|
|
4
4
|
Summary: Command-line interface for the HealthCloud MCP gateway, generated from the shared MCP tool manifest.
|
|
5
5
|
Project-URL: Issues, https://github.com/healthcloudservices/healthcloud-services/issues
|
|
6
6
|
Project-URL: Repository, https://github.com/healthcloudservices/healthcloud-services
|
|
@@ -316,6 +316,27 @@ otherwise it prompts (with hidden password input). It stores only the email,
|
|
|
316
316
|
tenant/environment binding, access/refresh tokens and expiry in the strict
|
|
317
317
|
owner-only `~/.healthcloud/cli/ams-session.json`; it never stores the password.
|
|
318
318
|
|
|
319
|
+
By default (`pretty`) these three commands print readable lines — never a raw
|
|
320
|
+
JSON blob and never a token:
|
|
321
|
+
|
|
322
|
+
```text
|
|
323
|
+
$ hc login --environment dev --tenant-id TENANT_ID
|
|
324
|
+
saved session: dev / TENANT_ID
|
|
325
|
+
email: admin@example.test
|
|
326
|
+
expires_at: 2026-11-02T01:42:29Z
|
|
327
|
+
$ hc logout
|
|
328
|
+
saved session removed: true
|
|
329
|
+
server session revoked: false
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
`hc login --status` prints the same three lines for the saved session, and a
|
|
333
|
+
second `hc logout` reports `saved session removed: false`. `--output json` (and
|
|
334
|
+
`--output raw`, which these commands treat as JSON) prints exactly the objects
|
|
335
|
+
they have always printed: `{"schema_version": 1, "ok": true, "session":
|
|
336
|
+
{"schema_version": 1, "environment", "tenant_id", "email", "expires_at"}}`
|
|
337
|
+
for `login`/`--status`, and `{"schema_version": 1, "ok": true, "removed":
|
|
338
|
+
true|false, "server_session_revoked": false}` for `logout`.
|
|
339
|
+
|
|
319
340
|
For CI and agents, log in non-interactively: pass `--email` and pipe the password
|
|
320
341
|
to `--password-stdin` (read as exactly one stdin line, never echoed, never a
|
|
321
342
|
flag value — there is no `--password` option):
|
|
@@ -545,11 +566,33 @@ explicitly that
|
|
|
545
566
|
fields — a disabled agent can still be selected by those paths — and that
|
|
546
567
|
in-flight runs, WSS sessions and schedules are untouched by this route.
|
|
547
568
|
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
569
|
+
Default `pretty` output prints those facts as labelled lines — `transition`,
|
|
570
|
+
`requested`, `evidence` (`saved`, `side_effects`, `runtime`), `eligibility`,
|
|
571
|
+
`mutation_replayed: false` — and closes with a `verification:` block, so a
|
|
572
|
+
deactivate reads `transition: deactivate`, then
|
|
573
|
+
`evidence: / saved: / status: confirmed`, then `verification: / saved_state:
|
|
574
|
+
confirmed`, `side_effects: unverified`, `runtime: unverified`; it is never one
|
|
575
|
+
raw JSON line. `--output json` prints the full envelope above, byte-identical to
|
|
576
|
+
before. An unknown outcome still prints its one-line `error:` plus the
|
|
577
|
+
reconciliation on stderr.
|
|
578
|
+
|
|
579
|
+
Without `--confirm` the command is a **preview**: it re-reads the agent exactly
|
|
580
|
+
once (the same one bounded `GET`), runs the same local pre-checks, prints
|
|
581
|
+
`preview: --confirm was not passed, so nothing has been sent.` and then the
|
|
582
|
+
resolved change to stderr — the target (`agent_id`, `name`, `type`,
|
|
583
|
+
`config_version`), `status` and `active` before -> after, and
|
|
584
|
+
`one PUT /agents/{agent_id} with body keys: status, active` — and exits 2 having
|
|
585
|
+
sent nothing. With `--confirm` the write path is unchanged: it re-reads before
|
|
586
|
+
the single `PUT`, so a preview is never an approval token. The preview text is
|
|
587
|
+
suppressed under `--output json`/`raw` (the same rule the confirmed path's
|
|
588
|
+
review follows) so those modes keep stderr machine-parsable.
|
|
589
|
+
|
|
590
|
+
Pre-checks refuse with exit 1 and **no write**: an agent outside the explicit
|
|
591
|
+
tenant, an already in-effect state, an unsupported `archived`/`draft` target
|
|
592
|
+
(named as having no supported reversible transition here), or a
|
|
593
|
+
`config_version` that no longer matches `--expected-config-version` — all of
|
|
594
|
+
these are also enforced by the preview, which still sends nothing. A missing
|
|
595
|
+
`--confirm` exits 2. That version check is a local best-effort
|
|
553
596
|
guard only: the route has no `If-Match` and the server never compares the
|
|
554
597
|
version, so it cannot prove atomicity and a request could still race between
|
|
555
598
|
the read and the PUT.
|
|
@@ -615,20 +658,34 @@ server audit log records an `UPDATE` with the payload. Runtime enforcement of
|
|
|
615
658
|
the new references is unobservable: no serving readback route exists, so this
|
|
616
659
|
command never claims it.
|
|
617
660
|
|
|
661
|
+
Default `pretty` output prints the same facts as labelled lines: `agent_id`,
|
|
662
|
+
`changed` (each field with `before`/`after`/`added`/`removed`),
|
|
663
|
+
`config_version: {before, after}`, `evidence` (`saved`, `side_effects`,
|
|
664
|
+
`runtime`), `mutation_replayed: false`, and a closing `verification:` block.
|
|
665
|
+
`--output json` prints the full envelope described here, unchanged.
|
|
666
|
+
|
|
618
667
|
`tool_ids` and `data_source_ids` address **unscoped global catalogs** by design;
|
|
619
668
|
the server checks tenant scope only for the department, team, skill, policy and
|
|
620
669
|
guardrail references. This command does not imply otherwise, and it does not
|
|
621
670
|
re-implement the server's reference checks.
|
|
622
671
|
|
|
623
|
-
|
|
624
|
-
|
|
672
|
+
Without `--confirm` the command is a **preview**: it reads the agent exactly once
|
|
673
|
+
(the same one bounded `GET`), computes the same reviewed diff and runs the same
|
|
674
|
+
local pre-checks, prints `preview: --confirm was not passed, so nothing has been
|
|
675
|
+
sent.` followed by the reviewed diff, and exits 2 having sent no `PUT`. With
|
|
676
|
+
`--confirm` the write path is unchanged: it re-reads before the single `PUT`, so
|
|
677
|
+
a preview is never an approval token. The preview text is suppressed under
|
|
678
|
+
`--output json`/`raw`, like the confirmed path's review.
|
|
679
|
+
|
|
680
|
+
Pre-checks refuse with exit 1 and **no write**: no assignment option at all
|
|
625
681
|
(exit 2), setting and clearing the same family together (exit 2), an agent
|
|
626
682
|
outside the explicit tenant, a request where every reference already equals the
|
|
627
683
|
read state ("already in effect"), or a `config_version` that no longer matches
|
|
628
|
-
`--expected-config-version
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
the
|
|
684
|
+
`--expected-config-version` — all of these are also enforced by the preview,
|
|
685
|
+
which still sends nothing. A missing `--confirm` exits 2. That version check is
|
|
686
|
+
a local best-effort guard only: the route has no `If-Match` and the server never
|
|
687
|
+
compares the version, so it cannot prove atomicity and a request could still
|
|
688
|
+
race between the read and the PUT. Operation-specific approval here is the CLI's own `--confirm` plus the
|
|
632
689
|
reviewed diff, deliberately separate from the tuning/plan-apply approval path;
|
|
633
690
|
the server enforces tenant isolation, reference existence and the audit log, and
|
|
634
691
|
it has **no** assignment approval or consent entity.
|
|
@@ -689,10 +746,24 @@ of restore and has no `--force`, no `--dry-run`, no interactive prompt and no
|
|
|
689
746
|
`--expected-config-version` — the route has no `If-Match` or version check, so a
|
|
690
747
|
version flag would advertise protection the server does not offer.
|
|
691
748
|
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
749
|
+
Default `pretty` output prints the result as labelled lines next to the stderr
|
|
750
|
+
disclosure: `agent_id`, `deleted: true`, `target`, `disclosure`, `evidence`
|
|
751
|
+
(`deleted`/`side_effects`/`runtime`), `mutation_replayed: false`, and a closing
|
|
752
|
+
`verification:` block. `--output json` prints the full envelope, unchanged. The
|
|
753
|
+
reviewed target and disclosure above are still printed to stderr in `pretty`
|
|
754
|
+
mode only; `--output json`/`raw` keep stderr machine-parsable.
|
|
755
|
+
|
|
756
|
+
Without `--confirm` the command is a **preview**: it reads the agent exactly once
|
|
757
|
+
(the same one bounded `GET`), prints `preview: --confirm was not passed, so
|
|
758
|
+
nothing has been sent.` plus the resolved destructive target and the whole
|
|
759
|
+
disclosure block to stderr, and exits 2 having sent no `DELETE`. With `--confirm`
|
|
760
|
+
the sequence above is unchanged: it re-reads before the single `DELETE`, so a
|
|
761
|
+
preview is never an approval token. The preview text is suppressed under
|
|
762
|
+
`--output json`/`raw`, like the confirmed path's review.
|
|
763
|
+
|
|
764
|
+
Refusals claim nothing and send nothing extra: an agent outside the explicit
|
|
765
|
+
tenant, the tenant identity mismatch and a delete target that no longer exists
|
|
766
|
+
are refused before the `DELETE` (the preview reports the same `not_found`); a `409` is a `conflict` refusal naming the referring
|
|
696
767
|
schedules; a `404` from the `DELETE` is `not_found`; and a `405` is the server's
|
|
697
768
|
refusal (`unsupported`). None of them is retried.
|
|
698
769
|
|
|
@@ -997,6 +1068,14 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
|
|
|
997
1068
|
"continuation": null}`). `get` emits `operation: "sessions.get"` and
|
|
998
1069
|
`data.session`. The projections never include the session's protected
|
|
999
1070
|
`initial_prompt`, tenant or user identity, or arbitrary response fields.
|
|
1071
|
+
- Default `pretty` output prints `list` as a table with honest headers —
|
|
1072
|
+
`ID`, `AGENT ID`, `OPERATION`, `STATUS`, `EXPIRES AT` for sessions and `ID`,
|
|
1073
|
+
`ROLE`, `MODE`, `PLAN ID`, `CREATED AT` for messages — followed by an entry
|
|
1074
|
+
count (`1 entry` / `N entries`), and `(no entries)` for an empty list. These
|
|
1075
|
+
projections carry no `name`, so no `NAME` column is invented and the `id` is
|
|
1076
|
+
always shown. `get`, `create` and `messages add` print `key: value` lines.
|
|
1077
|
+
`--output json` prints the full envelope exactly as before; pretty output is
|
|
1078
|
+
additive and never changes the envelope.
|
|
1000
1079
|
- `create` and `messages add` require a saved `hc login` session (env-var
|
|
1001
1080
|
credentials are not accepted for writes). Each write is sent exactly once and
|
|
1002
1081
|
the envelope carries the server's authoritative readback:
|
|
@@ -1169,6 +1248,12 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1169
1248
|
(create: reference counts by kind) and finding counts. No prompt,
|
|
1170
1249
|
instructions, greeting, summary, message content or plan value is printed to
|
|
1171
1250
|
stdout, stderr, a diagnostic or an error string.
|
|
1251
|
+
- Default `pretty` output renders `generate`, `get`, `apply`, `rollback` and
|
|
1252
|
+
`reconcile` as labelled lines — `plan:` with the bounded metadata, then
|
|
1253
|
+
`evidence:` (`saved`/`side_effects`/`runtime`) and `mutation_replayed: false`
|
|
1254
|
+
where the command reports them, and a closing `verification:` block for the
|
|
1255
|
+
writes. A write is never printed as a raw JSON blob in the default mode.
|
|
1256
|
+
`--output json` prints the full envelope, unchanged.
|
|
1172
1257
|
- The prompt is protected free text and never comes from argv: `--prompt-file`
|
|
1173
1258
|
reads one owner-only regular text file (no symlinks, at most 20000
|
|
1174
1259
|
characters) and sends it once. `--prompt-file` is optional — the server falls
|
|
@@ -1423,8 +1508,12 @@ and `hc ams organization hierarchy`) render in two modes (`--output` on the root
|
|
|
1423
1508
|
`hc` command; `pretty` is the default):
|
|
1424
1509
|
|
|
1425
1510
|
- **`pretty`** (default): a compact human view. Lists print an `ID`/`NAME`
|
|
1426
|
-
table with an entry count; a
|
|
1427
|
-
|
|
1511
|
+
table with an entry count; a collection whose rows have no `name` (sessions,
|
|
1512
|
+
messages, attachments) prints a table of that command's own columns plus the
|
|
1513
|
+
count, and `(no entries)` when empty. A single record prints `key: value`
|
|
1514
|
+
lines, the organization hierarchy prints an indented tree, and write or
|
|
1515
|
+
reconciliation evidence prints labelled `key: value` lines (the write's
|
|
1516
|
+
`verification:` block included) instead of JSON. Each failed AMS command
|
|
1428
1517
|
prints one stderr line, `error: <message> (<category>)` (flag/argument
|
|
1429
1518
|
usage errors from Click print Click's standard usage message instead).
|
|
1430
1519
|
- **`json`**: the stable result envelope (below) on stdout. When stdout is not
|
|
@@ -298,6 +298,27 @@ otherwise it prompts (with hidden password input). It stores only the email,
|
|
|
298
298
|
tenant/environment binding, access/refresh tokens and expiry in the strict
|
|
299
299
|
owner-only `~/.healthcloud/cli/ams-session.json`; it never stores the password.
|
|
300
300
|
|
|
301
|
+
By default (`pretty`) these three commands print readable lines — never a raw
|
|
302
|
+
JSON blob and never a token:
|
|
303
|
+
|
|
304
|
+
```text
|
|
305
|
+
$ hc login --environment dev --tenant-id TENANT_ID
|
|
306
|
+
saved session: dev / TENANT_ID
|
|
307
|
+
email: admin@example.test
|
|
308
|
+
expires_at: 2026-11-02T01:42:29Z
|
|
309
|
+
$ hc logout
|
|
310
|
+
saved session removed: true
|
|
311
|
+
server session revoked: false
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
`hc login --status` prints the same three lines for the saved session, and a
|
|
315
|
+
second `hc logout` reports `saved session removed: false`. `--output json` (and
|
|
316
|
+
`--output raw`, which these commands treat as JSON) prints exactly the objects
|
|
317
|
+
they have always printed: `{"schema_version": 1, "ok": true, "session":
|
|
318
|
+
{"schema_version": 1, "environment", "tenant_id", "email", "expires_at"}}`
|
|
319
|
+
for `login`/`--status`, and `{"schema_version": 1, "ok": true, "removed":
|
|
320
|
+
true|false, "server_session_revoked": false}` for `logout`.
|
|
321
|
+
|
|
301
322
|
For CI and agents, log in non-interactively: pass `--email` and pipe the password
|
|
302
323
|
to `--password-stdin` (read as exactly one stdin line, never echoed, never a
|
|
303
324
|
flag value — there is no `--password` option):
|
|
@@ -527,11 +548,33 @@ explicitly that
|
|
|
527
548
|
fields — a disabled agent can still be selected by those paths — and that
|
|
528
549
|
in-flight runs, WSS sessions and schedules are untouched by this route.
|
|
529
550
|
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
551
|
+
Default `pretty` output prints those facts as labelled lines — `transition`,
|
|
552
|
+
`requested`, `evidence` (`saved`, `side_effects`, `runtime`), `eligibility`,
|
|
553
|
+
`mutation_replayed: false` — and closes with a `verification:` block, so a
|
|
554
|
+
deactivate reads `transition: deactivate`, then
|
|
555
|
+
`evidence: / saved: / status: confirmed`, then `verification: / saved_state:
|
|
556
|
+
confirmed`, `side_effects: unverified`, `runtime: unverified`; it is never one
|
|
557
|
+
raw JSON line. `--output json` prints the full envelope above, byte-identical to
|
|
558
|
+
before. An unknown outcome still prints its one-line `error:` plus the
|
|
559
|
+
reconciliation on stderr.
|
|
560
|
+
|
|
561
|
+
Without `--confirm` the command is a **preview**: it re-reads the agent exactly
|
|
562
|
+
once (the same one bounded `GET`), runs the same local pre-checks, prints
|
|
563
|
+
`preview: --confirm was not passed, so nothing has been sent.` and then the
|
|
564
|
+
resolved change to stderr — the target (`agent_id`, `name`, `type`,
|
|
565
|
+
`config_version`), `status` and `active` before -> after, and
|
|
566
|
+
`one PUT /agents/{agent_id} with body keys: status, active` — and exits 2 having
|
|
567
|
+
sent nothing. With `--confirm` the write path is unchanged: it re-reads before
|
|
568
|
+
the single `PUT`, so a preview is never an approval token. The preview text is
|
|
569
|
+
suppressed under `--output json`/`raw` (the same rule the confirmed path's
|
|
570
|
+
review follows) so those modes keep stderr machine-parsable.
|
|
571
|
+
|
|
572
|
+
Pre-checks refuse with exit 1 and **no write**: an agent outside the explicit
|
|
573
|
+
tenant, an already in-effect state, an unsupported `archived`/`draft` target
|
|
574
|
+
(named as having no supported reversible transition here), or a
|
|
575
|
+
`config_version` that no longer matches `--expected-config-version` — all of
|
|
576
|
+
these are also enforced by the preview, which still sends nothing. A missing
|
|
577
|
+
`--confirm` exits 2. That version check is a local best-effort
|
|
535
578
|
guard only: the route has no `If-Match` and the server never compares the
|
|
536
579
|
version, so it cannot prove atomicity and a request could still race between
|
|
537
580
|
the read and the PUT.
|
|
@@ -597,20 +640,34 @@ server audit log records an `UPDATE` with the payload. Runtime enforcement of
|
|
|
597
640
|
the new references is unobservable: no serving readback route exists, so this
|
|
598
641
|
command never claims it.
|
|
599
642
|
|
|
643
|
+
Default `pretty` output prints the same facts as labelled lines: `agent_id`,
|
|
644
|
+
`changed` (each field with `before`/`after`/`added`/`removed`),
|
|
645
|
+
`config_version: {before, after}`, `evidence` (`saved`, `side_effects`,
|
|
646
|
+
`runtime`), `mutation_replayed: false`, and a closing `verification:` block.
|
|
647
|
+
`--output json` prints the full envelope described here, unchanged.
|
|
648
|
+
|
|
600
649
|
`tool_ids` and `data_source_ids` address **unscoped global catalogs** by design;
|
|
601
650
|
the server checks tenant scope only for the department, team, skill, policy and
|
|
602
651
|
guardrail references. This command does not imply otherwise, and it does not
|
|
603
652
|
re-implement the server's reference checks.
|
|
604
653
|
|
|
605
|
-
|
|
606
|
-
|
|
654
|
+
Without `--confirm` the command is a **preview**: it reads the agent exactly once
|
|
655
|
+
(the same one bounded `GET`), computes the same reviewed diff and runs the same
|
|
656
|
+
local pre-checks, prints `preview: --confirm was not passed, so nothing has been
|
|
657
|
+
sent.` followed by the reviewed diff, and exits 2 having sent no `PUT`. With
|
|
658
|
+
`--confirm` the write path is unchanged: it re-reads before the single `PUT`, so
|
|
659
|
+
a preview is never an approval token. The preview text is suppressed under
|
|
660
|
+
`--output json`/`raw`, like the confirmed path's review.
|
|
661
|
+
|
|
662
|
+
Pre-checks refuse with exit 1 and **no write**: no assignment option at all
|
|
607
663
|
(exit 2), setting and clearing the same family together (exit 2), an agent
|
|
608
664
|
outside the explicit tenant, a request where every reference already equals the
|
|
609
665
|
read state ("already in effect"), or a `config_version` that no longer matches
|
|
610
|
-
`--expected-config-version
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
the
|
|
666
|
+
`--expected-config-version` — all of these are also enforced by the preview,
|
|
667
|
+
which still sends nothing. A missing `--confirm` exits 2. That version check is
|
|
668
|
+
a local best-effort guard only: the route has no `If-Match` and the server never
|
|
669
|
+
compares the version, so it cannot prove atomicity and a request could still
|
|
670
|
+
race between the read and the PUT. Operation-specific approval here is the CLI's own `--confirm` plus the
|
|
614
671
|
reviewed diff, deliberately separate from the tuning/plan-apply approval path;
|
|
615
672
|
the server enforces tenant isolation, reference existence and the audit log, and
|
|
616
673
|
it has **no** assignment approval or consent entity.
|
|
@@ -671,10 +728,24 @@ of restore and has no `--force`, no `--dry-run`, no interactive prompt and no
|
|
|
671
728
|
`--expected-config-version` — the route has no `If-Match` or version check, so a
|
|
672
729
|
version flag would advertise protection the server does not offer.
|
|
673
730
|
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
731
|
+
Default `pretty` output prints the result as labelled lines next to the stderr
|
|
732
|
+
disclosure: `agent_id`, `deleted: true`, `target`, `disclosure`, `evidence`
|
|
733
|
+
(`deleted`/`side_effects`/`runtime`), `mutation_replayed: false`, and a closing
|
|
734
|
+
`verification:` block. `--output json` prints the full envelope, unchanged. The
|
|
735
|
+
reviewed target and disclosure above are still printed to stderr in `pretty`
|
|
736
|
+
mode only; `--output json`/`raw` keep stderr machine-parsable.
|
|
737
|
+
|
|
738
|
+
Without `--confirm` the command is a **preview**: it reads the agent exactly once
|
|
739
|
+
(the same one bounded `GET`), prints `preview: --confirm was not passed, so
|
|
740
|
+
nothing has been sent.` plus the resolved destructive target and the whole
|
|
741
|
+
disclosure block to stderr, and exits 2 having sent no `DELETE`. With `--confirm`
|
|
742
|
+
the sequence above is unchanged: it re-reads before the single `DELETE`, so a
|
|
743
|
+
preview is never an approval token. The preview text is suppressed under
|
|
744
|
+
`--output json`/`raw`, like the confirmed path's review.
|
|
745
|
+
|
|
746
|
+
Refusals claim nothing and send nothing extra: an agent outside the explicit
|
|
747
|
+
tenant, the tenant identity mismatch and a delete target that no longer exists
|
|
748
|
+
are refused before the `DELETE` (the preview reports the same `not_found`); a `409` is a `conflict` refusal naming the referring
|
|
678
749
|
schedules; a `404` from the `DELETE` is `not_found`; and a `405` is the server's
|
|
679
750
|
refusal (`unsupported`). None of them is retried.
|
|
680
751
|
|
|
@@ -979,6 +1050,14 @@ uv run --frozen hc ams sessions messages add --environment dev --tenant-id TENAN
|
|
|
979
1050
|
"continuation": null}`). `get` emits `operation: "sessions.get"` and
|
|
980
1051
|
`data.session`. The projections never include the session's protected
|
|
981
1052
|
`initial_prompt`, tenant or user identity, or arbitrary response fields.
|
|
1053
|
+
- Default `pretty` output prints `list` as a table with honest headers —
|
|
1054
|
+
`ID`, `AGENT ID`, `OPERATION`, `STATUS`, `EXPIRES AT` for sessions and `ID`,
|
|
1055
|
+
`ROLE`, `MODE`, `PLAN ID`, `CREATED AT` for messages — followed by an entry
|
|
1056
|
+
count (`1 entry` / `N entries`), and `(no entries)` for an empty list. These
|
|
1057
|
+
projections carry no `name`, so no `NAME` column is invented and the `id` is
|
|
1058
|
+
always shown. `get`, `create` and `messages add` print `key: value` lines.
|
|
1059
|
+
`--output json` prints the full envelope exactly as before; pretty output is
|
|
1060
|
+
additive and never changes the envelope.
|
|
982
1061
|
- `create` and `messages add` require a saved `hc login` session (env-var
|
|
983
1062
|
credentials are not accepted for writes). Each write is sent exactly once and
|
|
984
1063
|
the envelope carries the server's authoritative readback:
|
|
@@ -1151,6 +1230,12 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1151
1230
|
(create: reference counts by kind) and finding counts. No prompt,
|
|
1152
1231
|
instructions, greeting, summary, message content or plan value is printed to
|
|
1153
1232
|
stdout, stderr, a diagnostic or an error string.
|
|
1233
|
+
- Default `pretty` output renders `generate`, `get`, `apply`, `rollback` and
|
|
1234
|
+
`reconcile` as labelled lines — `plan:` with the bounded metadata, then
|
|
1235
|
+
`evidence:` (`saved`/`side_effects`/`runtime`) and `mutation_replayed: false`
|
|
1236
|
+
where the command reports them, and a closing `verification:` block for the
|
|
1237
|
+
writes. A write is never printed as a raw JSON blob in the default mode.
|
|
1238
|
+
`--output json` prints the full envelope, unchanged.
|
|
1154
1239
|
- The prompt is protected free text and never comes from argv: `--prompt-file`
|
|
1155
1240
|
reads one owner-only regular text file (no symlinks, at most 20000
|
|
1156
1241
|
characters) and sends it once. `--prompt-file` is optional — the server falls
|
|
@@ -1405,8 +1490,12 @@ and `hc ams organization hierarchy`) render in two modes (`--output` on the root
|
|
|
1405
1490
|
`hc` command; `pretty` is the default):
|
|
1406
1491
|
|
|
1407
1492
|
- **`pretty`** (default): a compact human view. Lists print an `ID`/`NAME`
|
|
1408
|
-
table with an entry count; a
|
|
1409
|
-
|
|
1493
|
+
table with an entry count; a collection whose rows have no `name` (sessions,
|
|
1494
|
+
messages, attachments) prints a table of that command's own columns plus the
|
|
1495
|
+
count, and `(no entries)` when empty. A single record prints `key: value`
|
|
1496
|
+
lines, the organization hierarchy prints an indented tree, and write or
|
|
1497
|
+
reconciliation evidence prints labelled `key: value` lines (the write's
|
|
1498
|
+
`verification:` block included) instead of JSON. Each failed AMS command
|
|
1410
1499
|
prints one stderr line, `error: <message> (<category>)` (flag/argument
|
|
1411
1500
|
usage errors from Click print Click's standard usage message instead).
|
|
1412
1501
|
- **`json`**: the stable result envelope (below) on stdout. When stdout is not
|
|
@@ -7,7 +7,7 @@ from pathlib import Path
|
|
|
7
7
|
import click
|
|
8
8
|
|
|
9
9
|
from ..context import AmsDependencies, pass_ams, preflight_agents
|
|
10
|
-
from ..errors import run_domain
|
|
10
|
+
from ..errors import run_domain, run_preview
|
|
11
11
|
from ..session import resolve_tenant_target
|
|
12
12
|
from .operations import (
|
|
13
13
|
ASSIGNMENT_FAMILIES,
|
|
@@ -16,6 +16,9 @@ from .operations import (
|
|
|
16
16
|
export_agent,
|
|
17
17
|
get_agent,
|
|
18
18
|
list_agents,
|
|
19
|
+
preview_assignments,
|
|
20
|
+
preview_delete,
|
|
21
|
+
preview_lifecycle,
|
|
19
22
|
set_assignments,
|
|
20
23
|
set_lifecycle,
|
|
21
24
|
)
|
|
@@ -37,6 +40,34 @@ def _agent(function):
|
|
|
37
40
|
return click.option("--agent-id", required=True)(function)
|
|
38
41
|
|
|
39
42
|
|
|
43
|
+
def _preview_then_refuse(
|
|
44
|
+
invocation,
|
|
45
|
+
operation: str,
|
|
46
|
+
target_data: dict,
|
|
47
|
+
preview,
|
|
48
|
+
refusal: str,
|
|
49
|
+
*,
|
|
50
|
+
login_hint: str,
|
|
51
|
+
) -> None:
|
|
52
|
+
"""Read the target once, print the reviewed change, then refuse with exit 2.
|
|
53
|
+
|
|
54
|
+
Nothing is written before the refusal, and the preview is still a read: a
|
|
55
|
+
failed read (not logged in, wrong tenant, 404, transport) keeps the bounded
|
|
56
|
+
error envelope instead of escaping as a traceback. The confirmed run
|
|
57
|
+
re-reads before its single write, so a preview is never an approval token.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def read() -> None:
|
|
61
|
+
_require_login(login_hint)
|
|
62
|
+
target = resolve_tenant_target(target_data["environment"], target_data["tenant_id"])
|
|
63
|
+
target_data.update(environment=target.environment, tenant_id=target.tenant_id)
|
|
64
|
+
preflight_agents(invocation, target, methods=("get_agent",))
|
|
65
|
+
preview(target)
|
|
66
|
+
|
|
67
|
+
run_preview(operation, target_data, read)
|
|
68
|
+
raise click.UsageError(refusal)
|
|
69
|
+
|
|
70
|
+
|
|
40
71
|
def _require_login(hint: str = LOGIN_HINT) -> None:
|
|
41
72
|
"""Agent writes need a saved `hc login` session, not env-var credentials."""
|
|
42
73
|
|
|
@@ -71,7 +102,8 @@ def create_lifecycle_command(dependencies: AmsDependencies, transition: str) ->
|
|
|
71
102
|
"--confirm",
|
|
72
103
|
is_flag=True,
|
|
73
104
|
default=False,
|
|
74
|
-
help="Required acknowledgement of the exact target and state; without it
|
|
105
|
+
help="Required acknowledgement of the exact target and state; without it the command reads the "
|
|
106
|
+
"target, prints the change and sends nothing.",
|
|
75
107
|
)
|
|
76
108
|
@pass_ams
|
|
77
109
|
def command(
|
|
@@ -82,14 +114,27 @@ def create_lifecycle_command(dependencies: AmsDependencies, transition: str) ->
|
|
|
82
114
|
expected_config_version: int,
|
|
83
115
|
confirm: bool,
|
|
84
116
|
) -> None:
|
|
85
|
-
if not confirm:
|
|
86
|
-
raise click.UsageError("--confirm is required; no lifecycle write was sent.")
|
|
87
117
|
target_data = {
|
|
88
118
|
"environment": environment,
|
|
89
119
|
"tenant_id": tenant_id,
|
|
90
120
|
"agent_id": agent_id,
|
|
91
121
|
"expected_config_version": expected_config_version,
|
|
92
122
|
}
|
|
123
|
+
if not confirm:
|
|
124
|
+
_preview_then_refuse(
|
|
125
|
+
invocation,
|
|
126
|
+
f"agents.{transition}",
|
|
127
|
+
target_data,
|
|
128
|
+
lambda target: preview_lifecycle(
|
|
129
|
+
dependencies,
|
|
130
|
+
target,
|
|
131
|
+
agent_id,
|
|
132
|
+
transition=transition,
|
|
133
|
+
expected_config_version=expected_config_version,
|
|
134
|
+
),
|
|
135
|
+
"--confirm is required; no lifecycle write was sent. Re-run with --confirm to send it.",
|
|
136
|
+
login_hint=LOGIN_HINT,
|
|
137
|
+
)
|
|
93
138
|
|
|
94
139
|
def execute():
|
|
95
140
|
_require_login()
|
|
@@ -123,13 +168,21 @@ def create_delete_command(dependencies: AmsDependencies) -> click.Command:
|
|
|
123
168
|
"--confirm",
|
|
124
169
|
is_flag=True,
|
|
125
170
|
default=False,
|
|
126
|
-
help="Required acknowledgement of the printed target and disclosure block; without it
|
|
171
|
+
help="Required acknowledgement of the printed target and disclosure block; without it the command "
|
|
172
|
+
"prints both and sends nothing.",
|
|
127
173
|
)
|
|
128
174
|
@pass_ams
|
|
129
175
|
def command(invocation, environment: str | None, tenant_id: str | None, agent_id: str, confirm: bool) -> None:
|
|
130
|
-
if not confirm:
|
|
131
|
-
raise click.UsageError("--confirm is required; no delete was sent.")
|
|
132
176
|
target_data = {"environment": environment, "tenant_id": tenant_id, "agent_id": agent_id}
|
|
177
|
+
if not confirm:
|
|
178
|
+
_preview_then_refuse(
|
|
179
|
+
invocation,
|
|
180
|
+
"agents.delete",
|
|
181
|
+
target_data,
|
|
182
|
+
lambda target: preview_delete(dependencies, target, agent_id),
|
|
183
|
+
"--confirm is required; no delete was sent. Re-run with --confirm to send it.",
|
|
184
|
+
login_hint=DELETE_LOGIN_HINT,
|
|
185
|
+
)
|
|
133
186
|
|
|
134
187
|
def execute():
|
|
135
188
|
_require_login(DELETE_LOGIN_HINT)
|
|
@@ -221,7 +274,10 @@ CONFIG_VERSION_HELP = (
|
|
|
221
274
|
"The config_version read before the write. A local best-effort guard only: the route has no "
|
|
222
275
|
"If-Match, so it never proves atomicity."
|
|
223
276
|
)
|
|
224
|
-
CONFIRM_HELP =
|
|
277
|
+
CONFIRM_HELP = (
|
|
278
|
+
"Required acknowledgement of the target and the printed reviewed diff; without it the command prints "
|
|
279
|
+
"the diff and sends nothing."
|
|
280
|
+
)
|
|
225
281
|
|
|
226
282
|
|
|
227
283
|
def _assignment_changes(values: dict, cleared: dict) -> dict:
|
|
@@ -320,8 +376,6 @@ def create_assignments_set_command(dependencies: AmsDependencies) -> click.Comma
|
|
|
320
376
|
clear_data_source_ids: bool,
|
|
321
377
|
confirm: bool,
|
|
322
378
|
) -> None:
|
|
323
|
-
if not confirm:
|
|
324
|
-
raise click.UsageError("--confirm is required; no assignment write was sent.")
|
|
325
379
|
changes = _assignment_changes(
|
|
326
380
|
{
|
|
327
381
|
"department_id": department_id,
|
|
@@ -351,6 +405,21 @@ def create_assignments_set_command(dependencies: AmsDependencies) -> click.Comma
|
|
|
351
405
|
"expected_config_version": expected_config_version,
|
|
352
406
|
"changed_fields": sorted(changes),
|
|
353
407
|
}
|
|
408
|
+
if not confirm:
|
|
409
|
+
_preview_then_refuse(
|
|
410
|
+
invocation,
|
|
411
|
+
"agents.assignments.set",
|
|
412
|
+
target_data,
|
|
413
|
+
lambda target: preview_assignments(
|
|
414
|
+
dependencies,
|
|
415
|
+
target,
|
|
416
|
+
agent_id,
|
|
417
|
+
changes,
|
|
418
|
+
expected_config_version=expected_config_version,
|
|
419
|
+
),
|
|
420
|
+
"--confirm is required; no assignment write was sent. Re-run with --confirm to send it.",
|
|
421
|
+
login_hint=ASSIGNMENT_LOGIN_HINT,
|
|
422
|
+
)
|
|
354
423
|
|
|
355
424
|
def execute():
|
|
356
425
|
_require_login(ASSIGNMENT_LOGIN_HINT)
|