healthcloud-cli 3.7.7__tar.gz → 3.7.8__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.7 → healthcloud_cli-3.7.8}/PKG-INFO +37 -8
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/README.md +36 -7
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/output.py +59 -13
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/sessions/operations.py +94 -18
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/sessions/projections.py +26 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/windows_files.py +19 -3
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/pyproject.toml +1 -1
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/.gitignore +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/hatch_build.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/admins/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/admins/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/admins/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/admins/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/agents/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/agents/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/agents/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/agents/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/agents/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/auth.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/compatibility.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/context.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/departments/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/departments/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/departments/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/departments/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/departments/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/domain_errors.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/errors.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/guardrails/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/guardrails/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/guardrails/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/guardrails/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/guardrails/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/policies/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/policies/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/policies/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/policies/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/policies/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/pretty.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/registry.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/results.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/session.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/sessions/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/sessions/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/sessions/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/skills/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/skills/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/skills/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/skills/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/skills/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/target.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/teams/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/teams/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/teams/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/teams/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/teams/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/tools/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/tools/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/tools/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/tools/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/tools/projections.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/tuning.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/ui.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/cli.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/context.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/contracts/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/contracts/mcp-tools.manifest.json +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/main.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/manifest.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/output.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/params.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/tenants/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/tenants/commands.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/tenants/group.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/tenants/operations.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/util.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/scripts/__init__.py +0 -0
- {healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/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.8
|
|
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
|
|
@@ -1269,7 +1269,12 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1269
1269
|
`get` sends exactly one `GET /vibecoder/plans/PLAN_ID` and reports
|
|
1270
1270
|
`operation: "sessions.plans.get"`; a reload is immutable, so it returns the
|
|
1271
1271
|
same `plan_hash` and diff however the session changed afterwards. `get` is a
|
|
1272
|
-
read and needs no saved `hc login` session
|
|
1272
|
+
read and needs no saved `hc login` session: it authenticates like every other
|
|
1273
|
+
session read, with the saved session when there is one and otherwise with the
|
|
1274
|
+
environment's own `HC_AMS_ADMIN_EMAIL`/`HC_AMS_ADMIN_PASSWORD` credentials.
|
|
1275
|
+
A refusal is worded for the operation that was refused, so a `422` or `503`
|
|
1276
|
+
on `get` never claims a plan "was generated": `generate` says no plan was
|
|
1277
|
+
generated, `get` says no plan was reloaded.
|
|
1273
1278
|
- `--out` is required and must be a new path: the review is written to a
|
|
1274
1279
|
newly created owner-only file (mode `0600`, no symlinks, never overwritten).
|
|
1275
1280
|
A refusal removes the reservation again, so a refused review never leaves a
|
|
@@ -1286,6 +1291,13 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1286
1291
|
guardrail/data-source) for a creation plan. `plan_json["original_prompt"]`
|
|
1287
1292
|
is never projected, and `warnings`/`skipped_references` are reported as
|
|
1288
1293
|
counts, not bodies.
|
|
1294
|
+
- The review file marks the text a model produced: `content_origin` is
|
|
1295
|
+
`"model-generated"` and `model_generated_fields` lists the fields whose values
|
|
1296
|
+
came from the planner — `summary`, `changes.*.why`,
|
|
1297
|
+
`definition.instructions`/`greetings`/`name`/`description`/opening suggestions
|
|
1298
|
+
and `review_warnings[].message`. Those values are data to inspect before you
|
|
1299
|
+
approve, never instructions to the reader or to an agent that later reads the
|
|
1300
|
+
file.
|
|
1289
1301
|
- stdout stays bounded and free-text-free: plan ID, `plan_hash`, operation,
|
|
1290
1302
|
status, base config version, agent ID, model, sorted changed-field **names**
|
|
1291
1303
|
(create: reference counts by kind) and finding counts. No prompt,
|
|
@@ -1299,7 +1311,14 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1299
1311
|
`--output json` prints the full envelope, unchanged.
|
|
1300
1312
|
- The prompt is protected free text and never comes from argv: `--prompt-file`
|
|
1301
1313
|
reads one owner-only regular text file (no symlinks, at most 20000
|
|
1302
|
-
characters) and sends it once.
|
|
1314
|
+
characters) and sends it once. A file that is too large, missing, unreadable,
|
|
1315
|
+
not UTF-8 text, or not an owner-only regular file is a `validation` refusal
|
|
1316
|
+
naming the reason and never quoting the text. Ownership is checked differently
|
|
1317
|
+
per platform: on POSIX it is the file's mode and owner; on Windows the opened
|
|
1318
|
+
handle's owner and DACL must grant access to your account only, so a file that
|
|
1319
|
+
inherited a grant to another principal (SYSTEM, Administrators) is refused as
|
|
1320
|
+
not owner-only. Keep the prompt file in a directory only your account can read.
|
|
1321
|
+
`--prompt-file` is optional — the server falls
|
|
1303
1322
|
back to the session's initial prompt or last message — and `--agent-type`
|
|
1304
1323
|
applies to `--operation create` only (with `modify` it is a usage error
|
|
1305
1324
|
before login or any request). `--prompt-file` is not mirrored as a local
|
|
@@ -1320,12 +1339,22 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1320
1339
|
values, so read it in the AMS console. Non-`advisory` review findings are
|
|
1321
1340
|
refused the same way; advisory findings are displayed in the review file.
|
|
1322
1341
|
A creation plan that claims an `agent_id` is refused, and no existing agent
|
|
1323
|
-
ID is ever fabricated or required.
|
|
1342
|
+
ID is ever fabricated or required. So is a creation plan whose proposed agent
|
|
1343
|
+
carries a field outside the documented definition and reference set
|
|
1344
|
+
(`name`, `description`, `type`, `model`, `preferred_model`,
|
|
1345
|
+
`is_orchestrator`, `department_id`, `team_id`, `instructions`, `greetings`,
|
|
1346
|
+
`opening_suggestions`, `opening_suggestions_options`, and the catalog
|
|
1347
|
+
`*_ids`), or whose reference field holds anything but non-blank catalog IDs:
|
|
1348
|
+
the operator never approves a definition the review did not show.
|
|
1324
1349
|
- Server answers are preserved, not replaced: a missing, expired or other
|
|
1325
|
-
user's session or plan is a `not_found` refusal naming it,
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1350
|
+
user's session or plan is a `not_found` refusal naming it, and an unavailable
|
|
1351
|
+
planner (`503`) is a `transport` refusal with no file written. A `422` from
|
|
1352
|
+
the plan route is a `validation` refusal, but its detail is only quoted when
|
|
1353
|
+
it is a known static contract message that carries no field values — the
|
|
1354
|
+
documented `"a prompt is required for agent creation"`. Any other `422`
|
|
1355
|
+
detail can quote protected field values, exactly like `validation_errors`
|
|
1356
|
+
and blocking review findings, so it is reported by category only and you read
|
|
1357
|
+
it in the AMS console. A dropped connection on `generate` reports one `transport`
|
|
1329
1358
|
error saying the plan's outcome is unknown and points at
|
|
1330
1359
|
`hc ams sessions messages list`/`plans get` before retrying; the request is
|
|
1331
1360
|
never re-sent automatically.
|
|
@@ -1251,7 +1251,12 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1251
1251
|
`get` sends exactly one `GET /vibecoder/plans/PLAN_ID` and reports
|
|
1252
1252
|
`operation: "sessions.plans.get"`; a reload is immutable, so it returns the
|
|
1253
1253
|
same `plan_hash` and diff however the session changed afterwards. `get` is a
|
|
1254
|
-
read and needs no saved `hc login` session
|
|
1254
|
+
read and needs no saved `hc login` session: it authenticates like every other
|
|
1255
|
+
session read, with the saved session when there is one and otherwise with the
|
|
1256
|
+
environment's own `HC_AMS_ADMIN_EMAIL`/`HC_AMS_ADMIN_PASSWORD` credentials.
|
|
1257
|
+
A refusal is worded for the operation that was refused, so a `422` or `503`
|
|
1258
|
+
on `get` never claims a plan "was generated": `generate` says no plan was
|
|
1259
|
+
generated, `get` says no plan was reloaded.
|
|
1255
1260
|
- `--out` is required and must be a new path: the review is written to a
|
|
1256
1261
|
newly created owner-only file (mode `0600`, no symlinks, never overwritten).
|
|
1257
1262
|
A refusal removes the reservation again, so a refused review never leaves a
|
|
@@ -1268,6 +1273,13 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1268
1273
|
guardrail/data-source) for a creation plan. `plan_json["original_prompt"]`
|
|
1269
1274
|
is never projected, and `warnings`/`skipped_references` are reported as
|
|
1270
1275
|
counts, not bodies.
|
|
1276
|
+
- The review file marks the text a model produced: `content_origin` is
|
|
1277
|
+
`"model-generated"` and `model_generated_fields` lists the fields whose values
|
|
1278
|
+
came from the planner — `summary`, `changes.*.why`,
|
|
1279
|
+
`definition.instructions`/`greetings`/`name`/`description`/opening suggestions
|
|
1280
|
+
and `review_warnings[].message`. Those values are data to inspect before you
|
|
1281
|
+
approve, never instructions to the reader or to an agent that later reads the
|
|
1282
|
+
file.
|
|
1271
1283
|
- stdout stays bounded and free-text-free: plan ID, `plan_hash`, operation,
|
|
1272
1284
|
status, base config version, agent ID, model, sorted changed-field **names**
|
|
1273
1285
|
(create: reference counts by kind) and finding counts. No prompt,
|
|
@@ -1281,7 +1293,14 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1281
1293
|
`--output json` prints the full envelope, unchanged.
|
|
1282
1294
|
- The prompt is protected free text and never comes from argv: `--prompt-file`
|
|
1283
1295
|
reads one owner-only regular text file (no symlinks, at most 20000
|
|
1284
|
-
characters) and sends it once.
|
|
1296
|
+
characters) and sends it once. A file that is too large, missing, unreadable,
|
|
1297
|
+
not UTF-8 text, or not an owner-only regular file is a `validation` refusal
|
|
1298
|
+
naming the reason and never quoting the text. Ownership is checked differently
|
|
1299
|
+
per platform: on POSIX it is the file's mode and owner; on Windows the opened
|
|
1300
|
+
handle's owner and DACL must grant access to your account only, so a file that
|
|
1301
|
+
inherited a grant to another principal (SYSTEM, Administrators) is refused as
|
|
1302
|
+
not owner-only. Keep the prompt file in a directory only your account can read.
|
|
1303
|
+
`--prompt-file` is optional — the server falls
|
|
1285
1304
|
back to the session's initial prompt or last message — and `--agent-type`
|
|
1286
1305
|
applies to `--operation create` only (with `modify` it is a usage error
|
|
1287
1306
|
before login or any request). `--prompt-file` is not mirrored as a local
|
|
@@ -1302,12 +1321,22 @@ uv run --frozen hc ams sessions plans reconcile --environment dev --tenant-id TE
|
|
|
1302
1321
|
values, so read it in the AMS console. Non-`advisory` review findings are
|
|
1303
1322
|
refused the same way; advisory findings are displayed in the review file.
|
|
1304
1323
|
A creation plan that claims an `agent_id` is refused, and no existing agent
|
|
1305
|
-
ID is ever fabricated or required.
|
|
1324
|
+
ID is ever fabricated or required. So is a creation plan whose proposed agent
|
|
1325
|
+
carries a field outside the documented definition and reference set
|
|
1326
|
+
(`name`, `description`, `type`, `model`, `preferred_model`,
|
|
1327
|
+
`is_orchestrator`, `department_id`, `team_id`, `instructions`, `greetings`,
|
|
1328
|
+
`opening_suggestions`, `opening_suggestions_options`, and the catalog
|
|
1329
|
+
`*_ids`), or whose reference field holds anything but non-blank catalog IDs:
|
|
1330
|
+
the operator never approves a definition the review did not show.
|
|
1306
1331
|
- Server answers are preserved, not replaced: a missing, expired or other
|
|
1307
|
-
user's session or plan is a `not_found` refusal naming it,
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1332
|
+
user's session or plan is a `not_found` refusal naming it, and an unavailable
|
|
1333
|
+
planner (`503`) is a `transport` refusal with no file written. A `422` from
|
|
1334
|
+
the plan route is a `validation` refusal, but its detail is only quoted when
|
|
1335
|
+
it is a known static contract message that carries no field values — the
|
|
1336
|
+
documented `"a prompt is required for agent creation"`. Any other `422`
|
|
1337
|
+
detail can quote protected field values, exactly like `validation_errors`
|
|
1338
|
+
and blocking review findings, so it is reported by category only and you read
|
|
1339
|
+
it in the AMS console. A dropped connection on `generate` reports one `transport`
|
|
1311
1340
|
error saying the plan's outcome is unknown and points at
|
|
1312
1341
|
`hc ams sessions messages list`/`plans get` before retrying; the request is
|
|
1313
1342
|
never re-sent automatically.
|
|
@@ -66,34 +66,75 @@ def read_json(path: Path, *, private: bool = False) -> dict:
|
|
|
66
66
|
) from None
|
|
67
67
|
|
|
68
68
|
|
|
69
|
+
class PrivateInputRefused(ValueError):
|
|
70
|
+
"""A protected input file the reader refused, with a value-free reason.
|
|
71
|
+
|
|
72
|
+
``reason`` names the failed property — ``too_large``, ``not_owner_only``,
|
|
73
|
+
``not_text`` or ``unreadable`` — never the file's path or content, so a
|
|
74
|
+
caller can map it to its own categorized refusal (a bare click error is not
|
|
75
|
+
the command's error contract) without quoting protected text.
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self, reason: str) -> None:
|
|
79
|
+
super().__init__(reason)
|
|
80
|
+
self.reason = reason
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def private_input_detail(reason: str, *, maximum: int) -> str:
|
|
84
|
+
"""A bounded, value-free sentence explaining why a protected file was refused."""
|
|
85
|
+
|
|
86
|
+
if reason == "too_large":
|
|
87
|
+
return f"it is larger than {maximum} characters"
|
|
88
|
+
if reason == "not_text":
|
|
89
|
+
return "it is not readable UTF-8 text"
|
|
90
|
+
if reason in ("not_owner_only", "not_regular"):
|
|
91
|
+
return "it is not an owner-only regular file"
|
|
92
|
+
return "it cannot be read"
|
|
93
|
+
|
|
94
|
+
|
|
69
95
|
def read_private_text(path: Path, *, maximum: int) -> str:
|
|
70
96
|
"""Read one owner-only plain-text input without quoting its content.
|
|
71
97
|
|
|
72
98
|
``maximum`` is a character bound checked before the text is handed on, so
|
|
73
99
|
an oversized protected input is refused locally instead of being sent. The
|
|
74
|
-
text itself is never included in the refusal: only the reason
|
|
100
|
+
text itself is never included in the refusal: only the reason, as a
|
|
101
|
+
:class:`PrivateInputRefused` carrying one of ``too_large``,
|
|
102
|
+
``not_owner_only``, ``not_regular``, ``not_text`` or ``unreadable``.
|
|
103
|
+
|
|
104
|
+
The owner-only check is a POSIX mode/ownership check. On Windows the same
|
|
105
|
+
guarantee comes from ``windows_files``, which validates the opened handle's
|
|
106
|
+
owner and DACL; its refusal carries its own reason (``not_owner_only`` or
|
|
107
|
+
``not_regular``) and is reported unchanged here, so a non-private file is not
|
|
108
|
+
misreported as unreadable. An inherited ACL cannot be made owner-only by the
|
|
109
|
+
creator, so the caller must still keep the file in a directory only the
|
|
110
|
+
operator can read.
|
|
75
111
|
"""
|
|
76
112
|
try:
|
|
77
113
|
if os.name == "nt":
|
|
78
|
-
from .windows_files import open_file
|
|
114
|
+
from .windows_files import PrivateHandleRefused, open_file
|
|
79
115
|
|
|
80
|
-
|
|
116
|
+
try:
|
|
117
|
+
fd = open_file(path, private=True)
|
|
118
|
+
except PrivateHandleRefused as error:
|
|
119
|
+
raise PrivateInputRefused(error.reason) from None
|
|
81
120
|
else:
|
|
82
121
|
fd = os.open(path, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
|
|
83
122
|
with os.fdopen(fd, encoding="utf-8") as stream:
|
|
84
123
|
info = os.fstat(stream.fileno())
|
|
85
|
-
if not stat.S_ISREG(info.st_mode)
|
|
86
|
-
|
|
87
|
-
):
|
|
88
|
-
raise
|
|
124
|
+
if not stat.S_ISREG(info.st_mode):
|
|
125
|
+
raise PrivateInputRefused("not_regular")
|
|
126
|
+
if os.name != "nt" and (info.st_mode & 0o077 or info.st_uid != os.getuid()):
|
|
127
|
+
raise PrivateInputRefused("not_owner_only")
|
|
89
128
|
text = stream.read(maximum + 1)
|
|
90
129
|
if len(text) > maximum:
|
|
91
|
-
raise
|
|
130
|
+
raise PrivateInputRefused("too_large")
|
|
92
131
|
return text
|
|
93
|
-
except
|
|
94
|
-
raise
|
|
95
|
-
|
|
96
|
-
) from None
|
|
132
|
+
except PrivateInputRefused:
|
|
133
|
+
raise
|
|
134
|
+
except UnicodeError:
|
|
135
|
+
raise PrivateInputRefused("not_text") from None
|
|
136
|
+
except OSError, ValueError:
|
|
137
|
+
raise PrivateInputRefused("unreadable") from None
|
|
97
138
|
|
|
98
139
|
|
|
99
140
|
def protected_text(
|
|
@@ -132,7 +173,12 @@ def protected_text(
|
|
|
132
173
|
if path is not None:
|
|
133
174
|
if not isinstance(path, str) or not path.strip():
|
|
134
175
|
raise refuse("validation", f"Explicit {file_flag} file path required.")
|
|
135
|
-
|
|
176
|
+
try:
|
|
177
|
+
text = read_private_text(Path(path), maximum=PROTECTED_TEXT_MAX_CHARS)
|
|
178
|
+
except PrivateInputRefused:
|
|
179
|
+
raise click.ClickException(
|
|
180
|
+
"Invalid input file; require a readable, owner-only regular text file without symlinks."
|
|
181
|
+
) from None
|
|
136
182
|
if not text:
|
|
137
183
|
raise refuse("validation", f"The file given to {file_flag} is empty; nothing was sent.")
|
|
138
184
|
return text
|
|
@@ -7,7 +7,14 @@ from pathlib import Path
|
|
|
7
7
|
|
|
8
8
|
from ..context import AmsDependencies
|
|
9
9
|
from ..domain_errors import DomainOutcomeUnknown, DomainReadRefused
|
|
10
|
-
from ..output import
|
|
10
|
+
from ..output import (
|
|
11
|
+
PROTECTED_TEXT_MAX_CHARS,
|
|
12
|
+
PrivateInputRefused,
|
|
13
|
+
private_input_detail,
|
|
14
|
+
private_output,
|
|
15
|
+
protected_text,
|
|
16
|
+
read_private_text,
|
|
17
|
+
)
|
|
11
18
|
from ..results import succeeded, unknown
|
|
12
19
|
from ..target import TenantTarget
|
|
13
20
|
from .projections import (
|
|
@@ -292,16 +299,21 @@ def _plan_operation(value: str) -> str:
|
|
|
292
299
|
|
|
293
300
|
|
|
294
301
|
def _prompt_file(path: str) -> str:
|
|
295
|
-
"""Read the protected prompt from one owner-only text file, never argv.
|
|
302
|
+
"""Read the protected prompt from one owner-only text file, never argv.
|
|
303
|
+
|
|
304
|
+
``read_private_text`` applies the size bound itself, so oversize, unsafe and
|
|
305
|
+
unreadable files are all refused here in the plan's own ``validation``
|
|
306
|
+
category instead of a bare click error. There is deliberately no second
|
|
307
|
+
length check: a check after that read could never run.
|
|
308
|
+
"""
|
|
296
309
|
|
|
297
310
|
if not isinstance(path, str) or not path.strip():
|
|
298
311
|
raise PlanReviewRefused("validation", "Explicit prompt file path required.")
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
)
|
|
304
|
-
return text
|
|
312
|
+
try:
|
|
313
|
+
return read_private_text(Path(path), maximum=PROMPT_MAX_CHARS)
|
|
314
|
+
except PrivateInputRefused as error:
|
|
315
|
+
detail = private_input_detail(error.reason, maximum=PROMPT_MAX_CHARS)
|
|
316
|
+
raise PlanReviewRefused("validation", f"The prompt file was refused: {detail}; nothing was sent.") from None
|
|
305
317
|
|
|
306
318
|
|
|
307
319
|
def _plans(sdk, methods, *, write: bool = False, what: str | None = None):
|
|
@@ -471,15 +483,34 @@ def _modify_diff(body: dict) -> dict:
|
|
|
471
483
|
|
|
472
484
|
|
|
473
485
|
def _references(agent: dict) -> dict:
|
|
486
|
+
"""Group the proposal's catalog references by kind, refusing anything but IDs.
|
|
487
|
+
|
|
488
|
+
Field **names** only ever reach a refusal: a non-string or blank item is
|
|
489
|
+
refused rather than dropped, because a dropped item is a reference the
|
|
490
|
+
operator would approve without having seen it.
|
|
491
|
+
"""
|
|
492
|
+
|
|
474
493
|
references = {}
|
|
475
494
|
for field, kind in PLAN_REFERENCE_FIELDS.items():
|
|
476
495
|
value = agent.get(field)
|
|
477
|
-
if
|
|
496
|
+
if value is None:
|
|
497
|
+
continue
|
|
498
|
+
if isinstance(value, str):
|
|
499
|
+
if not value.strip():
|
|
500
|
+
raise PlanReviewRefused(
|
|
501
|
+
"validation", f"Creation plan reference '{field}' is blank; it is not a catalog ID. Refused."
|
|
502
|
+
)
|
|
478
503
|
references[kind] = [value]
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
504
|
+
continue
|
|
505
|
+
if not isinstance(value, list) or not all(isinstance(item, str) and item.strip() for item in value):
|
|
506
|
+
raise PlanReviewRefused(
|
|
507
|
+
"validation",
|
|
508
|
+
f"Creation plan reference '{field}' must be a catalog ID or a list of catalog IDs; "
|
|
509
|
+
"anything else is refused by field name. No review file was written.",
|
|
510
|
+
)
|
|
511
|
+
ids = sorted(set(value))
|
|
512
|
+
if ids:
|
|
513
|
+
references[kind] = ids
|
|
483
514
|
return references
|
|
484
515
|
|
|
485
516
|
|
|
@@ -496,6 +527,13 @@ def _create_diff(plan, body: dict) -> dict:
|
|
|
496
527
|
raise PlanReviewRefused(
|
|
497
528
|
"validation", "A creation plan must carry the proposed agent definition; the server returned none."
|
|
498
529
|
)
|
|
530
|
+
unknown = sorted(set(agent) - set(PLAN_DEFINITION_FIELDS) - set(PLAN_REFERENCE_FIELDS))
|
|
531
|
+
if unknown:
|
|
532
|
+
raise PlanReviewRefused(
|
|
533
|
+
"validation",
|
|
534
|
+
f"Creation plan carries agent fields this review does not cover: {', '.join(unknown)}. "
|
|
535
|
+
"Refused by field name; no review file was written.",
|
|
536
|
+
)
|
|
499
537
|
return {
|
|
500
538
|
"definition": {name: agent[name] for name in PLAN_DEFINITION_FIELDS if name in agent},
|
|
501
539
|
"references": _references(agent),
|
|
@@ -523,9 +561,41 @@ def _plan_review(
|
|
|
523
561
|
return actual, review, plan_metadata(plan, actual, review)
|
|
524
562
|
|
|
525
563
|
|
|
564
|
+
# The only `422` details this CLI prints: static route-contract messages that
|
|
565
|
+
# name a rule and carry no field value. Everything else the server sends in a
|
|
566
|
+
# `422` detail is its own text, which can quote protected field values — the
|
|
567
|
+
# same reason `validation_errors` and blocking review findings are reported by
|
|
568
|
+
# count only. Extend this set only with a detail verified to be static.
|
|
569
|
+
PLAN_SAFE_422_DETAILS = frozenset({"a prompt is required for agent creation"})
|
|
570
|
+
|
|
571
|
+
|
|
572
|
+
def _plan_safe_detail(error: BaseException) -> str | None:
|
|
573
|
+
"""The server's own `422` detail, but only when it is a known value-free message."""
|
|
574
|
+
|
|
575
|
+
parsed = getattr(error, "parsed_body", None)
|
|
576
|
+
if not isinstance(parsed, dict):
|
|
577
|
+
return None
|
|
578
|
+
for key in ("detail", "error", "message"):
|
|
579
|
+
value = parsed.get(key)
|
|
580
|
+
if isinstance(value, str) and value.strip():
|
|
581
|
+
detail = " ".join(value.split())
|
|
582
|
+
return detail if detail in PLAN_SAFE_422_DETAILS else None
|
|
583
|
+
return None
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def _plan_not_done(kind: str) -> str:
|
|
587
|
+
"""What did not happen, worded for the operation that was refused."""
|
|
588
|
+
|
|
589
|
+
if kind == "generate":
|
|
590
|
+
return "No plan was generated."
|
|
591
|
+
return "No plan was reloaded; nothing was reviewed."
|
|
592
|
+
|
|
593
|
+
|
|
526
594
|
def _plan_write_refusal(
|
|
527
|
-
error: BaseException, *, session_id: str | None, plan_id: str | None
|
|
595
|
+
error: BaseException, *, kind: str, session_id: str | None, plan_id: str | None
|
|
528
596
|
) -> PlanReviewRefused | None:
|
|
597
|
+
"""Map one plan-route HTTP error for `generate` (`kind="generate"`) or `get`."""
|
|
598
|
+
|
|
529
599
|
status = getattr(error, "status_code", None)
|
|
530
600
|
if status == 404:
|
|
531
601
|
if plan_id is not None:
|
|
@@ -536,14 +606,20 @@ def _plan_write_refusal(
|
|
|
536
606
|
"not_found", f"Session '{session_id}' was not found in this tenant; no plan was generated."
|
|
537
607
|
)
|
|
538
608
|
if status == 422:
|
|
609
|
+
detail = _plan_safe_detail(error)
|
|
610
|
+
if detail is not None:
|
|
611
|
+
return PlanReviewRefused(
|
|
612
|
+
"validation", f"The plan request was refused by the server (422): {detail}. {_plan_not_done(kind)}"
|
|
613
|
+
)
|
|
539
614
|
return PlanReviewRefused(
|
|
540
615
|
"validation",
|
|
541
|
-
|
|
616
|
+
"The plan request was refused by the server (422). Its detail can quote protected field values, "
|
|
617
|
+
f"so it is not printed; review it in the AMS console. {_plan_not_done(kind)}",
|
|
542
618
|
)
|
|
543
619
|
if status == 503:
|
|
544
620
|
return PlanReviewRefused(
|
|
545
621
|
"transport",
|
|
546
|
-
"The plan service is unavailable (the server answered 503);
|
|
622
|
+
f"The plan service is unavailable (the server answered 503); {_plan_not_done(kind)} Retry later.",
|
|
547
623
|
)
|
|
548
624
|
return None
|
|
549
625
|
|
|
@@ -1009,7 +1085,7 @@ def generate_plan(
|
|
|
1009
1085
|
except HealthCloudNetworkError:
|
|
1010
1086
|
raise PlanReviewRefused("transport", UNKNOWN_PLAN_OUTCOME) from None
|
|
1011
1087
|
except HealthCloudHTTPError as error:
|
|
1012
|
-
refusal = _plan_write_refusal(error, session_id=session_id, plan_id=None)
|
|
1088
|
+
refusal = _plan_write_refusal(error, kind="generate", session_id=session_id, plan_id=None)
|
|
1013
1089
|
if refusal is not None:
|
|
1014
1090
|
raise refusal from error
|
|
1015
1091
|
raise
|
|
@@ -1043,7 +1119,7 @@ def get_plan(dependencies: AmsDependencies, target: TenantTarget, plan_id: str,
|
|
|
1043
1119
|
"was lost. Nothing was written; retry the read when the service is reachable.",
|
|
1044
1120
|
) from None
|
|
1045
1121
|
except HealthCloudHTTPError as error:
|
|
1046
|
-
refusal = _plan_write_refusal(error, session_id=None, plan_id=plan_id)
|
|
1122
|
+
refusal = _plan_write_refusal(error, kind="get", session_id=None, plan_id=plan_id)
|
|
1047
1123
|
if refusal is not None:
|
|
1048
1124
|
raise refusal from error
|
|
1049
1125
|
raise
|
|
@@ -50,6 +50,25 @@ def attachment_projection(attachment: TuningAttachment) -> dict:
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
|
|
53
|
+
# Every review field whose value is model-generated text: the planner writes it
|
|
54
|
+
# from the operator's prompt and the session context, so it is data to inspect,
|
|
55
|
+
# never an instruction to follow. The review file carries this list beside the
|
|
56
|
+
# values it describes (an LLM01/LLM07 marker for the human approval decision).
|
|
57
|
+
MODEL_GENERATED_FIELDS = {
|
|
58
|
+
"modify": ("changes.*.why", "review_warnings[].message", "summary"),
|
|
59
|
+
"create": (
|
|
60
|
+
"definition.description",
|
|
61
|
+
"definition.greetings",
|
|
62
|
+
"definition.instructions",
|
|
63
|
+
"definition.name",
|
|
64
|
+
"definition.opening_suggestions",
|
|
65
|
+
"definition.opening_suggestions_options",
|
|
66
|
+
"review_warnings[].message",
|
|
67
|
+
"summary",
|
|
68
|
+
),
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
53
72
|
def plan_review(target: TenantTarget, plan: TuningPlan, operation: str, diff: dict) -> dict:
|
|
54
73
|
"""The owner-only review artifact for one exact plan.
|
|
55
74
|
|
|
@@ -57,10 +76,17 @@ def plan_review(target: TenantTarget, plan: TuningPlan, operation: str, diff: di
|
|
|
57
76
|
owner-only ``--out`` file and never printed: ``plan_json["original_prompt"]``
|
|
58
77
|
is not projected at all, ``warnings``/``skipped_references`` are reported as
|
|
59
78
|
counts rather than bodies, and file actions keep attachment IDs only.
|
|
79
|
+
|
|
80
|
+
The summary, the per-change ``why``, the proposed instructions/greetings and
|
|
81
|
+
the advisory findings are model-generated free text: the file marks them as
|
|
82
|
+
such with ``content_origin`` and ``model_generated_fields`` so the operator
|
|
83
|
+
approving it treats them as untrusted data, not as instructions.
|
|
60
84
|
"""
|
|
61
85
|
|
|
62
86
|
review = {
|
|
63
87
|
"schema_version": 1,
|
|
88
|
+
"content_origin": "model-generated",
|
|
89
|
+
"model_generated_fields": sorted(MODEL_GENERATED_FIELDS[operation]),
|
|
64
90
|
"target": {
|
|
65
91
|
"environment": target.environment,
|
|
66
92
|
"tenant_id": target.tenant_id,
|
|
@@ -95,6 +95,20 @@ def _user_sid() -> str:
|
|
|
95
95
|
_close(token)
|
|
96
96
|
|
|
97
97
|
|
|
98
|
+
class PrivateHandleRefused(ValueError):
|
|
99
|
+
"""A Windows handle check refused the file, with a value-free reason.
|
|
100
|
+
|
|
101
|
+
A distinct type (not a plain ``ValueError``) so a reader can report the real
|
|
102
|
+
reason — ``not_owner_only`` or ``not_regular``, the same vocabulary the POSIX
|
|
103
|
+
reader uses — instead of collapsing every refusal into "cannot be read".
|
|
104
|
+
Neither the message nor the reason ever names the path, the owner or an ACE.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, reason: str, message: str) -> None:
|
|
108
|
+
super().__init__(message)
|
|
109
|
+
self.reason = reason
|
|
110
|
+
|
|
111
|
+
|
|
98
112
|
def _require_private(handle, user_sid: str) -> None:
|
|
99
113
|
owner, acl, descriptor = _P(), _P(), _P()
|
|
100
114
|
status = _get_security(
|
|
@@ -104,7 +118,7 @@ def _require_private(handle, user_sid: str) -> None:
|
|
|
104
118
|
raise ctypes.WinError(status)
|
|
105
119
|
try:
|
|
106
120
|
if not owner or _sid_string(owner) != user_sid or not acl:
|
|
107
|
-
raise
|
|
121
|
+
raise PrivateHandleRefused("not_owner_only", "Owner-only Windows DACL required")
|
|
108
122
|
count = ctypes.cast(acl, ctypes.POINTER(_Acl)).contents.count
|
|
109
123
|
for index in range(count):
|
|
110
124
|
ace = _P()
|
|
@@ -114,7 +128,9 @@ def _require_private(handle, user_sid: str) -> None:
|
|
|
114
128
|
continue
|
|
115
129
|
# Only ordinary ACCESS_ALLOWED_ACE grants to the current user are accepted.
|
|
116
130
|
if kind != 0 or _sid_string(ace.value + 8) != user_sid:
|
|
117
|
-
raise
|
|
131
|
+
raise PrivateHandleRefused(
|
|
132
|
+
"not_owner_only", "Private Windows files must not grant access to other principals"
|
|
133
|
+
)
|
|
118
134
|
finally:
|
|
119
135
|
_free(descriptor)
|
|
120
136
|
|
|
@@ -195,7 +211,7 @@ def open_file(path: Path, *, private: bool = False, create: bool = False) -> int
|
|
|
195
211
|
info = (w.DWORD * 2)() # FILE_ATTRIBUTE_TAG_INFO
|
|
196
212
|
_check(_file_info(handle, 9, info, ctypes.sizeof(info)))
|
|
197
213
|
if _file_type(handle) != 1 or info[0] & (0x10 | 0x400): # Disk files only; no directories or reparse points.
|
|
198
|
-
raise
|
|
214
|
+
raise PrivateHandleRefused("not_regular", "Regular non-reparse Windows file required")
|
|
199
215
|
if user is not None:
|
|
200
216
|
_require_private(handle, user)
|
|
201
217
|
fd = msvcrt.open_osfhandle(handle, (os.O_WRONLY if create else os.O_RDONLY) | os.O_BINARY)
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "healthcloud-cli"
|
|
7
|
-
version = "3.7.
|
|
7
|
+
version = "3.7.8"
|
|
8
8
|
description = "Command-line interface for the HealthCloud MCP gateway, generated from the shared MCP tool manifest."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.14"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/__init__.py
RENAMED
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/commands.py
RENAMED
|
File without changes
|
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/operations.py
RENAMED
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/data_sources/projections.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/departments/operations.py
RENAMED
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/departments/projections.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/guardrails/operations.py
RENAMED
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/guardrails/projections.py
RENAMED
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/__init__.py
RENAMED
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/commands.py
RENAMED
|
File without changes
|
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/operations.py
RENAMED
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/ams/organization/projections.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{healthcloud_cli-3.7.7 → healthcloud_cli-3.7.8}/healthcloud_cli/contracts/mcp-tools.manifest.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|