healthcloud-cli 3.4.0__tar.gz → 3.5.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.4.0 → healthcloud_cli-3.5.0}/.gitignore +5 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/PKG-INFO +157 -3
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/README.md +156 -2
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/agents/commands.py +29 -20
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/agents/operations.py +43 -20
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/context.py +42 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/data_sources/__init__.py +5 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/data_sources/commands.py +61 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/data_sources/group.py +18 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/data_sources/operations.py +71 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/data_sources/projections.py +12 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/departments/operations.py +2 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/errors.py +215 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/guardrails/operations.py +4 -1
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/organization/operations.py +1 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/policies/operations.py +2 -1
- healthcloud_cli-3.5.0/healthcloud_cli/ams/pretty.py +133 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/registry.py +4 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/results.py +42 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/session.py +2 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/skills/operations.py +2 -1
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/teams/operations.py +2 -1
- healthcloud_cli-3.5.0/healthcloud_cli/ams/tools/__init__.py +5 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/tools/commands.py +57 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/tools/group.py +18 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/tools/operations.py +66 -0
- healthcloud_cli-3.5.0/healthcloud_cli/ams/tools/projections.py +21 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/cli.py +107 -4
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/pyproject.toml +1 -1
- healthcloud_cli-3.4.0/healthcloud_cli/ams/errors.py +0 -97
- healthcloud_cli-3.4.0/healthcloud_cli/ams/results.py +0 -31
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/hatch_build.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/agents/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/agents/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/agents/projections.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/auth.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/commands.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/compatibility.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/departments/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/departments/commands.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/departments/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/departments/projections.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/domain_errors.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/guardrails/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/guardrails/commands.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/guardrails/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/guardrails/projections.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/organization/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/organization/commands.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/organization/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/organization/projections.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/output.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/policies/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/policies/commands.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/policies/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/policies/projections.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/skills/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/skills/commands.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/skills/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/skills/projections.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/target.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/teams/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/teams/commands.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/teams/group.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/teams/projections.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/tuning.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/ui.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/ams/windows_files.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/context.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/contracts/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/contracts/mcp-tools.manifest.json +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/main.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/manifest.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/output.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/params.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/healthcloud_cli/util.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.0}/scripts/__init__.py +0 -0
- {healthcloud_cli-3.4.0 → healthcloud_cli-3.5.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.5.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
|
|
@@ -293,6 +293,31 @@ uv run --frozen hc logout
|
|
|
293
293
|
otherwise it prompts (with hidden password input). It stores only the email,
|
|
294
294
|
tenant/environment binding, access/refresh tokens and expiry in the strict
|
|
295
295
|
owner-only `~/.healthcloud/cli/ams-session.json`; it never stores the password.
|
|
296
|
+
|
|
297
|
+
For CI and agents, log in non-interactively: pass `--email` and pipe the password
|
|
298
|
+
to `--password-stdin` (read as exactly one stdin line, never echoed, never a
|
|
299
|
+
flag value — there is no `--password` option):
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
printf '%s\n' "$HC_AMS_ADMIN_PASSWORD" | \
|
|
303
|
+
uv run --frozen hc login --environment dev --tenant-id TENANT_ID \
|
|
304
|
+
--email "$HC_AMS_ADMIN_EMAIL" --password-stdin
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
`--email` beats `HC_AMS_ADMIN_EMAIL` and `--password-stdin` beats
|
|
308
|
+
`HC_AMS_ADMIN_PASSWORD`. With `--password-stdin`, the piped line is always the
|
|
309
|
+
password: it is read before any prompt, so it can never be consumed as the
|
|
310
|
+
email, and the email must come from `--email`, `HC_AMS_ADMIN_EMAIL` or (only on
|
|
311
|
+
an interactive terminal) the email prompt. When stdin is not an interactive
|
|
312
|
+
terminal and neither `--password-stdin` nor `HC_AMS_ADMIN_PASSWORD` supplies the
|
|
313
|
+
password, `hc login` refuses with that guidance instead of attempting a hidden
|
|
314
|
+
prompt (no `GetPassWarning`, no echoed password); a missing email on a
|
|
315
|
+
non-interactive stdin (or a process with no stdin stream at all) is refused the
|
|
316
|
+
same way, naming `--email`/`--password-stdin`; every refusal happens before any
|
|
317
|
+
login request. `--status` cannot be combined with `--email` or `--password-stdin`.
|
|
318
|
+
Even on an interactive terminal, `--password-stdin` suppresses terminal echo
|
|
319
|
+
while the single line is read and restores it afterwards; a stdin stream that is
|
|
320
|
+
closed or errors mid-read is refused cleanly (exit 2, never a traceback).
|
|
296
321
|
AMS commands may omit both target flags while this session is present, except
|
|
297
322
|
that a `prod` session still needs `--environment prod` on every command. Explicit
|
|
298
323
|
flags and target files must match it exactly. A session within 60 seconds of
|
|
@@ -320,11 +345,22 @@ uv run --frozen hc ams discover --environment dev --tenant-id TENANT_ID
|
|
|
320
345
|
Read-only agent listing, inspection and export take flags instead of a target
|
|
321
346
|
file; both may be omitted together when a matching saved session exists. Without
|
|
322
347
|
one, they log in as tenant admin using `HC_AMS_ADMIN_EMAIL` /
|
|
323
|
-
`HC_AMS_ADMIN_PASSWORD`.
|
|
348
|
+
`HC_AMS_ADMIN_PASSWORD`. They emit the same versioned result envelope as the
|
|
349
|
+
other read domains (`operation`, `outcome`, `schema_version`, `target`, `data`;
|
|
350
|
+
no `verification` block on reads); failures emit one categorized JSON error
|
|
351
|
+
envelope on stderr with the same categories (not found, target mismatch,
|
|
352
|
+
refusal, logged out), not the plain-text `Error: …` lines of earlier releases.
|
|
353
|
+
`agents list` carries bounded projections under `data.agents` plus a `page`
|
|
354
|
+
block; `agents get` carries the bounded projection under `data.agent`. The
|
|
355
|
+
legacy top-level `{"agents": …, "environment": …, "tenant_id": …}` shape is
|
|
356
|
+
intentionally replaced by this envelope — scripts must read the new shape.
|
|
357
|
+
Stdout carries bounded metadata only; `adapter` shows
|
|
324
358
|
whether an agent runs from the `file` or `db` config adapter (`unknown` when the
|
|
325
359
|
server does not say). `export` first re-checks the agent/tenant identity, then
|
|
326
360
|
saves the server's own JSON/YAML export byte-for-byte to a new owner-only `--out`
|
|
327
|
-
file, refusing (and leaving no file) when the body is
|
|
361
|
+
file (exact bytes preserved), refusing (and leaving no file) when the body is
|
|
362
|
+
not valid UTF-8; its stdout reports the envelope with the file `path`, `format`
|
|
363
|
+
and `sha256` under `data` and the `agent_id` in `target`. `export`
|
|
328
364
|
needs an AMS build that serves `/agents/{agent_id}/configuration/export`; that
|
|
329
365
|
route is not yet deployed to dev:
|
|
330
366
|
|
|
@@ -335,6 +371,23 @@ uv run --frozen hc ams agents export --environment dev --tenant-id TENANT_ID --a
|
|
|
335
371
|
--format yaml --out agent.yaml
|
|
336
372
|
```
|
|
337
373
|
|
|
374
|
+
For example, `hc ams agents list` succeeds with (formatted for readability):
|
|
375
|
+
|
|
376
|
+
```json
|
|
377
|
+
{"schema_version": 1, "operation": "agents.list", "outcome": "succeeded",
|
|
378
|
+
"target": {"environment": "dev", "tenant_id": "TENANT_ID"},
|
|
379
|
+
"data": {"agents": [{"id": "AGENT_ID", "name": "…", "adapter": "file", "…": "…"}],
|
|
380
|
+
"page": {"kind": "unpaged", "returned_count": 1, "continuation": null}}}
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
and a missing `--agent-id` fails with one envelope on stderr and exit 1:
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{"schema_version": 1, "operation": "agents.get", "outcome": "failed",
|
|
387
|
+
"target": {"environment": "dev", "tenant_id": "TENANT_ID", "agent_id": "AGENT_ID"},
|
|
388
|
+
"error": {"category": "not_found", "message": "Agent 'AGENT_ID' was not found."}}
|
|
389
|
+
```
|
|
390
|
+
|
|
338
391
|
Read-only department listing and inspection use the same explicit tenant-admin
|
|
339
392
|
targeting. They emit one versioned JSON result on stdout; failures emit one
|
|
340
393
|
categorized JSON result on stderr. Department descriptions, creator identities,
|
|
@@ -421,6 +474,107 @@ additional data. Guardrail reads refuse target files, root credentials or base
|
|
|
421
474
|
URLs, raw output, blank or dot-segment guardrail IDs, and installed SDKs without
|
|
422
475
|
both guardrail methods before tenant-admin login or catalog reads.
|
|
423
476
|
|
|
477
|
+
Read-only tool catalog listing and inspection are complete and unpaged. The
|
|
478
|
+
bounded projection includes only IDs, names, read-only and confirmation flags,
|
|
479
|
+
agent types, required scopes, audit events, and activity labels:
|
|
480
|
+
|
|
481
|
+
```bash
|
|
482
|
+
uv run --frozen hc ams tools list --environment dev --tenant-id TENANT_ID
|
|
483
|
+
uv run --frozen hc ams tools get --environment dev --tenant-id TENANT_ID \
|
|
484
|
+
--tool-id TOOL_ID
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
When both target flags are omitted, these commands may use the matching saved
|
|
488
|
+
tenant-admin session. Explicit flags must match that session. Tool output never
|
|
489
|
+
includes descriptions, parameters, input schemas, PIP or connector routing, or
|
|
490
|
+
arbitrary response fields. The CLI does not execute, assign, create, update, or
|
|
491
|
+
delete tools; join agents or skills; or claim assignment, availability,
|
|
492
|
+
authorization, deployment, serving, or runtime effectiveness. It makes exactly
|
|
493
|
+
one catalog GET after tenant-admin login and does not paginate, retry, cache,
|
|
494
|
+
join, or infer additional data. Tool reads refuse target files, root credentials
|
|
495
|
+
or base URLs, raw output, blank or dot-segment IDs, and installed SDKs without
|
|
496
|
+
both tool methods before tenant-admin login or catalog reads.
|
|
497
|
+
|
|
498
|
+
Read-only data-source catalog listing and inspection are complete and unpaged.
|
|
499
|
+
The bounded projection includes only stable IDs and names:
|
|
500
|
+
|
|
501
|
+
```bash
|
|
502
|
+
uv run --frozen hc ams data-sources list --environment dev --tenant-id TENANT_ID
|
|
503
|
+
uv run --frozen hc ams data-sources get --environment dev --tenant-id TENANT_ID \
|
|
504
|
+
--data-source-id DATA_SOURCE_ID
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
When both target flags are omitted, these commands may use the matching saved
|
|
508
|
+
tenant-admin session. Explicit flags must match that session. Data-source output
|
|
509
|
+
never includes descriptions or arbitrary response fields and does not imply
|
|
510
|
+
access, patient consent, record availability, assignment, authorization, or
|
|
511
|
+
runtime effectiveness. The CLI does not create, update, delete, assign, access
|
|
512
|
+
records through, or execute data sources. It makes exactly one catalog GET after
|
|
513
|
+
tenant-admin login and does not paginate, retry, cache, truncate, join, or infer
|
|
514
|
+
additional data. Data-source reads refuse target files, root credentials or base
|
|
515
|
+
URLs, raw output, blank or dot-segment IDs, and installed SDKs without both
|
|
516
|
+
data-source methods before tenant-admin login or catalog reads.
|
|
517
|
+
|
|
518
|
+
### AMS output modes
|
|
519
|
+
|
|
520
|
+
The read commands (`hc ams skills|tools|policies|guardrails|data-sources|teams|departments list|get`
|
|
521
|
+
and `hc ams organization hierarchy`) render in two modes (`--output` on the root
|
|
522
|
+
`hc` command; `pretty` is the default):
|
|
523
|
+
|
|
524
|
+
- **`pretty`** (default): a compact human view. Lists print an `ID`/`NAME`
|
|
525
|
+
table with an entry count; a single record prints `key: value` lines; the
|
|
526
|
+
organization hierarchy prints an indented tree. Each failed AMS command
|
|
527
|
+
prints one stderr line, `error: <message> (<category>)` (flag/argument
|
|
528
|
+
usage errors from Click print Click's standard usage message instead).
|
|
529
|
+
- **`json`**: the stable result envelope (below) on stdout. When stdout is not
|
|
530
|
+
a terminal, the envelope is printed as one compact line; on a terminal it is
|
|
531
|
+
indented. Errors keep the structured JSON envelope on stderr.
|
|
532
|
+
|
|
533
|
+
The JSON envelope carries `schema_version`, `operation`, `target`, `outcome`,
|
|
534
|
+
and either `data` (succeeded) or `error: {category, message}` (failed). On
|
|
535
|
+
read-only operations the envelope no longer includes the `verification` block:
|
|
536
|
+
every field there is `not_applicable` on a read, so it was boilerplate. If a
|
|
537
|
+
future mutating operation reintroduces it, `verification` reappears only with
|
|
538
|
+
non-`not_applicable` content. Everything else about the envelope — field names,
|
|
539
|
+
ordering semantics, categories — is unchanged and stays parseable.
|
|
540
|
+
|
|
541
|
+
`--raw`/`--output raw` stays refused for AMS (`Raw output is refused for AMS.`),
|
|
542
|
+
and refusals keep the JSON error envelope on stderr.
|
|
543
|
+
|
|
544
|
+
```bash
|
|
545
|
+
hc ams skills list --environment dev --tenant-id TENANT_ID # pretty table
|
|
546
|
+
hc ams skills get --environment dev --tenant-id TENANT_ID --skill-id SKILL_ID # key: value
|
|
547
|
+
hc --output json ams skills list --environment dev --tenant-id TENANT_ID # compact envelope
|
|
548
|
+
hc --output json ams skills list --environment dev --tenant-id TENANT_ID | jq .data
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### What `hc ams` errors report
|
|
552
|
+
|
|
553
|
+
In `json` and `raw` modes each failed AMS command prints one JSON object on stderr with
|
|
554
|
+
an `error.category` and a message naming the real cause; in `pretty` mode the
|
|
555
|
+
same message is exactly one stderr line, `error: <message> (<category>)`:
|
|
556
|
+
|
|
557
|
+
- Local refusals keep their specific message and exit `1` with category
|
|
558
|
+
`validation`: `Raw output is refused for AMS.`, `AMS target does not match the
|
|
559
|
+
saved session; stopped before authentication.`, identity mismatches
|
|
560
|
+
(`Skill/tenant identity mismatch; stopped.`), and dot-segment ID refusals.
|
|
561
|
+
- No saved session and no target flags: category `authentication` with
|
|
562
|
+
`Not logged in; run \`hc login --environment <env> --tenant-id <tenant>\`.`
|
|
563
|
+
- A `get` whose requested ID does not exist (HTTP 404): category `not_found`
|
|
564
|
+
naming the ID, e.g. `Skill 'nosuch' was not found.` (a 404 on `list` stays
|
|
565
|
+
`validation`; departments keeps its legacy contract for all read statuses).
|
|
566
|
+
- HTTP 401/403 map to `authentication`/`authorization`; 409 to `conflict`;
|
|
567
|
+
network failures to `transport`; local output failures to `local_io`.
|
|
568
|
+
- Any unexpected internal error is category `internal` with a generic message;
|
|
569
|
+
it is never labeled `validation` and never leaks exception details.
|
|
570
|
+
|
|
571
|
+
```bash
|
|
572
|
+
hc --raw ams skills list --environment dev --tenant-id TENANT_ID # validation: raw refused
|
|
573
|
+
hc ams skills list --tenant-id OTHER_TENANT # validation: target mismatch
|
|
574
|
+
hc ams skills list # authentication: not logged in
|
|
575
|
+
hc ams skills get --skill-id NOSUCH # not_found: names the ID
|
|
576
|
+
```
|
|
577
|
+
|
|
424
578
|
Every other command requires an explicit local, non-secret target JSON file.
|
|
425
579
|
Only the canonical HTTPS AMS origin for that environment is accepted; custom
|
|
426
580
|
origins require separately verified support. Target and nested qualification keys
|
|
@@ -275,6 +275,31 @@ uv run --frozen hc logout
|
|
|
275
275
|
otherwise it prompts (with hidden password input). It stores only the email,
|
|
276
276
|
tenant/environment binding, access/refresh tokens and expiry in the strict
|
|
277
277
|
owner-only `~/.healthcloud/cli/ams-session.json`; it never stores the password.
|
|
278
|
+
|
|
279
|
+
For CI and agents, log in non-interactively: pass `--email` and pipe the password
|
|
280
|
+
to `--password-stdin` (read as exactly one stdin line, never echoed, never a
|
|
281
|
+
flag value — there is no `--password` option):
|
|
282
|
+
|
|
283
|
+
```bash
|
|
284
|
+
printf '%s\n' "$HC_AMS_ADMIN_PASSWORD" | \
|
|
285
|
+
uv run --frozen hc login --environment dev --tenant-id TENANT_ID \
|
|
286
|
+
--email "$HC_AMS_ADMIN_EMAIL" --password-stdin
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
`--email` beats `HC_AMS_ADMIN_EMAIL` and `--password-stdin` beats
|
|
290
|
+
`HC_AMS_ADMIN_PASSWORD`. With `--password-stdin`, the piped line is always the
|
|
291
|
+
password: it is read before any prompt, so it can never be consumed as the
|
|
292
|
+
email, and the email must come from `--email`, `HC_AMS_ADMIN_EMAIL` or (only on
|
|
293
|
+
an interactive terminal) the email prompt. When stdin is not an interactive
|
|
294
|
+
terminal and neither `--password-stdin` nor `HC_AMS_ADMIN_PASSWORD` supplies the
|
|
295
|
+
password, `hc login` refuses with that guidance instead of attempting a hidden
|
|
296
|
+
prompt (no `GetPassWarning`, no echoed password); a missing email on a
|
|
297
|
+
non-interactive stdin (or a process with no stdin stream at all) is refused the
|
|
298
|
+
same way, naming `--email`/`--password-stdin`; every refusal happens before any
|
|
299
|
+
login request. `--status` cannot be combined with `--email` or `--password-stdin`.
|
|
300
|
+
Even on an interactive terminal, `--password-stdin` suppresses terminal echo
|
|
301
|
+
while the single line is read and restores it afterwards; a stdin stream that is
|
|
302
|
+
closed or errors mid-read is refused cleanly (exit 2, never a traceback).
|
|
278
303
|
AMS commands may omit both target flags while this session is present, except
|
|
279
304
|
that a `prod` session still needs `--environment prod` on every command. Explicit
|
|
280
305
|
flags and target files must match it exactly. A session within 60 seconds of
|
|
@@ -302,11 +327,22 @@ uv run --frozen hc ams discover --environment dev --tenant-id TENANT_ID
|
|
|
302
327
|
Read-only agent listing, inspection and export take flags instead of a target
|
|
303
328
|
file; both may be omitted together when a matching saved session exists. Without
|
|
304
329
|
one, they log in as tenant admin using `HC_AMS_ADMIN_EMAIL` /
|
|
305
|
-
`HC_AMS_ADMIN_PASSWORD`.
|
|
330
|
+
`HC_AMS_ADMIN_PASSWORD`. They emit the same versioned result envelope as the
|
|
331
|
+
other read domains (`operation`, `outcome`, `schema_version`, `target`, `data`;
|
|
332
|
+
no `verification` block on reads); failures emit one categorized JSON error
|
|
333
|
+
envelope on stderr with the same categories (not found, target mismatch,
|
|
334
|
+
refusal, logged out), not the plain-text `Error: …` lines of earlier releases.
|
|
335
|
+
`agents list` carries bounded projections under `data.agents` plus a `page`
|
|
336
|
+
block; `agents get` carries the bounded projection under `data.agent`. The
|
|
337
|
+
legacy top-level `{"agents": …, "environment": …, "tenant_id": …}` shape is
|
|
338
|
+
intentionally replaced by this envelope — scripts must read the new shape.
|
|
339
|
+
Stdout carries bounded metadata only; `adapter` shows
|
|
306
340
|
whether an agent runs from the `file` or `db` config adapter (`unknown` when the
|
|
307
341
|
server does not say). `export` first re-checks the agent/tenant identity, then
|
|
308
342
|
saves the server's own JSON/YAML export byte-for-byte to a new owner-only `--out`
|
|
309
|
-
file, refusing (and leaving no file) when the body is
|
|
343
|
+
file (exact bytes preserved), refusing (and leaving no file) when the body is
|
|
344
|
+
not valid UTF-8; its stdout reports the envelope with the file `path`, `format`
|
|
345
|
+
and `sha256` under `data` and the `agent_id` in `target`. `export`
|
|
310
346
|
needs an AMS build that serves `/agents/{agent_id}/configuration/export`; that
|
|
311
347
|
route is not yet deployed to dev:
|
|
312
348
|
|
|
@@ -317,6 +353,23 @@ uv run --frozen hc ams agents export --environment dev --tenant-id TENANT_ID --a
|
|
|
317
353
|
--format yaml --out agent.yaml
|
|
318
354
|
```
|
|
319
355
|
|
|
356
|
+
For example, `hc ams agents list` succeeds with (formatted for readability):
|
|
357
|
+
|
|
358
|
+
```json
|
|
359
|
+
{"schema_version": 1, "operation": "agents.list", "outcome": "succeeded",
|
|
360
|
+
"target": {"environment": "dev", "tenant_id": "TENANT_ID"},
|
|
361
|
+
"data": {"agents": [{"id": "AGENT_ID", "name": "…", "adapter": "file", "…": "…"}],
|
|
362
|
+
"page": {"kind": "unpaged", "returned_count": 1, "continuation": null}}}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
and a missing `--agent-id` fails with one envelope on stderr and exit 1:
|
|
366
|
+
|
|
367
|
+
```json
|
|
368
|
+
{"schema_version": 1, "operation": "agents.get", "outcome": "failed",
|
|
369
|
+
"target": {"environment": "dev", "tenant_id": "TENANT_ID", "agent_id": "AGENT_ID"},
|
|
370
|
+
"error": {"category": "not_found", "message": "Agent 'AGENT_ID' was not found."}}
|
|
371
|
+
```
|
|
372
|
+
|
|
320
373
|
Read-only department listing and inspection use the same explicit tenant-admin
|
|
321
374
|
targeting. They emit one versioned JSON result on stdout; failures emit one
|
|
322
375
|
categorized JSON result on stderr. Department descriptions, creator identities,
|
|
@@ -403,6 +456,107 @@ additional data. Guardrail reads refuse target files, root credentials or base
|
|
|
403
456
|
URLs, raw output, blank or dot-segment guardrail IDs, and installed SDKs without
|
|
404
457
|
both guardrail methods before tenant-admin login or catalog reads.
|
|
405
458
|
|
|
459
|
+
Read-only tool catalog listing and inspection are complete and unpaged. The
|
|
460
|
+
bounded projection includes only IDs, names, read-only and confirmation flags,
|
|
461
|
+
agent types, required scopes, audit events, and activity labels:
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
uv run --frozen hc ams tools list --environment dev --tenant-id TENANT_ID
|
|
465
|
+
uv run --frozen hc ams tools get --environment dev --tenant-id TENANT_ID \
|
|
466
|
+
--tool-id TOOL_ID
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
When both target flags are omitted, these commands may use the matching saved
|
|
470
|
+
tenant-admin session. Explicit flags must match that session. Tool output never
|
|
471
|
+
includes descriptions, parameters, input schemas, PIP or connector routing, or
|
|
472
|
+
arbitrary response fields. The CLI does not execute, assign, create, update, or
|
|
473
|
+
delete tools; join agents or skills; or claim assignment, availability,
|
|
474
|
+
authorization, deployment, serving, or runtime effectiveness. It makes exactly
|
|
475
|
+
one catalog GET after tenant-admin login and does not paginate, retry, cache,
|
|
476
|
+
join, or infer additional data. Tool reads refuse target files, root credentials
|
|
477
|
+
or base URLs, raw output, blank or dot-segment IDs, and installed SDKs without
|
|
478
|
+
both tool methods before tenant-admin login or catalog reads.
|
|
479
|
+
|
|
480
|
+
Read-only data-source catalog listing and inspection are complete and unpaged.
|
|
481
|
+
The bounded projection includes only stable IDs and names:
|
|
482
|
+
|
|
483
|
+
```bash
|
|
484
|
+
uv run --frozen hc ams data-sources list --environment dev --tenant-id TENANT_ID
|
|
485
|
+
uv run --frozen hc ams data-sources get --environment dev --tenant-id TENANT_ID \
|
|
486
|
+
--data-source-id DATA_SOURCE_ID
|
|
487
|
+
```
|
|
488
|
+
|
|
489
|
+
When both target flags are omitted, these commands may use the matching saved
|
|
490
|
+
tenant-admin session. Explicit flags must match that session. Data-source output
|
|
491
|
+
never includes descriptions or arbitrary response fields and does not imply
|
|
492
|
+
access, patient consent, record availability, assignment, authorization, or
|
|
493
|
+
runtime effectiveness. The CLI does not create, update, delete, assign, access
|
|
494
|
+
records through, or execute data sources. It makes exactly one catalog GET after
|
|
495
|
+
tenant-admin login and does not paginate, retry, cache, truncate, join, or infer
|
|
496
|
+
additional data. Data-source reads refuse target files, root credentials or base
|
|
497
|
+
URLs, raw output, blank or dot-segment IDs, and installed SDKs without both
|
|
498
|
+
data-source methods before tenant-admin login or catalog reads.
|
|
499
|
+
|
|
500
|
+
### AMS output modes
|
|
501
|
+
|
|
502
|
+
The read commands (`hc ams skills|tools|policies|guardrails|data-sources|teams|departments list|get`
|
|
503
|
+
and `hc ams organization hierarchy`) render in two modes (`--output` on the root
|
|
504
|
+
`hc` command; `pretty` is the default):
|
|
505
|
+
|
|
506
|
+
- **`pretty`** (default): a compact human view. Lists print an `ID`/`NAME`
|
|
507
|
+
table with an entry count; a single record prints `key: value` lines; the
|
|
508
|
+
organization hierarchy prints an indented tree. Each failed AMS command
|
|
509
|
+
prints one stderr line, `error: <message> (<category>)` (flag/argument
|
|
510
|
+
usage errors from Click print Click's standard usage message instead).
|
|
511
|
+
- **`json`**: the stable result envelope (below) on stdout. When stdout is not
|
|
512
|
+
a terminal, the envelope is printed as one compact line; on a terminal it is
|
|
513
|
+
indented. Errors keep the structured JSON envelope on stderr.
|
|
514
|
+
|
|
515
|
+
The JSON envelope carries `schema_version`, `operation`, `target`, `outcome`,
|
|
516
|
+
and either `data` (succeeded) or `error: {category, message}` (failed). On
|
|
517
|
+
read-only operations the envelope no longer includes the `verification` block:
|
|
518
|
+
every field there is `not_applicable` on a read, so it was boilerplate. If a
|
|
519
|
+
future mutating operation reintroduces it, `verification` reappears only with
|
|
520
|
+
non-`not_applicable` content. Everything else about the envelope — field names,
|
|
521
|
+
ordering semantics, categories — is unchanged and stays parseable.
|
|
522
|
+
|
|
523
|
+
`--raw`/`--output raw` stays refused for AMS (`Raw output is refused for AMS.`),
|
|
524
|
+
and refusals keep the JSON error envelope on stderr.
|
|
525
|
+
|
|
526
|
+
```bash
|
|
527
|
+
hc ams skills list --environment dev --tenant-id TENANT_ID # pretty table
|
|
528
|
+
hc ams skills get --environment dev --tenant-id TENANT_ID --skill-id SKILL_ID # key: value
|
|
529
|
+
hc --output json ams skills list --environment dev --tenant-id TENANT_ID # compact envelope
|
|
530
|
+
hc --output json ams skills list --environment dev --tenant-id TENANT_ID | jq .data
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### What `hc ams` errors report
|
|
534
|
+
|
|
535
|
+
In `json` and `raw` modes each failed AMS command prints one JSON object on stderr with
|
|
536
|
+
an `error.category` and a message naming the real cause; in `pretty` mode the
|
|
537
|
+
same message is exactly one stderr line, `error: <message> (<category>)`:
|
|
538
|
+
|
|
539
|
+
- Local refusals keep their specific message and exit `1` with category
|
|
540
|
+
`validation`: `Raw output is refused for AMS.`, `AMS target does not match the
|
|
541
|
+
saved session; stopped before authentication.`, identity mismatches
|
|
542
|
+
(`Skill/tenant identity mismatch; stopped.`), and dot-segment ID refusals.
|
|
543
|
+
- No saved session and no target flags: category `authentication` with
|
|
544
|
+
`Not logged in; run \`hc login --environment <env> --tenant-id <tenant>\`.`
|
|
545
|
+
- A `get` whose requested ID does not exist (HTTP 404): category `not_found`
|
|
546
|
+
naming the ID, e.g. `Skill 'nosuch' was not found.` (a 404 on `list` stays
|
|
547
|
+
`validation`; departments keeps its legacy contract for all read statuses).
|
|
548
|
+
- HTTP 401/403 map to `authentication`/`authorization`; 409 to `conflict`;
|
|
549
|
+
network failures to `transport`; local output failures to `local_io`.
|
|
550
|
+
- Any unexpected internal error is category `internal` with a generic message;
|
|
551
|
+
it is never labeled `validation` and never leaks exception details.
|
|
552
|
+
|
|
553
|
+
```bash
|
|
554
|
+
hc --raw ams skills list --environment dev --tenant-id TENANT_ID # validation: raw refused
|
|
555
|
+
hc ams skills list --tenant-id OTHER_TENANT # validation: target mismatch
|
|
556
|
+
hc ams skills list # authentication: not logged in
|
|
557
|
+
hc ams skills get --skill-id NOSUCH # not_found: names the ID
|
|
558
|
+
```
|
|
559
|
+
|
|
406
560
|
Every other command requires an explicit local, non-secret target JSON file.
|
|
407
561
|
Only the canonical HTTPS AMS origin for that environment is accepted; custom
|
|
408
562
|
origins require separately verified support. Target and nested qualification keys
|
|
@@ -6,11 +6,10 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
import click
|
|
8
8
|
|
|
9
|
-
from ..compatibility import safe_errors
|
|
10
9
|
from ..context import AmsDependencies, pass_ams, preflight_agents
|
|
11
|
-
from ..
|
|
10
|
+
from ..errors import run_domain
|
|
12
11
|
from ..session import resolve_tenant_target
|
|
13
|
-
from .operations import
|
|
12
|
+
from .operations import export_agent, get_agent, list_agents
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def _environment(function):
|
|
@@ -25,22 +24,21 @@ def _agent(function):
|
|
|
25
24
|
return click.option("--agent-id", required=True)(function)
|
|
26
25
|
|
|
27
26
|
|
|
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
27
|
def create_list_command(dependencies: AmsDependencies) -> click.Command:
|
|
36
28
|
@click.command("list")
|
|
37
29
|
@_environment
|
|
38
30
|
@_tenant
|
|
39
31
|
@pass_ams
|
|
40
|
-
@safe_errors
|
|
41
32
|
def command(invocation, environment: str | None, tenant_id: str | None) -> None:
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
target_data = {"environment": environment, "tenant_id": tenant_id}
|
|
34
|
+
|
|
35
|
+
def execute():
|
|
36
|
+
target = resolve_tenant_target(environment, tenant_id)
|
|
37
|
+
target_data.update(environment=target.environment, tenant_id=target.tenant_id)
|
|
38
|
+
preflight_agents(invocation)
|
|
39
|
+
return list_agents(dependencies, target)
|
|
40
|
+
|
|
41
|
+
run_domain("agents.list", target_data, execute)
|
|
44
42
|
|
|
45
43
|
return command
|
|
46
44
|
|
|
@@ -51,10 +49,16 @@ def create_get_command(dependencies: AmsDependencies) -> click.Command:
|
|
|
51
49
|
@_tenant
|
|
52
50
|
@_agent
|
|
53
51
|
@pass_ams
|
|
54
|
-
@safe_errors
|
|
55
52
|
def command(invocation, environment: str | None, tenant_id: str | None, agent_id: str) -> None:
|
|
56
|
-
|
|
57
|
-
|
|
53
|
+
target_data = {"environment": environment, "tenant_id": tenant_id, "agent_id": agent_id}
|
|
54
|
+
|
|
55
|
+
def execute():
|
|
56
|
+
target = resolve_tenant_target(environment, tenant_id)
|
|
57
|
+
target_data.update(environment=target.environment, tenant_id=target.tenant_id)
|
|
58
|
+
preflight_agents(invocation)
|
|
59
|
+
return get_agent(dependencies, target, agent_id)
|
|
60
|
+
|
|
61
|
+
run_domain("agents.get", target_data, execute)
|
|
58
62
|
|
|
59
63
|
return command
|
|
60
64
|
|
|
@@ -67,7 +71,6 @@ def create_export_command(dependencies: AmsDependencies) -> click.Command:
|
|
|
67
71
|
@click.option("--out", required=True, type=click.Path(path_type=Path, dir_okay=False))
|
|
68
72
|
@click.option("--format", "export_format", type=click.Choice(["json", "yaml"]), default="json", show_default=True)
|
|
69
73
|
@pass_ams
|
|
70
|
-
@safe_errors
|
|
71
74
|
def command(
|
|
72
75
|
invocation,
|
|
73
76
|
environment: str | None,
|
|
@@ -76,8 +79,14 @@ def create_export_command(dependencies: AmsDependencies) -> click.Command:
|
|
|
76
79
|
out: Path,
|
|
77
80
|
export_format: str,
|
|
78
81
|
) -> None:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
+
target_data = {"environment": environment, "tenant_id": tenant_id, "agent_id": agent_id}
|
|
83
|
+
|
|
84
|
+
def execute():
|
|
85
|
+
target = resolve_tenant_target(environment, tenant_id)
|
|
86
|
+
target_data.update(environment=target.environment, tenant_id=target.tenant_id)
|
|
87
|
+
preflight_agents(invocation)
|
|
88
|
+
return export_agent(dependencies, target, agent_id, out, export_format)
|
|
89
|
+
|
|
90
|
+
run_domain("agents.export", target_data, execute)
|
|
82
91
|
|
|
83
92
|
return command
|
|
@@ -7,18 +7,29 @@ from pathlib import Path
|
|
|
7
7
|
from typing import Literal
|
|
8
8
|
|
|
9
9
|
from ..context import AmsDependencies
|
|
10
|
+
from ..domain_errors import DomainReadRefused
|
|
10
11
|
from ..output import private_text_output
|
|
12
|
+
from ..results import succeeded
|
|
11
13
|
from ..target import TenantTarget
|
|
12
14
|
from .projections import detail, summary
|
|
13
15
|
|
|
14
16
|
|
|
15
|
-
class AgentReadRefused(
|
|
16
|
-
"""A
|
|
17
|
+
class AgentReadRefused(DomainReadRefused):
|
|
18
|
+
"""A categorized agent refusal safe for the CLI adapter to expose."""
|
|
19
|
+
|
|
20
|
+
pass
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
def _agent_id(value: str) -> str:
|
|
20
24
|
if not isinstance(value, str) or not value.strip():
|
|
21
|
-
raise AgentReadRefused("Explicit agent ID required.")
|
|
25
|
+
raise AgentReadRefused("validation", "Explicit agent ID required.")
|
|
26
|
+
return value
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _target(target: TenantTarget, agent_id: str | None = None) -> dict:
|
|
30
|
+
value = {"environment": target.environment, "tenant_id": target.tenant_id}
|
|
31
|
+
if agent_id is not None:
|
|
32
|
+
value["agent_id"] = agent_id
|
|
22
33
|
return value
|
|
23
34
|
|
|
24
35
|
|
|
@@ -26,18 +37,23 @@ def list_agents(dependencies: AmsDependencies, target: TenantTarget) -> dict:
|
|
|
26
37
|
with dependencies.authenticated_sdk_factory(target) as sdk:
|
|
27
38
|
agents = sdk.ams_admin.list_agents()
|
|
28
39
|
if any(agent.tenant_id != target.tenant_id for agent in agents):
|
|
29
|
-
raise AgentReadRefused("Agent/tenant identity mismatch; stopped.")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
raise AgentReadRefused("validation", "Agent/tenant identity mismatch; stopped.")
|
|
41
|
+
projections = [summary(agent) for agent in agents]
|
|
42
|
+
return succeeded(
|
|
43
|
+
"agents.list",
|
|
44
|
+
_target(target),
|
|
45
|
+
{
|
|
46
|
+
"agents": projections,
|
|
47
|
+
"page": {"kind": "unpaged", "returned_count": len(projections), "continuation": None},
|
|
48
|
+
},
|
|
49
|
+
read_only=True,
|
|
50
|
+
)
|
|
35
51
|
|
|
36
52
|
|
|
37
53
|
def _verified_agent(sdk, target: TenantTarget, agent_id: str):
|
|
38
54
|
agent = sdk.ams_admin.get_agent(agent_id)
|
|
39
55
|
if agent is None or agent.id != agent_id or agent.tenant_id != target.tenant_id:
|
|
40
|
-
raise AgentReadRefused("Agent/tenant identity mismatch; stopped.")
|
|
56
|
+
raise AgentReadRefused("validation", "Agent/tenant identity mismatch; stopped.")
|
|
41
57
|
return agent
|
|
42
58
|
|
|
43
59
|
|
|
@@ -45,7 +61,12 @@ def get_agent(dependencies: AmsDependencies, target: TenantTarget, agent_id: str
|
|
|
45
61
|
agent_id = _agent_id(agent_id)
|
|
46
62
|
with dependencies.authenticated_sdk_factory(target) as sdk:
|
|
47
63
|
agent = _verified_agent(sdk, target, agent_id)
|
|
48
|
-
return
|
|
64
|
+
return succeeded(
|
|
65
|
+
"agents.get",
|
|
66
|
+
_target(target, agent_id),
|
|
67
|
+
{"agent": detail(agent)},
|
|
68
|
+
read_only=True,
|
|
69
|
+
)
|
|
49
70
|
|
|
50
71
|
|
|
51
72
|
def export_agent(
|
|
@@ -57,16 +78,18 @@ def export_agent(
|
|
|
57
78
|
) -> dict:
|
|
58
79
|
agent_id = _agent_id(agent_id)
|
|
59
80
|
if export_format not in ("json", "yaml"):
|
|
60
|
-
raise AgentReadRefused("Export format must be json or yaml.")
|
|
81
|
+
raise AgentReadRefused("validation", "Export format must be json or yaml.")
|
|
61
82
|
with private_text_output(output) as write, dependencies.authenticated_sdk_factory(target) as sdk:
|
|
62
83
|
_verified_agent(sdk, target, agent_id)
|
|
63
84
|
text = sdk.ams_admin.export_agent_configuration(agent_id, format=export_format)
|
|
64
85
|
write(text)
|
|
65
|
-
return
|
|
66
|
-
"
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
86
|
+
return succeeded(
|
|
87
|
+
"agents.export",
|
|
88
|
+
_target(target, agent_id),
|
|
89
|
+
{
|
|
90
|
+
"path": str(output),
|
|
91
|
+
"format": export_format,
|
|
92
|
+
"sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(),
|
|
93
|
+
},
|
|
94
|
+
read_only=True,
|
|
95
|
+
)
|
|
@@ -197,6 +197,48 @@ def preflight_guardrails(invocation: AmsContext, target: TenantTarget) -> None:
|
|
|
197
197
|
sdk.close()
|
|
198
198
|
|
|
199
199
|
|
|
200
|
+
def preflight_tools(invocation: AmsContext, target: TenantTarget) -> None:
|
|
201
|
+
"""Validate local tool-read compatibility before login or data reads."""
|
|
202
|
+
|
|
203
|
+
_check_root(invocation)
|
|
204
|
+
if invocation.target_file is not None:
|
|
205
|
+
raise click.ClickException("Tools requires explicit environment/tenant flags, without a target file.")
|
|
206
|
+
sdk = HCSDK(environment=target.environment, tenant_id=target.tenant_id)
|
|
207
|
+
try:
|
|
208
|
+
resource = getattr(getattr(sdk, "ams_admin", None), "tools", None)
|
|
209
|
+
if (
|
|
210
|
+
resource is None
|
|
211
|
+
or not callable(getattr(resource, "list", None))
|
|
212
|
+
or not callable(getattr(resource, "get", None))
|
|
213
|
+
):
|
|
214
|
+
raise click.ClickException(
|
|
215
|
+
"SDK missing ams_admin tools; use the reviewed SDK version required by this CLI."
|
|
216
|
+
)
|
|
217
|
+
finally:
|
|
218
|
+
sdk.close()
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def preflight_data_sources(invocation: AmsContext, target: TenantTarget) -> None:
|
|
222
|
+
"""Validate local data-source-read compatibility before login or data reads."""
|
|
223
|
+
|
|
224
|
+
_check_root(invocation)
|
|
225
|
+
if invocation.target_file is not None:
|
|
226
|
+
raise click.ClickException("Data sources requires explicit environment/tenant flags, without a target file.")
|
|
227
|
+
sdk = HCSDK(environment=target.environment, tenant_id=target.tenant_id)
|
|
228
|
+
try:
|
|
229
|
+
resource = getattr(getattr(sdk, "ams_admin", None), "data_sources", None)
|
|
230
|
+
if (
|
|
231
|
+
resource is None
|
|
232
|
+
or not callable(getattr(resource, "list", None))
|
|
233
|
+
or not callable(getattr(resource, "get", None))
|
|
234
|
+
):
|
|
235
|
+
raise click.ClickException(
|
|
236
|
+
"SDK missing ams_admin data sources; use the reviewed SDK version required by this CLI."
|
|
237
|
+
)
|
|
238
|
+
finally:
|
|
239
|
+
sdk.close()
|
|
240
|
+
|
|
241
|
+
|
|
200
242
|
def require_target(invocation: AmsContext) -> AmsTarget:
|
|
201
243
|
"""Validate root options and load the explicitly selected AMS target."""
|
|
202
244
|
|