cinna-cli 0.5.0__tar.gz → 0.5.2__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.
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/PKG-INFO +20 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/README.md +19 -1
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/README.md +3 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/account_workspace/account_workspace.md +6 -1
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/account_workspace/account_workspace_tech.md +20 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_api/agent_api.md +82 -3
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_api/agent_api_acceptance.md +112 -1
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_api/agent_api_tech.md +117 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/delegation/delegation.md +5 -3
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/delegation/delegation_tech.md +3 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/git_versioning/git_versioning.md +26 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/git_versioning/git_versioning_acceptance.md +29 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/git_versioning/git_versioning_tech.md +26 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/local_agent_import/local_agent_import.md +4 -0
- cinna_cli-0.5.2/docs/features/team_library/team_library.md +128 -0
- cinna_cli-0.5.2/docs/features/team_library/team_library_acceptance.md +136 -0
- cinna_cli-0.5.2/docs/features/team_library/team_library_tech.md +129 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/pyproject.toml +1 -1
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/account.py +509 -13
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/client.py +202 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/delegation.py +11 -5
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/git_versioning.py +178 -11
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/kit_contract.py +1 -1
- cinna_cli-0.5.2/src/cinna/library.py +1203 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/local_import.py +66 -3
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/main.py +301 -10
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +26 -2
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/templates/GIT_VERSIONING.md +7 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_account.py +282 -2
- cinna_cli-0.5.2/tests/test_agent_api_access.py +512 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_client.py +132 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_delegation.py +10 -0
- cinna_cli-0.5.2/tests/test_library.py +743 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_local_import.py +101 -1
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/uv.lock +1 -1
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/.claude/commands/cinna-cli.feature.doc.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/.github/workflows/publish.yml +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/.gitignore +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/LICENSE.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/account_workspace/account_workspace_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_addons/agent_addons.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_addons/agent_addons_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_addons/agent_addons_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_management/agent_management.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_management/agent_management_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_management/agent_management_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_schedules/agent_schedules.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_schedules/agent_schedules_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/agent_schedules/agent_schedules_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/bootstrap_onboarding/bootstrap_onboarding.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/bootstrap_onboarding/bootstrap_onboarding_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/delegation/delegation_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/doctor/doctor.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/doctor/doctor_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/doctor/doctor_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/improvement_requests/improvement_requests.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/improvement_requests/improvement_requests_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/improvement_requests/improvement_requests_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/live_sync/live_sync.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/live_sync/live_sync_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/live_sync/live_sync_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/local_agent_import/local_agent_import_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/local_agent_import/local_agent_import_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/mcp_integration/mcp_integration.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/mcp_integration/mcp_integration_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/mcp_integration/mcp_integration_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/remote_chat/remote_chat.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/remote_chat/remote_chat_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/remote_chat/remote_chat_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/remote_exec/remote_exec.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/remote_exec/remote_exec_acceptance.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/remote_exec/remote_exec_tech.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/interface.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/mutagen_capabilities.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/scripts/check_docs_references.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/__init__.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/auth.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/bootstrap.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/chat.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/cli_version.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/config.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/console.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/context.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/doctor.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/errors.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/improve.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/logging.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/mcp_proxy.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/mutagen_runtime.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/scenarios.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/sync.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/sync_session.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/sync_ssh_shim.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/sync_tui.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/templates/CHAT_TESTING.md +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/templates/CLAUDE.md.template +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/src/cinna/templates/__init__.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/__init__.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/conftest.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_auth.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_bootstrap.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_chat.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_cli_version.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_config.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_console.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_context.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_doctor.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_git_versioning.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_improve.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_kit_contract.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_main.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_mutagen_runtime.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_onboarding.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_scenarios.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_sync.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_sync_session.py +0 -0
- {cinna_cli-0.5.0 → cinna_cli-0.5.2}/tests/test_sync_ssh_shim.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: cinna-cli
|
|
3
|
-
Version: 0.5.
|
|
3
|
+
Version: 0.5.2
|
|
4
4
|
Summary: Local development CLI for Cinna Core agents
|
|
5
5
|
Project-URL: Homepage, https://github.com/opencinna/cinna-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/opencinna/cinna-cli
|
|
@@ -486,6 +486,24 @@ Take a package out of the catalog. Agents that already installed it keep what th
|
|
|
486
486
|
|
|
487
487
|
Put a delisted package back. `delist` has no inverse route of its own — without this verb the only way back would be `cinna api`, which would make delisting the one door in this group that opens only outwards.
|
|
488
488
|
|
|
489
|
+
### `cinna agent-api access list | issue | revoke | scopes`
|
|
490
|
+
|
|
491
|
+
Give people (or your own local agent) API access to a producer, from the account workspace. A key is issued to a **person** and lands in their credentials; **its value never appears in the CLI** — not on self-issue, not in `--json`. Reveal and rotate live on the web credential page, and so does the external-access switch (when it is off, `issue` is refused with the producer's Integrations URL). Writes need the `agent-developer` role.
|
|
492
|
+
|
|
493
|
+
- `list <producer> [--all] [--json]` — people, their scopes and keys (prefix, status, expiry, last used, linked agents, id). Ended keys (revoked, expired, removed) are hidden unless `--all`; `--json` is always the full listing.
|
|
494
|
+
- `issue <producer> --to <email|me> [--scopes a,b] [--expires-in-days N] [--service-uri SLOT] [--label TEXT] [--read-only] [--json]` — `--to` is required. `me` puts the key in your own credentials and prints the web URL where you reveal it; a local agent in Cinna Desktop calls it with `cinna_credentials.agent_api_session("<slot>")`, a cloud agent is wired with `cinna connect agent-api` instead.
|
|
495
|
+
- `revoke <producer> <key> [--yes] [--json]` — `<key>` is the key id or the exact token prefix (an elided one is refused). Asks first; `--json` needs `--yes`; an already revoked key is reported, exit 0. The person's scopes stay.
|
|
496
|
+
- `scopes <producer> --to <email|me> (--set a,b | --clear) [--json]` — change a grant without touching keys.
|
|
497
|
+
|
|
498
|
+
```bash
|
|
499
|
+
cinna agent-api access issue orders-agent --to me --service-uri orders-api
|
|
500
|
+
cinna agent-api access issue orders-agent --to ana@example.com --scopes read --expires-in-days 30
|
|
501
|
+
cinna agent-api access list orders-agent --all
|
|
502
|
+
cinna agent-api access revoke orders-agent a1b2c3d4
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
`cinna account credentials list` shows an issued key's state in its Status column, and a skill slot filled by a revoked or expired key reads as not usable. `cinna agent import` does not draft `agent_api` credentials; it prints the `access issue` / `connect agent-api` command instead.
|
|
506
|
+
|
|
489
507
|
### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
|
|
490
508
|
|
|
491
509
|
Wire one agent to another's REST API from the account workspace. Resolves both agents (name, slug, or ID), mints a producer API token, and attaches it to the consumer as a credential — which rides the consumer's normal credential sync into its remote environment, so no key ever touches your machine. Prints the credential ID, token prefix, base URL, and spec URL. `--read-only` restricts the consumer to read-only access; `--label` names the credential. Backend errors surface verbatim: 400 if the producer's REST API is disabled, 403/404 for ownership violations.
|
|
@@ -508,7 +526,7 @@ Generic escape hatch into the platform API, authenticated with the account token
|
|
|
508
526
|
|
|
509
527
|
- `--json '<obj>'` or `--data @file.json` supply a JSON request body (mutually exclusive); repeatable `--query k=v` supplies query parameters (repeating a key builds a list).
|
|
510
528
|
- The inner response is passed through verbatim: the body prints to stdout (pretty-printed for JSON) and the exit code is `0` for 2xx and `1` for an inner 4xx/5xx — so it composes in shell pipelines.
|
|
511
|
-
- When the escape hatch itself refuses the call, the detail prints to stderr and the exit code is `2`: policy denials (credentials, user management, admin, CLI, MFA/auth, and streaming routes are excluded — shown as `blocked by platform policy: …`), rate limiting (429, with the Retry-After delay), and request/response size caps (413/502).
|
|
529
|
+
- When the escape hatch itself refuses the call, the detail prints to stderr and the exit code is `2`: policy denials (credentials, user management, admin, CLI, MFA/auth, and streaming routes are excluded, as are routes that would hand out a secret: any method on `agents/*/agent-api/keys…` (issued keys), on `environments/*/workspace/download` and `view-file`, and on `dashboards/*/blocks/*/env-file`; and POST on A2A access-tokens, MCP/ACP connector tokens, agent and task-trigger webhook create/regenerate-token. Paths are matched after percent-decoding. — shown as `blocked by platform policy: …`), rate limiting (429, with the Retry-After delay), and request/response size caps (413/502).
|
|
512
530
|
|
|
513
531
|
`<path>` accepts the same **agent references** the rest of the CLI does: a segment straight after `agents/` that is not already a UUID is resolved against the account's agent listing, and the substitution is announced on stderr so a `--json` stdout stream stays pure. Resolution is sugar and never breaks the hatch — a reference that does not resolve (or an agent listing that cannot be reached) leaves the path exactly as typed, because `agents/` is a route prefix as well as a collection.
|
|
514
532
|
|
|
@@ -449,6 +449,24 @@ Take a package out of the catalog. Agents that already installed it keep what th
|
|
|
449
449
|
|
|
450
450
|
Put a delisted package back. `delist` has no inverse route of its own — without this verb the only way back would be `cinna api`, which would make delisting the one door in this group that opens only outwards.
|
|
451
451
|
|
|
452
|
+
### `cinna agent-api access list | issue | revoke | scopes`
|
|
453
|
+
|
|
454
|
+
Give people (or your own local agent) API access to a producer, from the account workspace. A key is issued to a **person** and lands in their credentials; **its value never appears in the CLI** — not on self-issue, not in `--json`. Reveal and rotate live on the web credential page, and so does the external-access switch (when it is off, `issue` is refused with the producer's Integrations URL). Writes need the `agent-developer` role.
|
|
455
|
+
|
|
456
|
+
- `list <producer> [--all] [--json]` — people, their scopes and keys (prefix, status, expiry, last used, linked agents, id). Ended keys (revoked, expired, removed) are hidden unless `--all`; `--json` is always the full listing.
|
|
457
|
+
- `issue <producer> --to <email|me> [--scopes a,b] [--expires-in-days N] [--service-uri SLOT] [--label TEXT] [--read-only] [--json]` — `--to` is required. `me` puts the key in your own credentials and prints the web URL where you reveal it; a local agent in Cinna Desktop calls it with `cinna_credentials.agent_api_session("<slot>")`, a cloud agent is wired with `cinna connect agent-api` instead.
|
|
458
|
+
- `revoke <producer> <key> [--yes] [--json]` — `<key>` is the key id or the exact token prefix (an elided one is refused). Asks first; `--json` needs `--yes`; an already revoked key is reported, exit 0. The person's scopes stay.
|
|
459
|
+
- `scopes <producer> --to <email|me> (--set a,b | --clear) [--json]` — change a grant without touching keys.
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
cinna agent-api access issue orders-agent --to me --service-uri orders-api
|
|
463
|
+
cinna agent-api access issue orders-agent --to ana@example.com --scopes read --expires-in-days 30
|
|
464
|
+
cinna agent-api access list orders-agent --all
|
|
465
|
+
cinna agent-api access revoke orders-agent a1b2c3d4
|
|
466
|
+
```
|
|
467
|
+
|
|
468
|
+
`cinna account credentials list` shows an issued key's state in its Status column, and a skill slot filled by a revoked or expired key reads as not usable. `cinna agent import` does not draft `agent_api` credentials; it prints the `access issue` / `connect agent-api` command instead.
|
|
469
|
+
|
|
452
470
|
### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
|
|
453
471
|
|
|
454
472
|
Wire one agent to another's REST API from the account workspace. Resolves both agents (name, slug, or ID), mints a producer API token, and attaches it to the consumer as a credential — which rides the consumer's normal credential sync into its remote environment, so no key ever touches your machine. Prints the credential ID, token prefix, base URL, and spec URL. `--read-only` restricts the consumer to read-only access; `--label` names the credential. Backend errors surface verbatim: 400 if the producer's REST API is disabled, 403/404 for ownership violations.
|
|
@@ -471,7 +489,7 @@ Generic escape hatch into the platform API, authenticated with the account token
|
|
|
471
489
|
|
|
472
490
|
- `--json '<obj>'` or `--data @file.json` supply a JSON request body (mutually exclusive); repeatable `--query k=v` supplies query parameters (repeating a key builds a list).
|
|
473
491
|
- The inner response is passed through verbatim: the body prints to stdout (pretty-printed for JSON) and the exit code is `0` for 2xx and `1` for an inner 4xx/5xx — so it composes in shell pipelines.
|
|
474
|
-
- When the escape hatch itself refuses the call, the detail prints to stderr and the exit code is `2`: policy denials (credentials, user management, admin, CLI, MFA/auth, and streaming routes are excluded — shown as `blocked by platform policy: …`), rate limiting (429, with the Retry-After delay), and request/response size caps (413/502).
|
|
492
|
+
- When the escape hatch itself refuses the call, the detail prints to stderr and the exit code is `2`: policy denials (credentials, user management, admin, CLI, MFA/auth, and streaming routes are excluded, as are routes that would hand out a secret: any method on `agents/*/agent-api/keys…` (issued keys), on `environments/*/workspace/download` and `view-file`, and on `dashboards/*/blocks/*/env-file`; and POST on A2A access-tokens, MCP/ACP connector tokens, agent and task-trigger webhook create/regenerate-token. Paths are matched after percent-decoding. — shown as `blocked by platform policy: …`), rate limiting (429, with the Retry-After delay), and request/response size caps (413/502).
|
|
475
493
|
|
|
476
494
|
`<path>` accepts the same **agent references** the rest of the CLI does: a segment straight after `agents/` that is not already a UUID is resolved against the account's agent listing, and the substitution is announced on stderr so a `--json` stdout stream stays pure. Resolution is sugar and never breaks the hatch — a reference that does not resolve (or an agent listing that cannot be reached) leaves the path exactly as typed, because `agents/` is a route prefix as well as a collection.
|
|
477
495
|
|
|
@@ -292,13 +292,14 @@ authoring convention.
|
|
|
292
292
|
| **Live sync** | `cinna sync` (status, conflicts `[--diff]`, push, pull, resolve) + Mutagen transport; the session is started by `cinna agent sync` so the first push has a baseline | [business](features/live_sync/live_sync.md) · [tech](features/live_sync/live_sync_tech.md) · [acceptance](features/live_sync/live_sync_acceptance.md) |
|
|
293
293
|
| **Remote exec** | `cinna exec` | [business](features/remote_exec/remote_exec.md) · [tech](features/remote_exec/remote_exec_tech.md) · [acceptance](features/remote_exec/remote_exec_acceptance.md) |
|
|
294
294
|
| **Remote chat** | `cinna chat` (incl. `--attach` / `--show` to recover or read an existing session; poll failures retried within `--timeout`) · `cinna agent scenarios list / run` (replay `docs/test_scenarios/` Say rows, one session per row) | <!-- nocheck: agent-workspace path --> [business](features/remote_chat/remote_chat.md) · [tech](features/remote_chat/remote_chat_tech.md) · [acceptance](features/remote_chat/remote_chat_acceptance.md) |
|
|
295
|
-
| **Agent API** | `cinna agent-api` (enable, refresh, spec, call) · `cinna api` · `cinna connect agent-api` | [business](features/agent_api/agent_api.md) · [tech](features/agent_api/agent_api_tech.md) · [acceptance](features/agent_api/agent_api_acceptance.md) |
|
|
295
|
+
| **Agent API** | `cinna agent-api` (enable, refresh, spec, call, access list/issue/revoke/scopes) · `cinna api` · `cinna connect agent-api` | [business](features/agent_api/agent_api.md) · [tech](features/agent_api/agent_api_tech.md) · [acceptance](features/agent_api/agent_api_acceptance.md) |
|
|
296
296
|
| **Agent addons** | `cinna skills` (list — with each declared credential slot's readiness, publish, install, uninstall, update, toggle, refresh, catalog, show, revisions, files, grants, grant, revoke, visibility, delist, relist) — the whole skills lifecycle: what an agent carries, publishing one of its `skills/<name>/` folders to the catalog, installing a package on another agent and keeping it current, and sharing after the fact | [business](features/agent_addons/agent_addons.md) · [tech](features/agent_addons/agent_addons_tech.md) · [acceptance](features/agent_addons/agent_addons_acceptance.md) |
|
|
297
297
|
| **MCP integration** | `cinna connect mcp` · `cinna mcp-proxy` (knowledge stdio server) | [business](features/mcp_integration/mcp_integration.md) · [tech](features/mcp_integration/mcp_integration_tech.md) · [acceptance](features/mcp_integration/mcp_integration_acceptance.md) |
|
|
298
|
-
| **Git versioning** | `cinna git` (link, status, commit, push, pull, log, checkout, unlink) | [business](features/git_versioning/git_versioning.md) · [tech](features/git_versioning/git_versioning_tech.md) · [acceptance](features/git_versioning/git_versioning_acceptance.md) |
|
|
298
|
+
| **Git versioning** | `cinna git` (link, status, commit, push, pull, log, checkout, unlink) — external remotes with your own credentials, `cinna` remotes with the CLI token | [business](features/git_versioning/git_versioning.md) · [tech](features/git_versioning/git_versioning_tech.md) · [acceptance](features/git_versioning/git_versioning_acceptance.md) |
|
|
299
299
|
| **Durable delegation** | `cinna delegation` (create — idempotent per target + `--id`, status, report — owner side with a task id or from inside a cloud task, reply), gated on the backend capability version | [business](features/delegation/delegation.md) · [tech](features/delegation/delegation_tech.md) · [acceptance](features/delegation/delegation_acceptance.md) |
|
|
300
300
|
| **Improvement requests** | `cinna improve` (list, show, download, status) | [business](features/improvement_requests/improvement_requests.md) · [tech](features/improvement_requests/improvement_requests_tech.md) · [acceptance](features/improvement_requests/improvement_requests_acceptance.md) |
|
|
301
301
|
| **Local agent import** | `cinna agent import` (Local Agent Kit → cloud agent), incl. the versioned folder contract (`.cinna-kit/layout.json`), `publications.json` and `content_hash` | [business](features/local_agent_import/local_agent_import.md) · [tech](features/local_agent_import/local_agent_import_tech.md) · [acceptance](features/local_agent_import/local_agent_import_acceptance.md) |
|
|
302
|
+
| **Team library** | `cinna library` (list, get, share, update, status, grants, suggest) — kit folders in `Local/<slug>/` shared through cinna-core-hosted git with the account token; `cinna agent import --from-library` | [business](features/team_library/team_library.md) · [tech](features/team_library/team_library_tech.md) · [acceptance](features/team_library/team_library_acceptance.md) |
|
|
302
303
|
|
|
303
304
|
The sections below (Git Versioning, Sync Transport, Remote Exec, Remote Chat,
|
|
304
305
|
Bootstrap Flow) remain as in-README quick references and backend contracts; the
|
|
@@ -156,7 +156,12 @@ each with its own token, registry entry, and Mutagen session.
|
|
|
156
156
|
step; `--workspace` overrides the target workspace.
|
|
157
157
|
- `cinna account credentials list` shows credentials with their type, **slot**,
|
|
158
158
|
setup status (complete / needs setup, plus a placeholder marker) and full id —
|
|
159
|
-
metadata only.
|
|
159
|
+
metadata only. For an issued Agent API key the Status cell shows the key's
|
|
160
|
+
state instead (Active; API turned off by producer / External access paused;
|
|
161
|
+
Revoked by issuer / Producer removed / Issuer account removed / Expired), and
|
|
162
|
+
`agent show` appends it when not active. A draft with `--type agent_api` is
|
|
163
|
+
refused by the platform: such a credential has no key behind it; use
|
|
164
|
+
`cinna agent-api access issue` (see [agent_api](../agent_api/agent_api.md)). The slot is the credential's service URI: the non-secret id a
|
|
160
165
|
skill's `credentials:` block names and a script looks the credential up by, so
|
|
161
166
|
it is the column that answers "which credential fills this skill's slot". The
|
|
162
167
|
hint under the table names `cinna account credentials update <id> --service-uri
|
{cinna_cli-0.5.0 → cinna_cli-0.5.2}/docs/features/account_workspace/account_workspace_tech.md
RENAMED
|
@@ -216,6 +216,26 @@ Related (documented here as integration points): `cinna agent sync` →
|
|
|
216
216
|
- `src/cinna/account.py:run_credentials_update()` — refuses an empty update;
|
|
217
217
|
metadata fields only (`name`/`notes`/`service_uri`/`allow_sharing`).
|
|
218
218
|
- `src/cinna/account.py:_credential_status_cell()` — complete / needs setup / —.
|
|
219
|
+
- `src/cinna/account.py:_key_status_cell()` / `_KEY_STATUS_LABELS` — an issued
|
|
220
|
+
key's status as a coloured label; `run_credentials_list()` uses it in place of
|
|
221
|
+
the complete / needs-setup cell when `agent_api_key_status` is set, and
|
|
222
|
+
`_credential_summary()` appends it when not `active`.
|
|
223
|
+
`agent_api_key_status` is in `_ACCOUNT_AUTHORITATIVE_FIELDS`, so linked-credential
|
|
224
|
+
overlays carry it. (`tests/test_account.py`)
|
|
225
|
+
- Skill slot readiness: `src/cinna/account.py:_slot_readiness()` treats a filled
|
|
226
|
+
carrier whose key status is not `active` as unusable and returns
|
|
227
|
+
`key_revoked` (revoked_by_issuer / producer_removed / issuer_removed),
|
|
228
|
+
`key_expired`, `key_paused` (api_disabled / external_access_paused) or
|
|
229
|
+
`key_unavailable` (a status this CLI does not know), via
|
|
230
|
+
`_key_unavailable_state()` / `_dead_key_carrier()` (every filled carrier of the
|
|
231
|
+
slot, of the declared type, holds a dead or paused key). A newer platform
|
|
232
|
+
flags such a slot itself as `access_revoked`; the CLI refines that reason to the
|
|
233
|
+
key state so the remedy fits. An older platform names no issue for catalog rows,
|
|
234
|
+
so the CLI checks key status there too. `_needs_linked_credentials()` fetches
|
|
235
|
+
the linked credentials for catalog rows with an `access_revoked` issue or an
|
|
236
|
+
unflagged declared slot. `_slot_remedy()` words the fix: ask the producer's owner
|
|
237
|
+
for a new key (`agent_api_issue_hint()`) for revoked/expired, "recovers by
|
|
238
|
+
itself" for paused, upgrade the CLI for unknown.
|
|
219
239
|
- `src/cinna/account.py:run_credentials_list()` — `--json` echoes the listing
|
|
220
240
|
untouched; otherwise the table adds a `Slot` column (`_credential_slot_cell()`,
|
|
221
241
|
the escaped `service_uri` or a dim dash), marks `is_placeholder`, never wraps the
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
Let an agent **expose its own HTTP/OpenAPI surface** (a "producer" REST API)
|
|
6
6
|
that another agent (a "consumer") can call, and give a coding agent the local
|
|
7
7
|
loop to build, verify, and wire those connections from the account workspace:
|
|
8
|
-
`cinna agent-api` (enable / refresh / spec / call) drives the
|
|
8
|
+
`cinna agent-api` (enable / refresh / spec / call / access) drives the
|
|
9
9
|
**build→verify** loop on a producer; `cinna connect agent-api` does the
|
|
10
10
|
**one-click wiring** of a consumer to it; and `cinna api` is a separate
|
|
11
11
|
**escape hatch** for calling the platform's *own* control-plane API directly.
|
|
@@ -67,7 +67,8 @@ to test a producer endpoint — use `cinna agent-api call`.
|
|
|
67
67
|
consumer authenticates its real calls with it.
|
|
68
68
|
- **Escape hatch** — the account `api-proxy` route `cinna api` rides: a buffered
|
|
69
69
|
JSON pass-through to the platform's own API, with excluded categories
|
|
70
|
-
(credentials, user management, admin, CLI, MFA/auth, streaming
|
|
70
|
+
(credentials, user management, admin, CLI, MFA/auth, streaming, and the
|
|
71
|
+
secret-minting routes listed under "Escape-hatch deny-list") denied.
|
|
71
72
|
|
|
72
73
|
## User flows
|
|
73
74
|
|
|
@@ -97,6 +98,35 @@ to test a producer endpoint — use `cinna agent-api call`.
|
|
|
97
98
|
appears read-only under `workspace/credentials/` in a synced workspace) —
|
|
98
99
|
no manual key handling.
|
|
99
100
|
|
|
101
|
+
### Give API access (a person, or a local agent)
|
|
102
|
+
- `cinna agent-api access list <producer>` shows who has access: one row per
|
|
103
|
+
person with their scopes and keys (prefix, status, expiry, last use, how many
|
|
104
|
+
agents use it, key id). `--all` also shows ended keys; `--json` is always the
|
|
105
|
+
full listing. The footer says whether external access is on and which scopes
|
|
106
|
+
the producer declares.
|
|
107
|
+
- `cinna agent-api access issue <producer> --to <email|me>` issues a key to a
|
|
108
|
+
person. `--scopes a,b` sets their scopes (`""` = an explicit empty grant),
|
|
109
|
+
`--expires-in-days N` (1-3650), `--service-uri SLOT` names the credential's
|
|
110
|
+
slot (default `<producer>-api`), `--label`, `--read-only`.
|
|
111
|
+
- `--to me`: the key lands in your own credentials; the output names the slot
|
|
112
|
+
and the web credential page where you reveal the value. A local agent
|
|
113
|
+
(Cinna Desktop) then calls it by slot; a cloud agent is wired with
|
|
114
|
+
`cinna connect agent-api` instead.
|
|
115
|
+
- `--to <email>`: the key lands in that person's credentials; you never see
|
|
116
|
+
the value. The output prints the revoke command.
|
|
117
|
+
- Scopes outside the producer's declared catalog are stored anyway, with a
|
|
118
|
+
warning.
|
|
119
|
+
- `cinna agent-api access revoke <producer> <key>` ends one key (asks first,
|
|
120
|
+
`--yes` skips). The person's scopes stay, so a new key can be issued without
|
|
121
|
+
re-granting.
|
|
122
|
+
- `cinna agent-api access scopes <producer> --to <email|me> --set a,b | --clear`
|
|
123
|
+
changes a person's scopes without touching their keys, so a working key keeps
|
|
124
|
+
working. `--clear` removes the grant.
|
|
125
|
+
- The result shows up elsewhere: `cinna account credentials list` shows the
|
|
126
|
+
key's status in the Status column, and a skill slot filled by a dead key
|
|
127
|
+
reads as not usable (see
|
|
128
|
+
[account_workspace](../account_workspace/account_workspace.md)).
|
|
129
|
+
|
|
100
130
|
### Call the platform's own API (unrelated to producer APIs)
|
|
101
131
|
- `cinna api GET agents`, `cinna api POST agents/<id>/duplicate`,
|
|
102
132
|
`cinna api PATCH agents/<id> --json '{…}'` — `PATH` is relative to the API
|
|
@@ -132,7 +162,8 @@ to test a producer endpoint — use `cinna agent-api call`.
|
|
|
132
162
|
its own refusal from a mirrored inner error by a marker response header.
|
|
133
163
|
- **Escape-hatch deny-list.** `cinna api` cannot reach credentials, user
|
|
134
164
|
management, admin, CLI, MFA/auth, or streaming routes — the platform denies
|
|
135
|
-
them; don't waste calls.
|
|
165
|
+
them; don't waste calls. It also refuses routes that would hand out a secret:
|
|
166
|
+
any method on `agents/*/agent-api/keys…` (issued keys), on `environments/*/workspace/download` and `view-file`, and on `dashboards/*/blocks/*/env-file`; and POST on A2A access-tokens, MCP/ACP connector tokens, agent and task-trigger webhook create/regenerate-token. Paths are matched after percent-decoding. These refuse with 403 `excluded_path` (exit 2). Prefix-only listings stay reachable.
|
|
136
167
|
- **An elided id is refused locally.** A path segment carrying `…` or `...` is
|
|
137
168
|
an id copied out of a table cell too narrow to print it whole. It reaches the
|
|
138
169
|
API as a well-formed path and comes back `404 Agent not found` — a sentence
|
|
@@ -150,6 +181,37 @@ to test a producer endpoint — use `cinna agent-api call`.
|
|
|
150
181
|
- **No secret round-trip.** `connect agent-api` mints the producer token
|
|
151
182
|
server-side and attaches it as a consumer credential; the secret value never
|
|
152
183
|
passes through the CLI as something the user pastes or stores locally.
|
|
184
|
+
- **A key value never crosses the CLI.** The platform sends none on the access
|
|
185
|
+
routes, the client drops any stray `token` field, and every runner drops it
|
|
186
|
+
again before printing or `--json`. Self-issue returns a web URL where the
|
|
187
|
+
person reveals the value; nobody should paste one into the CLI.
|
|
188
|
+
- **Rotate and reveal are web-only.** Rotation replaces a value someone outside
|
|
189
|
+
the platform holds, which the CLI could not hand over, and a coding agent
|
|
190
|
+
could otherwise start a countdown that breaks every holder of the old value.
|
|
191
|
+
There is no verb for either.
|
|
192
|
+
- **The external-access switch is web-only.** It is the deliberate human act
|
|
193
|
+
that exposes the API outside the platform. When it is off, `issue` is refused
|
|
194
|
+
with `external_access_disabled` and the producer's Integrations URL is
|
|
195
|
+
appended to the message (only when the platform's message lacks one); `list`
|
|
196
|
+
warns and prints the same URL.
|
|
197
|
+
- **Terminal keys are hidden unless `--all`.** Revoked, producer/issuer removed
|
|
198
|
+
and expired keys drop out of the `access list` table with a footer count;
|
|
199
|
+
`--json` always carries everything.
|
|
200
|
+
- **`--to` is required on `issue` and `scopes`.** The recipient is always a
|
|
201
|
+
deliberate choice: an email (resolved server-side, exact case-insensitive
|
|
202
|
+
match among active users) or `me`. An unknown email is refused, and one that
|
|
203
|
+
matches several accounts is refused as ambiguous rather than guessed.
|
|
204
|
+
- **A key reference is a key id or the exact token prefix.** A reference
|
|
205
|
+
carrying an elision marker is refused locally (same rule as other ids); no
|
|
206
|
+
match or several matches fail with a candidate listing. Revoking an
|
|
207
|
+
already-revoked key is a no-op that exits 0 ("already revoked"); `--json`
|
|
208
|
+
requires `--yes`.
|
|
209
|
+
- **Only the producer's owner (or an admin) manages access, and writes need
|
|
210
|
+
the developer role.** A producer that is missing or not yours reads as not
|
|
211
|
+
found. `list` is open to any account token.
|
|
212
|
+
- **The generic escape hatch cannot issue keys.** `cinna api POST
|
|
213
|
+
agents/<id>/agent-api/keys` is refused (exit 2); the `access` verbs are the
|
|
214
|
+
only CLI way.
|
|
153
215
|
- **Read-only is a connect-time choice.** `--read-only` is applied when the
|
|
154
216
|
connection is created; it scopes the *consumer's* access, not the producer's
|
|
155
217
|
surface.
|
|
@@ -169,6 +231,15 @@ build/verify (producer):
|
|
|
169
231
|
▼ producer's env)
|
|
170
232
|
agent-api call ─► owner-preview proxy ─► producer endpoint (no consumer token)
|
|
171
233
|
|
|
234
|
+
people access (issued keys and scopes):
|
|
235
|
+
cinna agent-api access list/issue/revoke/scopes
|
|
236
|
+
│ account token
|
|
237
|
+
▼
|
|
238
|
+
/api/v1/cli/account/agent-api/{access,keys,keys/revoke,access/scopes}
|
|
239
|
+
│ key lands in the recipient's credentials (value never returned)
|
|
240
|
+
▼
|
|
241
|
+
recipient reveals it on the web; a local agent calls it by slot
|
|
242
|
+
|
|
172
243
|
wiring (consumer):
|
|
173
244
|
cinna connect agent-api --producer A --consumer B
|
|
174
245
|
│ account token
|
|
@@ -195,6 +266,14 @@ platform escape hatch (unrelated target):
|
|
|
195
266
|
- **Remote sync / exec** — authoring a producer API means editing files in the
|
|
196
267
|
producer's synced `workspace/agent_api/` and pushing them to the running env
|
|
197
268
|
(`cinna dev` / `cinna exec`); the spec is harvested from that env.
|
|
269
|
+
- **Account workspace credentials and skill slots** — an issued key is a
|
|
270
|
+
credential whose listing row carries its key status; skill slot readiness
|
|
271
|
+
treats a revoked, expired or paused key as not usable. See
|
|
272
|
+
[account_workspace](../account_workspace/account_workspace.md).
|
|
273
|
+
- **Local agent import** — `cinna agent import` never drafts an `agent_api`
|
|
274
|
+
credential (it could never be used); it attaches an existing same-name one or
|
|
275
|
+
prints the `access issue` / `connect agent-api` command. See
|
|
276
|
+
[local_agent_import](../local_agent_import/local_agent_import.md).
|
|
198
277
|
- **`cinna agent show`** — surfaces a producer's live `agent_api_status`
|
|
199
278
|
alongside its prompts and credentials, reusing the same status renderer.
|
|
200
279
|
|
|
@@ -21,7 +21,7 @@ for** failure modes.
|
|
|
21
21
|
- **Editable install** of the CLI under test:
|
|
22
22
|
`python3 -c "import cinna,os;print(os.path.dirname(cinna.__file__))"` must
|
|
23
23
|
point at this repo's `src/cinna`. Confirm `which cinna` resolves and
|
|
24
|
-
`cinna agent-api --help` lists `enable / refresh / spec / call`.
|
|
24
|
+
`cinna agent-api --help` lists `enable / refresh / spec / call / access`.
|
|
25
25
|
- **A producer agent** you own whose workspace has (or will have) an
|
|
26
26
|
`agent_api/` package + `policy.yaml`. Ideally synced locally
|
|
27
27
|
(`cinna agent sync <producer>`) so you can edit + push the endpoint code.
|
|
@@ -196,6 +196,111 @@ for** failure modes.
|
|
|
196
196
|
- **Watch for:** trying to test a producer endpoint via `cinna api` (wrong
|
|
197
197
|
target — it calls platform routes, not the producer's served API).
|
|
198
198
|
|
|
199
|
+
### 13. Issue yourself a key: URL, never a value
|
|
200
|
+
|
|
201
|
+
- **Goal:** a builder gives themselves API access to a producer and never sees
|
|
202
|
+
the key value.
|
|
203
|
+
- **Setup:** the producer's REST API is enabled; turn **external access** on in
|
|
204
|
+
the web (Integrations tab of the producer). It is web-only by design.
|
|
205
|
+
- **Steps:**
|
|
206
|
+
```
|
|
207
|
+
cinna agent-api access issue "<Producer>" --to me
|
|
208
|
+
cinna account credentials list
|
|
209
|
+
cinna agent-api access list "<Producer>"
|
|
210
|
+
```
|
|
211
|
+
- **Expected:** `Key <prefix> is in your credentials as slot <producer>-api …`
|
|
212
|
+
with a `…/credential/<id>` URL and the `cinna_credentials.agent_api_session`
|
|
213
|
+
hint; the output contains no key value. `credentials list` shows the new row
|
|
214
|
+
with Status `Active`; `access list` shows you with that key as Active.
|
|
215
|
+
- **Watch for:** any full key or `token` field in the output (also with
|
|
216
|
+
`--json`); the credentials row showing `complete` instead of the key status.
|
|
217
|
+
|
|
218
|
+
### 14. External access off refuses with the URL
|
|
219
|
+
|
|
220
|
+
- **Goal:** issuing is fail-loud while external access is off.
|
|
221
|
+
- **Setup:** turn external access off in the web.
|
|
222
|
+
- **Steps:**
|
|
223
|
+
```
|
|
224
|
+
cinna agent-api access issue "<Producer>" --to me; echo "exit=$?"
|
|
225
|
+
cinna agent-api access list "<Producer>"
|
|
226
|
+
```
|
|
227
|
+
- **Expected:** non-zero exit; the message says external access is disabled and
|
|
228
|
+
ends with `…/agent/<id>#integrations` (once, not doubled). `access list`
|
|
229
|
+
prints `External access: off` and a warning with the same URL. Turn it back on
|
|
230
|
+
before scenario 15.
|
|
231
|
+
- **Watch for:** a generic error without the URL; any CLI path that switches it
|
|
232
|
+
on.
|
|
233
|
+
|
|
234
|
+
### 15. Issue to another person, scopes, and list
|
|
235
|
+
|
|
236
|
+
- **Goal:** give a colleague a scoped key without ever seeing it.
|
|
237
|
+
- **Steps:**
|
|
238
|
+
```
|
|
239
|
+
cinna agent-api access issue "<Producer>" --to <other-email> --scopes read --expires-in-days 30 --label qa
|
|
240
|
+
cinna agent-api access scopes "<Producer>" --to <other-email> --set read,write
|
|
241
|
+
cinna agent-api access list "<Producer>"
|
|
242
|
+
cinna agent-api access list "<Producer>" --json
|
|
243
|
+
cinna agent-api access issue "<Producer>" --to nobody@example.invalid; echo "exit=$?"
|
|
244
|
+
cinna agent-api access issue "<Producer>" --to nobody@example.invalid --json; echo "exit=$?"
|
|
245
|
+
```
|
|
246
|
+
- **Expected:** `Issued key <prefix> to <other-email>. It lands in their
|
|
247
|
+
credentials; you never see its value.` plus the revoke command. The scopes
|
|
248
|
+
command prints the person's resulting scopes and the key keeps working. The
|
|
249
|
+
table shows the person, scopes, key prefix, status, expiry, last used, linked
|
|
250
|
+
agent count and key id; `--json` is the full payload with no `token`
|
|
251
|
+
anywhere. The unknown recipient is refused and nothing is issued: the plain
|
|
252
|
+
run prints the server's sentence on stderr, and the `--json` run prints one
|
|
253
|
+
`{"result": "error", "code": "recipient_not_found", …}` line on stdout. Both
|
|
254
|
+
exit 1. An undeclared scope prints a warning but is stored.
|
|
255
|
+
- **Watch for:** `--to` omitted not being a usage error; a key value in any
|
|
256
|
+
output; the scopes change revoking or replacing the key.
|
|
257
|
+
|
|
258
|
+
### 16. Revoke: hidden by default, visible with `--all`, recipient sees it
|
|
259
|
+
|
|
260
|
+
- **Goal:** revoking ends one key, keeps the grant, and is visible on both sides.
|
|
261
|
+
- **Steps:**
|
|
262
|
+
```
|
|
263
|
+
cinna agent-api access revoke "<Producer>" <prefix> # answer n first, then y
|
|
264
|
+
cinna agent-api access list "<Producer>"
|
|
265
|
+
cinna agent-api access list "<Producer>" --all
|
|
266
|
+
cinna agent-api access revoke "<Producer>" <prefix> --yes # again
|
|
267
|
+
cinna agent-api access revoke "<Producer>" <prefix> --json # no --yes
|
|
268
|
+
```
|
|
269
|
+
Then check the recipient's side. A recipient who is a developer can use their
|
|
270
|
+
own account workspace:
|
|
271
|
+
```
|
|
272
|
+
cinna account credentials list
|
|
273
|
+
```
|
|
274
|
+
An agent-user cannot create an account workspace (setup tokens are
|
|
275
|
+
developer-only), so for them sign in to the web app as the recipient and open
|
|
276
|
+
**Credentials**, or call `GET /api/v1/credentials` with their token and read
|
|
277
|
+
`agent_api_key_status`.
|
|
278
|
+
- **Expected:** declining sends nothing; accepting prints `Revoked key <prefix>
|
|
279
|
+
issued to <email>. Status: Revoked by issuer`. The default list hides the key
|
|
280
|
+
with `1 ended key hidden (--all)` while the person's scopes row stays;
|
|
281
|
+
`--all` shows `Revoked by issuer`. Revoking again prints `already revoked`
|
|
282
|
+
and exits 0. `--json` without `--yes` is a usage error. The recipient still
|
|
283
|
+
has the credential, with Status `Revoked by issuer` (`agent_api_key_status:
|
|
284
|
+
revoked_by_issuer` in the API).
|
|
285
|
+
- **Watch for:** the grant disappearing with the key; an elided prefix
|
|
286
|
+
(`ab12…`) not being refused locally; the recipient's credential being
|
|
287
|
+
deleted rather than flagged.
|
|
288
|
+
|
|
289
|
+
### 17. The escape hatch cannot mint keys
|
|
290
|
+
|
|
291
|
+
- **Goal:** `cinna api` is not a back door around the no-value rule.
|
|
292
|
+
- **Steps:**
|
|
293
|
+
```
|
|
294
|
+
cinna api POST agents/<producer-id>/agent-api/keys --json '{"label":"x"}'; echo "exit=$?"
|
|
295
|
+
cinna api GET agents/<producer-id>/agent-api/access; echo "exit=$?"
|
|
296
|
+
cinna agent-api access list "<Producer>"
|
|
297
|
+
```
|
|
298
|
+
- **Expected:** the POST prints `blocked by platform policy: …` on stderr and
|
|
299
|
+
exits 2; no key was created (the list is unchanged). The `access` GET through
|
|
300
|
+
the hatch is still reachable. The same refusal (403 `excluded_path`, exit 2)
|
|
301
|
+
applies to any method on `agents/*/agent-api/keys…`, `environments/*/workspace/download` and `view-file`, `dashboards/*/blocks/*/env-file`, and POST on token-minting routes (A2A access-tokens, MCP/ACP connector tokens, webhook create/regenerate-token), including percent-encoded spellings.
|
|
302
|
+
- **Watch for:** exit 1 instead of 2; a key created by the call.
|
|
303
|
+
|
|
199
304
|
## Cross-cutting invariants (must hold across all scenarios)
|
|
200
305
|
|
|
201
306
|
- **Resolve-before-mutate.** A bad agent ref fails before any enable / refresh /
|
|
@@ -209,6 +314,8 @@ for** failure modes.
|
|
|
209
314
|
both `agent-api call` and `cinna api`; only `cinna api` adds exit 2 for the
|
|
210
315
|
hatch's own refusal (policy / rate limit / size cap), distinguished by the
|
|
211
316
|
marker header — never conflated with an inner 4xx.
|
|
317
|
+
- **No key value in any access output.** Not in the table, not in `--json`,
|
|
318
|
+
not on self-issue; reveal and rotate are web-only.
|
|
212
319
|
- **Spec is pipeable.** `cinna agent-api spec` emits plain JSON to stdout (or the
|
|
213
320
|
`-o` file), never Rich-decorated.
|
|
214
321
|
|
|
@@ -221,4 +328,8 @@ for** failure modes.
|
|
|
221
328
|
`cinna agent-api enable "<Producer>" --disable`.
|
|
222
329
|
- Remove any deliberately-broken `agent_api/` edits (revert + sync), then
|
|
223
330
|
`cinna agent-api refresh` to restore a clean harvested spec.
|
|
331
|
+
- Revoke test keys (`cinna agent-api access revoke "<Producer>" <prefix> --yes`),
|
|
332
|
+
clear test grants (`cinna agent-api access scopes "<Producer>" --to <email>
|
|
333
|
+
--clear`), delete the revoked credentials in the web, and set external access
|
|
334
|
+
back to its prior state.
|
|
224
335
|
- Delete scratch files written by `cinna agent-api spec -o spec.json`.
|
|
@@ -13,10 +13,16 @@ talks to the platform through `AccountClient` with the account token.
|
|
|
13
13
|
shared status renderer.
|
|
14
14
|
- `src/cinna/client.py` — `AccountClient` methods that hit the account routes
|
|
15
15
|
(`set_agent_api_enabled`, `refresh_agent_api`, `get_agent_api_spec`,
|
|
16
|
-
`call_agent_api`, `connect_agent_api`, `connect_mcp`, `api_proxy
|
|
16
|
+
`call_agent_api`, `connect_agent_api`, `connect_mcp`, `api_proxy`, and the
|
|
17
|
+
four access methods `list_agent_api_access`, `issue_agent_api_key`,
|
|
18
|
+
`revoke_agent_api_key`, `set_agent_api_scopes`) plus the `drop_token()` helper.
|
|
17
19
|
- Tests: `tests/test_account.py` (enable/refresh/spec/call, connect agent-api,
|
|
18
20
|
`cinna api` exit-code matrix, `api_proxy` client behavior),
|
|
19
|
-
`tests/test_client.py` (`AccountClient` route shapes
|
|
21
|
+
`tests/test_client.py` (`AccountClient` route shapes, the access methods,
|
|
22
|
+
coded refusals, `drop_token`), `tests/test_agent_api_access.py` (the four
|
|
23
|
+
`access` verbs end to end against a mocked client), `tests/test_account.py`
|
|
24
|
+
(key status cells, slot readiness key states), `tests/test_local_import.py`
|
|
25
|
+
(`agent_api` spec skipped).
|
|
20
26
|
|
|
21
27
|
## Command surface
|
|
22
28
|
|
|
@@ -31,6 +37,16 @@ Each verb → its handler in `src/cinna/main.py` → the `run_*` in
|
|
|
31
37
|
`src/cinna/account.py:run_agent_api_spec()`
|
|
32
38
|
- `cinna agent-api call` → `src/cinna/main.py:agent_api_call()` →
|
|
33
39
|
`src/cinna/account.py:run_agent_api_call()`
|
|
40
|
+
- `cinna agent-api access list` → `src/cinna/main.py:agent_api_access_list()` →
|
|
41
|
+
`src/cinna/account.py:run_agent_api_access_list()`
|
|
42
|
+
- `cinna agent-api access issue` → `src/cinna/main.py:agent_api_access_issue()` →
|
|
43
|
+
`src/cinna/account.py:run_agent_api_access_issue()` (`--to` required;
|
|
44
|
+
`--expires-in-days` is `click.IntRange(1, 3650)`)
|
|
45
|
+
- `cinna agent-api access revoke` → `src/cinna/main.py:agent_api_access_revoke()` →
|
|
46
|
+
`src/cinna/account.py:run_agent_api_access_revoke()`
|
|
47
|
+
- `cinna agent-api access scopes` → `src/cinna/main.py:agent_api_access_scopes()` →
|
|
48
|
+
`src/cinna/account.py:run_agent_api_access_scopes()` (exactly one of `--set` /
|
|
49
|
+
`--clear`, checked in the Click handler)
|
|
34
50
|
- `cinna connect agent-api` → `src/cinna/main.py:connect_agent_api()` →
|
|
35
51
|
`src/cinna/account.py:run_connect_agent_api()`
|
|
36
52
|
- `cinna connect mcp` → `src/cinna/main.py:connect_mcp()` →
|
|
@@ -75,6 +91,49 @@ Each verb → its handler in `src/cinna/main.py` → the `run_*` in
|
|
|
75
91
|
`client.api_proxy(method, path, query, json_body)`, then branches on the
|
|
76
92
|
marker header (see below) to set the exit code.
|
|
77
93
|
|
|
94
|
+
## Access verbs: key functions
|
|
95
|
+
|
|
96
|
+
- `src/cinna/account.py:_access_recipient()` — `--to` as the request field:
|
|
97
|
+
`me` becomes a null email, blank is a usage error.
|
|
98
|
+
- `src/cinna/account.py:_parse_scope_list()` — `a,b` to a list, `""` to an
|
|
99
|
+
explicit empty list, unset to `None` (leave the grant as is).
|
|
100
|
+
- `src/cinna/main.py:_access_json_mode()` — each `access` command's own
|
|
101
|
+
`--json` also turns on console JSON mode, so a refusal prints the
|
|
102
|
+
`{"result": "error", "code", "detail", "http_status"}` line with the server's
|
|
103
|
+
code instead of a bare `Error: <sentence>`
|
|
104
|
+
(`tests/test_agent_api_access.py:test_json_refusal_carries_the_server_code`).
|
|
105
|
+
- `src/cinna/account.py:run_agent_api_access_list()` — one row per person/key;
|
|
106
|
+
a grant-only person is one row with Key `—`; terminal keys filtered by
|
|
107
|
+
`_TERMINAL_KEY_STATUSES` unless `--all` with an "N ended keys hidden" footer;
|
|
108
|
+
`_print_access_footer()` prints external access on/off (warning plus
|
|
109
|
+
`manage_url` when off) and the scope catalog.
|
|
110
|
+
- `src/cinna/account.py:run_agent_api_access_issue()` — catches a
|
|
111
|
+
`CodedRefusal` with code `external_access_disabled` and re-raises it with
|
|
112
|
+
`_manage_url()` appended only when the message has no `http` URL; self-issue
|
|
113
|
+
prints the slot, `credential_url` and the `cinna_credentials.agent_api_session`
|
|
114
|
+
hint, other-issue prints the revoke hint via `_agent_ref_for_hint()`;
|
|
115
|
+
`_warn_unknown_scopes()` warns, never refuses.
|
|
116
|
+
- `src/cinna/account.py:_resolve_access_key()` — key id first, then exact
|
|
117
|
+
`token_prefix`, resolved against a fresh access listing; elided refs
|
|
118
|
+
(`_ELIDED_MARKERS`) are a usage error; none/several matches list candidates.
|
|
119
|
+
- `src/cinna/account.py:run_agent_api_access_revoke()` — `_require_yes_for_json()`
|
|
120
|
+
first; already `revoked_by_issuer` returns without a call; other ended
|
|
121
|
+
statuses ask "revoke it anyway?"; confirmation declined aborts with no call.
|
|
122
|
+
- `src/cinna/account.py:run_agent_api_access_scopes()` — `--clear` sends `None`;
|
|
123
|
+
prints the resulting scopes or "Grant removed".
|
|
124
|
+
- `src/cinna/account.py:_KEY_STATUS_LABELS` / `_key_status_cell()` — the one
|
|
125
|
+
status-word table (green Active; yellow API turned off by producer / External
|
|
126
|
+
access paused; red Revoked by issuer / Producer removed / Issuer account
|
|
127
|
+
removed / Expired; unknown values printed raw and dim).
|
|
128
|
+
- `src/cinna/account.py:_key_unavailable_state()`,
|
|
129
|
+
`agent_api_producer_from_slot()`, `agent_api_issue_hint()` — shared by slot
|
|
130
|
+
readiness and the importer; the hint derives the producer from a
|
|
131
|
+
`<producer>-api` slot (else `<producer>`) and omits `--service-uri` when there
|
|
132
|
+
is no slot.
|
|
133
|
+
- `src/cinna/client.py:drop_token()` — recursively removes every `token` key
|
|
134
|
+
from a payload; applied by `AccountClient._access_response()` and again by
|
|
135
|
+
each runner.
|
|
136
|
+
|
|
78
137
|
## The escape hatch's exit-code logic (`run_api`)
|
|
79
138
|
|
|
80
139
|
`src/cinna/account.py:run_api()` distinguishes three outcomes by the presence of
|
|
@@ -107,6 +166,22 @@ contract but has no exit-2 case (it returns the buffered
|
|
|
107
166
|
- `call_agent_api(agent_id, method, path, query, json_body)` → `POST
|
|
108
167
|
/api/v1/cli/account/agent-api/call`; owner-preview invocation, returns
|
|
109
168
|
`{status_code, headers, body, is_json}`.
|
|
169
|
+
- `list_agent_api_access(agent_id)` → `GET
|
|
170
|
+
/api/v1/cli/account/agent-api/access` (`agent_id` query param).
|
|
171
|
+
- `issue_agent_api_key(agent_id, *, issued_to_email, scopes, expires_in_days,
|
|
172
|
+
service_uri, label, read_only)` → `POST /api/v1/cli/account/agent-api/keys`;
|
|
173
|
+
`None` fields are omitted from the body.
|
|
174
|
+
- `revoke_agent_api_key(agent_id, key_id)` → `POST
|
|
175
|
+
/api/v1/cli/account/agent-api/keys/revoke`.
|
|
176
|
+
- `set_agent_api_scopes(agent_id, user_email, scopes)` → `PUT
|
|
177
|
+
/api/v1/cli/account/agent-api/access/scopes`; `scopes` is always sent, as an
|
|
178
|
+
explicit `null` for `--clear` (core answers 422 if the field is omitted; null
|
|
179
|
+
removes the grant), `user_email` omitted for the caller.
|
|
180
|
+
- All four go through `AccountClient._access_response()`: a bare
|
|
181
|
+
`{"detail": "Not Found"}` 404 becomes a `PlatformError` saying the platform
|
|
182
|
+
predates API access from the CLI; otherwise `_handle_response(coded=True)`
|
|
183
|
+
raises a `CodedRefusal` for a `{detail: {code, message}}` body (not for 401);
|
|
184
|
+
then `drop_token()`.
|
|
110
185
|
- `connect_agent_api(producer_agent_id, consumer_agent_id, credential_label,
|
|
111
186
|
read_only_override)` → `POST /api/v1/cli/account/connect/agent-api`.
|
|
112
187
|
- `connect_mcp(connector_id, consumer_agent_id, …)` → `POST
|
|
@@ -126,6 +201,26 @@ All under `/api/v1/cli/account/*`, account-token auth:
|
|
|
126
201
|
- `GET …/agent-api/spec` — the harvested OpenAPI spec JSON.
|
|
127
202
|
- `POST …/agent-api/call` — owner-preview endpoint invocation (query forwarded,
|
|
128
203
|
no consumer token, no policy edge).
|
|
204
|
+
- `GET …/agent-api/access?agent_id=` — people, scopes, keys (terminal keys
|
|
205
|
+
included), `external_access_enabled`, `identity_enabled`, `scope_catalog`,
|
|
206
|
+
`manage_url`.
|
|
207
|
+
- `POST …/agent-api/keys` — issue a key; answers the key's public fields plus
|
|
208
|
+
`base_url`, `spec_url`, `service_uri`, `issued_to_self`, `credential_url`
|
|
209
|
+
(self-issue only) and `unknown_scopes`. Never a key value.
|
|
210
|
+
- `POST …/agent-api/keys/revoke` — body `{agent_id, key_id}`; idempotent
|
|
211
|
+
(`status: revoked_by_issuer`); the grant is untouched.
|
|
212
|
+
- `PUT …/agent-api/access/scopes` — body `{agent_id, user_email, scopes}`;
|
|
213
|
+
`scopes` is required but nullable, so `--clear` sends `"scopes": null`
|
|
214
|
+
explicitly (omitting the field is a 422 on the core side); answers `{entry, unknown_scopes}`.
|
|
215
|
+
- `GET …/credentials` — each row carries `agent_api_key_status` for issued
|
|
216
|
+
keys (null for connections); `POST …/credentials` with type `agent_api` is
|
|
217
|
+
refused with a 400 that names the issue command.
|
|
218
|
+
- `api-proxy` refusals added with issued keys: 403 `excluded_path` (CLI exit 2)
|
|
219
|
+
for any method on `agents/*/agent-api/keys…`, `environments/*/workspace/download`
|
|
220
|
+
and `view-file`, `dashboards/*/blocks/*/env-file`, and POST on A2A
|
|
221
|
+
access-tokens, MCP/ACP connector tokens and agent / task-trigger webhook
|
|
222
|
+
create/regenerate-token; paths are matched percent-decoded. The CLI needs no
|
|
223
|
+
code for this: the hatch's own refusal already exits 2.
|
|
129
224
|
- `POST …/connect/agent-api` — mint the producer token + attach to the consumer
|
|
130
225
|
as a credential.
|
|
131
226
|
- `POST …/connect/mcp` — the agent2agent MCP wiring (sibling).
|
|
@@ -133,6 +228,13 @@ All under `/api/v1/cli/account/*`, account-token auth:
|
|
|
133
228
|
the "Platform API Endpoints" table in `docs/README.md` for the canonical
|
|
134
229
|
account-route list.
|
|
135
230
|
|
|
231
|
+
Error codes on the four access routes (`{"detail": {"code", "message"}}`,
|
|
232
|
+
surfaced as `CodedRefusal`): `agent_not_found` (404, not owned: no leak),
|
|
233
|
+
`recipient_not_found` (404), `ambiguous_recipient` (409, case-only duplicate
|
|
234
|
+
emails), `external_access_disabled` (400), `api_disabled` (400), `key_not_found`
|
|
235
|
+
(404), and `invalid_request` / `not_found` / `conflict` for any other platform
|
|
236
|
+
refusal. 422 is body validation. A non-developer gets a plain 403 on writes.
|
|
237
|
+
|
|
136
238
|
Producer-side author contract (not CLI files — they live in the producer's
|
|
137
239
|
synced workspace, harvested by the platform): `workspace/agent_api/*.py`
|
|
138
240
|
(endpoint modules) and `workspace/policy.yaml` (access/guardrail policy).
|
|
@@ -160,5 +262,18 @@ synced workspace, harvested by the platform): `workspace/agent_api/*.py`
|
|
|
160
262
|
raises only on 401.)
|
|
161
263
|
(`tests/test_account.py` — the `test_api_*` matrix,
|
|
162
264
|
`…test_account_client_api_proxy_*`)
|
|
265
|
+
- **No key value, ever.** `drop_token()` runs in the client and again in each
|
|
266
|
+
runner before printing or `--json`; the revoke path also scrubs the listing it
|
|
267
|
+
resolves the key from. (`tests/test_client.py` token-drop tests,
|
|
268
|
+
`tests/test_agent_api_access.py` self-issue test with a mock that returns a
|
|
269
|
+
`token`.)
|
|
270
|
+
- **`external_access_disabled` carries a URL.** The URL is appended only when
|
|
271
|
+
absent so a platform that already names it is not doubled.
|
|
272
|
+
(`src/cinna/account.py:run_agent_api_access_issue()`)
|
|
273
|
+
- **Unknown key status is unavailable, not usable.** `_key_unavailable_state()`
|
|
274
|
+
returns `key_unavailable` for a status this CLI does not know; the cell prints
|
|
275
|
+
it raw and dim. (`tests/test_account.py` slot readiness tests)
|
|
276
|
+
- **Confirm before revoke, nothing sent on decline.** `--json` without `--yes`
|
|
277
|
+
is a usage error. (`tests/test_agent_api_access.py`)
|
|
163
278
|
- **`--json` / `--data` mutual exclusion** in `run_api`; `--data @file` strips a
|
|
164
279
|
leading `@`. (`src/cinna/account.py:run_api()`)
|
|
@@ -152,9 +152,11 @@ result attached to the current task ──► delivered to the requester (Deskto
|
|
|
152
152
|
- **Remote chat** — a delegated task runs as a platform session like the ones
|
|
153
153
|
`cinna chat` drives. See [Remote Chat](../remote_chat/remote_chat.md).
|
|
154
154
|
- **cinna-core task MCP server (not in this repo)** — inside cloud tasks the
|
|
155
|
-
backend's MCP
|
|
156
|
-
|
|
157
|
-
|
|
155
|
+
backend's `cinna` MCP server exposes a `tasks_report` tool (the Cinna Task
|
|
156
|
+
Protocol, kit contract 1.6.0) that records the same kind of result; an
|
|
157
|
+
environment built before protocol version 1 still has it as `handover_report`
|
|
158
|
+
on the older `agent_task` server. It is implemented and documented in
|
|
159
|
+
cinna-core; this CLI only offers the equivalent command-line path.
|
|
158
160
|
|
|
159
161
|
Implementation: see [delegation_tech.md](delegation_tech.md).
|
|
160
162
|
Real-usage e2e scenarios: see [delegation_acceptance.md](delegation_acceptance.md).
|
|
@@ -122,8 +122,9 @@ Executor side — direct call, agent-token auth:
|
|
|
122
122
|
belongs to this environment's agent and owner, and resolves the task from the
|
|
123
123
|
session. Any 2xx is success.
|
|
124
124
|
|
|
125
|
-
Related, not in this repo: cinna-core's
|
|
126
|
-
`
|
|
125
|
+
Related, not in this repo: cinna-core's `cinna` MCP server exposes a
|
|
126
|
+
`tasks_report` tool for the same executor-side report (`handover_report` on
|
|
127
|
+
environments built before protocol version 1).
|
|
127
128
|
|
|
128
129
|
## Edge cases & guardrails (preserve these)
|
|
129
130
|
|