healthcloud-cli 3.2.4__tar.gz → 3.3.0__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.2.4 → healthcloud_cli-3.3.0}/PKG-INFO +108 -1
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/README.md +107 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/agents/__init__.py +5 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/agents/commands.py +83 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/agents/group.py +18 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/agents/operations.py +72 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/agents/projections.py +37 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/ams/auth.py +6 -2
- healthcloud_cli-3.3.0/healthcloud_cli/ams/commands.py +28 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/compatibility.py +264 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/context.py +143 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/departments/__init__.py +5 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/departments/commands.py +59 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/departments/group.py +18 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/departments/operations.py +71 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/departments/projections.py +19 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/domain_errors.py +9 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/errors.py +87 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/group.py +30 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/organization/__init__.py +5 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/organization/commands.py +28 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/organization/group.py +17 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/organization/operations.py +33 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/organization/projections.py +29 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/ams/output.py +17 -3
- healthcloud_cli-3.3.0/healthcloud_cli/ams/registry.py +71 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/results.py +31 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/ams/target.py +14 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/teams/__init__.py +5 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/teams/commands.py +55 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/teams/group.py +18 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/teams/operations.py +67 -0
- healthcloud_cli-3.3.0/healthcloud_cli/ams/teams/projections.py +21 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/cli.py +10 -9
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/contracts/mcp-tools.manifest.json +690 -237
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/pyproject.toml +1 -1
- healthcloud_cli-3.2.4/healthcloud_cli/ams/commands.py +0 -242
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/.gitignore +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/hatch_build.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/__init__.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/ams/__init__.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/ams/tuning.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/ams/ui.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/ams/windows_files.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/context.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/contracts/__init__.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/main.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/manifest.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/output.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/params.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/healthcloud_cli/util.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/scripts/__init__.py +0 -0
- {healthcloud_cli-3.2.4 → healthcloud_cli-3.3.0}/scripts/bundle_manifest.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: healthcloud-cli
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
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
|
|
@@ -56,6 +56,24 @@ uv run --frozen hc mcp tools list --audience patient
|
|
|
56
56
|
uv run --frozen hc mcp patient get-patient --help
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
AMS help and shell-completion discovery are credential-free and offline: they
|
|
60
|
+
do not resolve root profiles, read target files, create SDK clients, or log in.
|
|
61
|
+
You can inspect the group, a leaf command, and completion candidates before
|
|
62
|
+
configuring credentials or a target:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv run --frozen hc ams --help
|
|
66
|
+
uv run --frozen hc ams inspect --help
|
|
67
|
+
_HC_COMPLETE=bash_complete COMP_WORDS="hc ams in" COMP_CWORD=2 uv run --frozen hc
|
|
68
|
+
_HC_COMPLETE=bash_complete COMP_WORDS="hc ams inspect --o" COMP_CWORD=3 uv run --frozen hc
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Operational validation starts only when Click invokes an AMS leaf callback.
|
|
72
|
+
`discover` then validates its explicit environment and tenant arguments; the
|
|
73
|
+
other leaves require `--target-file` and apply the existing root, target, and
|
|
74
|
+
authentication checks described below. Deferring preflight does not relax
|
|
75
|
+
those safety requirements.
|
|
76
|
+
|
|
59
77
|
The bundled tool manifest (`healthcloud_cli/contracts/mcp-tools.manifest.json`)
|
|
60
78
|
is a copy of `packages/cli/contracts/mcp-tools.manifest.json`. Re-sync it
|
|
61
79
|
after pulling manifest changes with:
|
|
@@ -269,6 +287,54 @@ required; no MCP defaults, admin login, or automatic candidate selection are use
|
|
|
269
287
|
uv run --frozen hc ams discover --environment dev --tenant-id TENANT_ID
|
|
270
288
|
```
|
|
271
289
|
|
|
290
|
+
Read-only agent listing, inspection and export also take explicit flags instead
|
|
291
|
+
of a target file, and log in as tenant admin (`HC_AMS_ADMIN_EMAIL` /
|
|
292
|
+
`HC_AMS_ADMIN_PASSWORD`). Stdout carries bounded metadata only; `adapter` shows
|
|
293
|
+
whether an agent runs from the `file` or `db` config adapter (`unknown` when the
|
|
294
|
+
server does not say). `export` first re-checks the agent/tenant identity, then
|
|
295
|
+
saves the server's own JSON/YAML export byte-for-byte to a new owner-only `--out`
|
|
296
|
+
file, refusing (and leaving no file) when the body is not valid UTF-8. `export`
|
|
297
|
+
needs an AMS build that serves `/agents/{agent_id}/configuration/export`; that
|
|
298
|
+
route is not yet deployed to dev:
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
uv run --frozen hc ams agents list --environment dev --tenant-id TENANT_ID
|
|
302
|
+
uv run --frozen hc ams agents get --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID
|
|
303
|
+
uv run --frozen hc ams agents export --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID \
|
|
304
|
+
--format yaml --out agent.yaml
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Read-only department listing and inspection use the same explicit tenant-admin
|
|
308
|
+
targeting. They emit one versioned JSON result on stdout; failures emit one
|
|
309
|
+
categorized JSON result on stderr. Department descriptions, creator identities,
|
|
310
|
+
and evaluation-suite content are never included in the projection:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
uv run --frozen hc ams departments list --environment dev --tenant-id TENANT_ID
|
|
314
|
+
uv run --frozen hc ams departments get --environment dev --tenant-id TENANT_ID \
|
|
315
|
+
--department-id DEPARTMENT_ID
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Department reads are complete and unpaged. The CLI does not expose department
|
|
319
|
+
create, update, delete, activate, or deactivate operations.
|
|
320
|
+
|
|
321
|
+
Read-only team listing and inspection are also complete and unpaged. The bounded
|
|
322
|
+
projection omits descriptions and creator identities; it includes evaluation-suite
|
|
323
|
+
IDs but never suite content. The CLI does not expose team mutations:
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
uv run --frozen hc ams teams list --environment dev --tenant-id TENANT_ID
|
|
327
|
+
uv run --frozen hc ams teams get --environment dev --tenant-id TENANT_ID --team-id TEAM_ID
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
The organization hierarchy is the backend-built department/team/agent tree. The
|
|
331
|
+
CLI preserves its order and nesting without issuing entity reads, joining records,
|
|
332
|
+
or inferring missing relationships:
|
|
333
|
+
|
|
334
|
+
```bash
|
|
335
|
+
uv run --frozen hc ams organization hierarchy --environment dev --tenant-id TENANT_ID
|
|
336
|
+
```
|
|
337
|
+
|
|
272
338
|
Every other command requires an explicit local, non-secret target JSON file.
|
|
273
339
|
Only the canonical HTTPS AMS origin for that environment is accepted; custom
|
|
274
340
|
origins require separately verified support. Target and nested qualification keys
|
|
@@ -441,3 +507,44 @@ then GET plan and GET agent. Restart the stateful simulation before repeating;
|
|
|
441
507
|
stop it with Ctrl+C afterward. This is a source-backed synthetic failure fixture,
|
|
442
508
|
not the deployed service. The required CI tests exercise both pre-commit and
|
|
443
509
|
post-commit 503 over loopback HTTP without requiring a Postman login or installation.
|
|
510
|
+
|
|
511
|
+
|
|
512
|
+
### Installed CLI integration
|
|
513
|
+
|
|
514
|
+
From the repository root, with Python 3.14, uv 0.12.5 and Node.js available:
|
|
515
|
+
|
|
516
|
+
```bash
|
|
517
|
+
python -m unittest discover -s tooling/cli-integration -p 'test_*.py' -v
|
|
518
|
+
python tooling/cli-integration/run.py --output /tmp/hc-cli-integration
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
The runner installs the SDK, tenant-admin SDK and CLI from the frozen CLI lock
|
|
522
|
+
with `uv sync --no-editable` into a fresh temporary environment outside the
|
|
523
|
+
checkout. It verifies installed module ownership, version, help for every AMS
|
|
524
|
+
command, native shell completion, discovery and the
|
|
525
|
+
inspect/plan/show/apply/restore journey. Negative
|
|
526
|
+
journeys exercise denied login, identity/qualification/plan rejection, malformed
|
|
527
|
+
responses, failed readback and uncertain writes without mutation replay.
|
|
528
|
+
|
|
529
|
+
The `Installed CLI integration` workflow selects its uv-installed Linux job at
|
|
530
|
+
job level for `packages/cli/**`, `packages/pip/**`,
|
|
531
|
+
`packages/pip-tenantadmin/**`, its own tooling/workflow, docs compiler inputs
|
|
532
|
+
(`tooling/docs-compiler/**`, `docs-src/**`, `docs/sdk/**`), MCP contracts
|
|
533
|
+
(`contracts/mcp/**`), and root package/lock, Python/lock and Ruff inputs. Backend-only changes skip the install job;
|
|
534
|
+
the small `CLI integration` result check still runs and verifies applicability.
|
|
535
|
+
Invalid selection or an applicable failed/cancelled/skipped job fails it.
|
|
536
|
+
The job retains only sanitized consumer/JUnit results for seven days.
|
|
537
|
+
|
|
538
|
+
All scenario records and credentials are synthetic. A test-only Python startup
|
|
539
|
+
adapter preserves the canonical DEV AMS/tenant-admin origins for product
|
|
540
|
+
validation while routing only those exact origins over real HTTP to an owned
|
|
541
|
+
loopback fixture; other origins are refused before network access. Child
|
|
542
|
+
processes use an isolated home and an allowlisted environment. The adapter is
|
|
543
|
+
outside all shipped distributions. This boundary does not verify TLS or deployed
|
|
544
|
+
service conformance. A verified save still exits 1 with `outcome="saved"` and
|
|
545
|
+
`publication="unknown"`; publication and runtime acceptance remain unverified.
|
|
546
|
+
|
|
547
|
+
Required-check activation is tracked separately in
|
|
548
|
+
[services #724](https://github.com/healthcloudservices/healthcloud-services/issues/724).
|
|
549
|
+
Require `CLI integration` only after the workflow is merged and both relevant
|
|
550
|
+
and unrelated-change Actions results and effective settings are verified.
|
|
@@ -38,6 +38,24 @@ uv run --frozen hc mcp tools list --audience patient
|
|
|
38
38
|
uv run --frozen hc mcp patient get-patient --help
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
+
AMS help and shell-completion discovery are credential-free and offline: they
|
|
42
|
+
do not resolve root profiles, read target files, create SDK clients, or log in.
|
|
43
|
+
You can inspect the group, a leaf command, and completion candidates before
|
|
44
|
+
configuring credentials or a target:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
uv run --frozen hc ams --help
|
|
48
|
+
uv run --frozen hc ams inspect --help
|
|
49
|
+
_HC_COMPLETE=bash_complete COMP_WORDS="hc ams in" COMP_CWORD=2 uv run --frozen hc
|
|
50
|
+
_HC_COMPLETE=bash_complete COMP_WORDS="hc ams inspect --o" COMP_CWORD=3 uv run --frozen hc
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Operational validation starts only when Click invokes an AMS leaf callback.
|
|
54
|
+
`discover` then validates its explicit environment and tenant arguments; the
|
|
55
|
+
other leaves require `--target-file` and apply the existing root, target, and
|
|
56
|
+
authentication checks described below. Deferring preflight does not relax
|
|
57
|
+
those safety requirements.
|
|
58
|
+
|
|
41
59
|
The bundled tool manifest (`healthcloud_cli/contracts/mcp-tools.manifest.json`)
|
|
42
60
|
is a copy of `packages/cli/contracts/mcp-tools.manifest.json`. Re-sync it
|
|
43
61
|
after pulling manifest changes with:
|
|
@@ -251,6 +269,54 @@ required; no MCP defaults, admin login, or automatic candidate selection are use
|
|
|
251
269
|
uv run --frozen hc ams discover --environment dev --tenant-id TENANT_ID
|
|
252
270
|
```
|
|
253
271
|
|
|
272
|
+
Read-only agent listing, inspection and export also take explicit flags instead
|
|
273
|
+
of a target file, and log in as tenant admin (`HC_AMS_ADMIN_EMAIL` /
|
|
274
|
+
`HC_AMS_ADMIN_PASSWORD`). Stdout carries bounded metadata only; `adapter` shows
|
|
275
|
+
whether an agent runs from the `file` or `db` config adapter (`unknown` when the
|
|
276
|
+
server does not say). `export` first re-checks the agent/tenant identity, then
|
|
277
|
+
saves the server's own JSON/YAML export byte-for-byte to a new owner-only `--out`
|
|
278
|
+
file, refusing (and leaving no file) when the body is not valid UTF-8. `export`
|
|
279
|
+
needs an AMS build that serves `/agents/{agent_id}/configuration/export`; that
|
|
280
|
+
route is not yet deployed to dev:
|
|
281
|
+
|
|
282
|
+
```bash
|
|
283
|
+
uv run --frozen hc ams agents list --environment dev --tenant-id TENANT_ID
|
|
284
|
+
uv run --frozen hc ams agents get --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID
|
|
285
|
+
uv run --frozen hc ams agents export --environment dev --tenant-id TENANT_ID --agent-id AGENT_ID \
|
|
286
|
+
--format yaml --out agent.yaml
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Read-only department listing and inspection use the same explicit tenant-admin
|
|
290
|
+
targeting. They emit one versioned JSON result on stdout; failures emit one
|
|
291
|
+
categorized JSON result on stderr. Department descriptions, creator identities,
|
|
292
|
+
and evaluation-suite content are never included in the projection:
|
|
293
|
+
|
|
294
|
+
```bash
|
|
295
|
+
uv run --frozen hc ams departments list --environment dev --tenant-id TENANT_ID
|
|
296
|
+
uv run --frozen hc ams departments get --environment dev --tenant-id TENANT_ID \
|
|
297
|
+
--department-id DEPARTMENT_ID
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
Department reads are complete and unpaged. The CLI does not expose department
|
|
301
|
+
create, update, delete, activate, or deactivate operations.
|
|
302
|
+
|
|
303
|
+
Read-only team listing and inspection are also complete and unpaged. The bounded
|
|
304
|
+
projection omits descriptions and creator identities; it includes evaluation-suite
|
|
305
|
+
IDs but never suite content. The CLI does not expose team mutations:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
uv run --frozen hc ams teams list --environment dev --tenant-id TENANT_ID
|
|
309
|
+
uv run --frozen hc ams teams get --environment dev --tenant-id TENANT_ID --team-id TEAM_ID
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The organization hierarchy is the backend-built department/team/agent tree. The
|
|
313
|
+
CLI preserves its order and nesting without issuing entity reads, joining records,
|
|
314
|
+
or inferring missing relationships:
|
|
315
|
+
|
|
316
|
+
```bash
|
|
317
|
+
uv run --frozen hc ams organization hierarchy --environment dev --tenant-id TENANT_ID
|
|
318
|
+
```
|
|
319
|
+
|
|
254
320
|
Every other command requires an explicit local, non-secret target JSON file.
|
|
255
321
|
Only the canonical HTTPS AMS origin for that environment is accepted; custom
|
|
256
322
|
origins require separately verified support. Target and nested qualification keys
|
|
@@ -423,3 +489,44 @@ then GET plan and GET agent. Restart the stateful simulation before repeating;
|
|
|
423
489
|
stop it with Ctrl+C afterward. This is a source-backed synthetic failure fixture,
|
|
424
490
|
not the deployed service. The required CI tests exercise both pre-commit and
|
|
425
491
|
post-commit 503 over loopback HTTP without requiring a Postman login or installation.
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
### Installed CLI integration
|
|
495
|
+
|
|
496
|
+
From the repository root, with Python 3.14, uv 0.12.5 and Node.js available:
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
python -m unittest discover -s tooling/cli-integration -p 'test_*.py' -v
|
|
500
|
+
python tooling/cli-integration/run.py --output /tmp/hc-cli-integration
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
The runner installs the SDK, tenant-admin SDK and CLI from the frozen CLI lock
|
|
504
|
+
with `uv sync --no-editable` into a fresh temporary environment outside the
|
|
505
|
+
checkout. It verifies installed module ownership, version, help for every AMS
|
|
506
|
+
command, native shell completion, discovery and the
|
|
507
|
+
inspect/plan/show/apply/restore journey. Negative
|
|
508
|
+
journeys exercise denied login, identity/qualification/plan rejection, malformed
|
|
509
|
+
responses, failed readback and uncertain writes without mutation replay.
|
|
510
|
+
|
|
511
|
+
The `Installed CLI integration` workflow selects its uv-installed Linux job at
|
|
512
|
+
job level for `packages/cli/**`, `packages/pip/**`,
|
|
513
|
+
`packages/pip-tenantadmin/**`, its own tooling/workflow, docs compiler inputs
|
|
514
|
+
(`tooling/docs-compiler/**`, `docs-src/**`, `docs/sdk/**`), MCP contracts
|
|
515
|
+
(`contracts/mcp/**`), and root package/lock, Python/lock and Ruff inputs. Backend-only changes skip the install job;
|
|
516
|
+
the small `CLI integration` result check still runs and verifies applicability.
|
|
517
|
+
Invalid selection or an applicable failed/cancelled/skipped job fails it.
|
|
518
|
+
The job retains only sanitized consumer/JUnit results for seven days.
|
|
519
|
+
|
|
520
|
+
All scenario records and credentials are synthetic. A test-only Python startup
|
|
521
|
+
adapter preserves the canonical DEV AMS/tenant-admin origins for product
|
|
522
|
+
validation while routing only those exact origins over real HTTP to an owned
|
|
523
|
+
loopback fixture; other origins are refused before network access. Child
|
|
524
|
+
processes use an isolated home and an allowlisted environment. The adapter is
|
|
525
|
+
outside all shipped distributions. This boundary does not verify TLS or deployed
|
|
526
|
+
service conformance. A verified save still exits 1 with `outcome="saved"` and
|
|
527
|
+
`publication="unknown"`; publication and runtime acceptance remain unverified.
|
|
528
|
+
|
|
529
|
+
Required-check activation is tracked separately in
|
|
530
|
+
[services #724](https://github.com/healthcloudservices/healthcloud-services/issues/724).
|
|
531
|
+
Require `CLI integration` only after the workflow is merged and both relevant
|
|
532
|
+
and unrelated-change Actions results and effective settings are verified.
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Fresh Click adapters for read-only agent operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ..compatibility import safe_errors
|
|
10
|
+
from ..context import AmsDependencies, pass_ams, preflight_agents
|
|
11
|
+
from ..output import metadata
|
|
12
|
+
from ..target import TenantTarget
|
|
13
|
+
from .operations import AgentReadRefused, export_agent, get_agent, list_agents
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _environment(function):
|
|
17
|
+
return click.option("--environment", type=click.Choice(["dev", "uat", "prod"]), required=True)(function)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _tenant(function):
|
|
21
|
+
return click.option("--tenant-id", required=True)(function)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _agent(function):
|
|
25
|
+
return click.option("--agent-id", required=True)(function)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _run(operation, *args):
|
|
29
|
+
try:
|
|
30
|
+
return operation(*args)
|
|
31
|
+
except AgentReadRefused as exc:
|
|
32
|
+
raise click.ClickException(str(exc)) from None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def create_list_command(dependencies: AmsDependencies) -> click.Command:
|
|
36
|
+
@click.command("list")
|
|
37
|
+
@_environment
|
|
38
|
+
@_tenant
|
|
39
|
+
@pass_ams
|
|
40
|
+
@safe_errors
|
|
41
|
+
def command(invocation, environment: str, tenant_id: str) -> None:
|
|
42
|
+
preflight_agents(invocation)
|
|
43
|
+
metadata(_run(list_agents, dependencies, TenantTarget(environment, tenant_id)))
|
|
44
|
+
|
|
45
|
+
return command
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def create_get_command(dependencies: AmsDependencies) -> click.Command:
|
|
49
|
+
@click.command("get")
|
|
50
|
+
@_environment
|
|
51
|
+
@_tenant
|
|
52
|
+
@_agent
|
|
53
|
+
@pass_ams
|
|
54
|
+
@safe_errors
|
|
55
|
+
def command(invocation, environment: str, tenant_id: str, agent_id: str) -> None:
|
|
56
|
+
preflight_agents(invocation)
|
|
57
|
+
metadata(_run(get_agent, dependencies, TenantTarget(environment, tenant_id), agent_id))
|
|
58
|
+
|
|
59
|
+
return command
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def create_export_command(dependencies: AmsDependencies) -> click.Command:
|
|
63
|
+
@click.command("export")
|
|
64
|
+
@_environment
|
|
65
|
+
@_tenant
|
|
66
|
+
@_agent
|
|
67
|
+
@click.option("--out", required=True, type=click.Path(path_type=Path, dir_okay=False))
|
|
68
|
+
@click.option("--format", "export_format", type=click.Choice(["json", "yaml"]), default="json", show_default=True)
|
|
69
|
+
@pass_ams
|
|
70
|
+
@safe_errors
|
|
71
|
+
def command(
|
|
72
|
+
invocation,
|
|
73
|
+
environment: str,
|
|
74
|
+
tenant_id: str,
|
|
75
|
+
agent_id: str,
|
|
76
|
+
out: Path,
|
|
77
|
+
export_format: str,
|
|
78
|
+
) -> None:
|
|
79
|
+
preflight_agents(invocation)
|
|
80
|
+
target = TenantTarget(environment, tenant_id)
|
|
81
|
+
metadata(_run(export_agent, dependencies, target, agent_id, out, export_format))
|
|
82
|
+
|
|
83
|
+
return command
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""Fresh agent-domain group assembly."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
from ..context import AmsDependencies
|
|
8
|
+
from .commands import create_export_command, create_get_command, create_list_command
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_agents_group(dependencies: AmsDependencies) -> click.Group:
|
|
12
|
+
@click.group("agents")
|
|
13
|
+
def group() -> None:
|
|
14
|
+
"""Read-only agent listing, inspection and export for one explicit tenant."""
|
|
15
|
+
|
|
16
|
+
for factory in (create_list_command, create_get_command, create_export_command):
|
|
17
|
+
group.add_command(factory(dependencies))
|
|
18
|
+
return group
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Click-free orchestration for agent list, get, and exact-text export."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Literal
|
|
8
|
+
|
|
9
|
+
from ..context import AmsDependencies
|
|
10
|
+
from ..output import private_text_output
|
|
11
|
+
from ..target import TenantTarget
|
|
12
|
+
from .projections import detail, summary
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AgentReadRefused(ValueError):
|
|
16
|
+
"""A stable refusal safe for the CLI adapter to expose."""
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _agent_id(value: str) -> str:
|
|
20
|
+
if not isinstance(value, str) or not value.strip():
|
|
21
|
+
raise AgentReadRefused("Explicit agent ID required.")
|
|
22
|
+
return value
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def list_agents(dependencies: AmsDependencies, target: TenantTarget) -> dict:
|
|
26
|
+
with dependencies.authenticated_sdk_factory(target) as sdk:
|
|
27
|
+
agents = sdk.ams_admin.list_agents()
|
|
28
|
+
if any(agent.tenant_id != target.tenant_id for agent in agents):
|
|
29
|
+
raise AgentReadRefused("Agent/tenant identity mismatch; stopped.")
|
|
30
|
+
return {
|
|
31
|
+
"environment": target.environment,
|
|
32
|
+
"tenant_id": target.tenant_id,
|
|
33
|
+
"agents": [summary(agent) for agent in agents],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _verified_agent(sdk, target: TenantTarget, agent_id: str):
|
|
38
|
+
agent = sdk.ams_admin.get_agent(agent_id)
|
|
39
|
+
if agent is None or agent.id != agent_id or agent.tenant_id != target.tenant_id:
|
|
40
|
+
raise AgentReadRefused("Agent/tenant identity mismatch; stopped.")
|
|
41
|
+
return agent
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def get_agent(dependencies: AmsDependencies, target: TenantTarget, agent_id: str) -> dict:
|
|
45
|
+
agent_id = _agent_id(agent_id)
|
|
46
|
+
with dependencies.authenticated_sdk_factory(target) as sdk:
|
|
47
|
+
agent = _verified_agent(sdk, target, agent_id)
|
|
48
|
+
return {"environment": target.environment, "tenant_id": target.tenant_id} | detail(agent)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def export_agent(
|
|
52
|
+
dependencies: AmsDependencies,
|
|
53
|
+
target: TenantTarget,
|
|
54
|
+
agent_id: str,
|
|
55
|
+
output: Path,
|
|
56
|
+
export_format: Literal["json", "yaml"],
|
|
57
|
+
) -> dict:
|
|
58
|
+
agent_id = _agent_id(agent_id)
|
|
59
|
+
if export_format not in ("json", "yaml"):
|
|
60
|
+
raise AgentReadRefused("Export format must be json or yaml.")
|
|
61
|
+
with private_text_output(output) as write, dependencies.authenticated_sdk_factory(target) as sdk:
|
|
62
|
+
_verified_agent(sdk, target, agent_id)
|
|
63
|
+
text = sdk.ams_admin.export_agent_configuration(agent_id, format=export_format)
|
|
64
|
+
write(text)
|
|
65
|
+
return {
|
|
66
|
+
"environment": target.environment,
|
|
67
|
+
"tenant_id": target.tenant_id,
|
|
68
|
+
"agent_id": agent_id,
|
|
69
|
+
"path": str(output),
|
|
70
|
+
"format": export_format,
|
|
71
|
+
"sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(),
|
|
72
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""Explicit bounded projections for agent reads."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from healthcloud.models.ams_tuning import AgentConfig
|
|
6
|
+
|
|
7
|
+
from ..output import digest
|
|
8
|
+
|
|
9
|
+
ASSIGNED_REFERENCES = ("tool_ids", "skill_ids", "guardrail_ids", "policy_ids", "data_source_ids")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _metadata_string(value: object) -> str:
|
|
13
|
+
return value if isinstance(value, str) and value.strip() else "unknown"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def summary(agent: AgentConfig) -> dict:
|
|
17
|
+
runtime = agent.runtime_configuration if isinstance(agent.runtime_configuration, dict) else {}
|
|
18
|
+
return {
|
|
19
|
+
"id": agent.id,
|
|
20
|
+
"name": agent.name,
|
|
21
|
+
"key": agent.key,
|
|
22
|
+
"type": agent.type,
|
|
23
|
+
"status": agent.status,
|
|
24
|
+
"active": agent.active,
|
|
25
|
+
"config_version": agent.config_version,
|
|
26
|
+
"adapter": _metadata_string(runtime.get("adapter")),
|
|
27
|
+
"runtime": _metadata_string(runtime.get("runtime")),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def detail(agent: AgentConfig) -> dict:
|
|
32
|
+
values = {"instructions": agent.instructions, "greetings": agent.greetings}
|
|
33
|
+
return summary(agent) | {
|
|
34
|
+
"fields": sorted(values),
|
|
35
|
+
"field_hashes": {key: digest(value) for key, value in values.items()},
|
|
36
|
+
"assigned_references": {key: getattr(agent, key) for key in ASSIGNED_REFERENCES},
|
|
37
|
+
}
|
|
@@ -8,17 +8,21 @@ import click
|
|
|
8
8
|
from healthcloud import HCSDK
|
|
9
9
|
from healthcloud_tenantadmin import HCTenantAdminSDK
|
|
10
10
|
|
|
11
|
-
from .target import AmsTarget
|
|
11
|
+
from .target import AmsTarget, TenantTarget
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
@contextmanager
|
|
15
|
-
def authenticated_sdk(target: AmsTarget):
|
|
15
|
+
def authenticated_sdk(target: AmsTarget | TenantTarget):
|
|
16
16
|
sdk = HCSDK(environment=target.environment, tenant_id=target.tenant_id)
|
|
17
17
|
try:
|
|
18
18
|
if not hasattr(sdk, "ams_admin"):
|
|
19
19
|
raise click.ClickException(
|
|
20
20
|
"SDK missing ams_admin; use the reviewed local editable SDK checkout (see CLI README)."
|
|
21
21
|
)
|
|
22
|
+
if isinstance(target, TenantTarget) and not hasattr(sdk.ams_admin, "list_agents"):
|
|
23
|
+
raise click.ClickException(
|
|
24
|
+
"SDK missing ams_admin agent listing; use the reviewed local editable SDK checkout (see CLI README)."
|
|
25
|
+
)
|
|
22
26
|
email = os.environ.get("HC_AMS_ADMIN_EMAIL")
|
|
23
27
|
password = os.environ.get("HC_AMS_ADMIN_PASSWORD")
|
|
24
28
|
if not email or not password:
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Legacy AMS command imports backed by one compatibility assembly."""
|
|
2
|
+
|
|
3
|
+
from .compatibility import safe_errors
|
|
4
|
+
from .group import create_ams_group
|
|
5
|
+
|
|
6
|
+
ams = create_ams_group()
|
|
7
|
+
agents = ams.commands["agents"]
|
|
8
|
+
discover = ams.commands["discover"]
|
|
9
|
+
inspect = ams.commands["inspect"]
|
|
10
|
+
plan = ams.commands["plan"]
|
|
11
|
+
show = ams.commands["show"]
|
|
12
|
+
apply = ams.commands["apply"]
|
|
13
|
+
restore = ams.commands["restore"]
|
|
14
|
+
ui = ams.commands["ui"]
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ams",
|
|
18
|
+
"agents",
|
|
19
|
+
"apply",
|
|
20
|
+
"create_ams_group",
|
|
21
|
+
"discover",
|
|
22
|
+
"inspect",
|
|
23
|
+
"plan",
|
|
24
|
+
"restore",
|
|
25
|
+
"safe_errors",
|
|
26
|
+
"show",
|
|
27
|
+
"ui",
|
|
28
|
+
]
|