cinna-cli 0.2.6__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/.gitignore +1 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/PKG-INFO +20 -1
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/README.md +19 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/README.md +5 -0
- cinna_cli-0.3.0/docs/features/local_agent_import/local_agent_import.md +181 -0
- cinna_cli-0.3.0/docs/features/local_agent_import/local_agent_import_acceptance.md +264 -0
- cinna_cli-0.3.0/docs/features/local_agent_import/local_agent_import_tech.md +389 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/pyproject.toml +1 -1
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/client.py +14 -0
- cinna_cli-0.3.0/src/cinna/kit_contract.py +857 -0
- cinna_cli-0.3.0/src/cinna/local_import.py +1131 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/main.py +60 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/sync.py +20 -1
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +5 -1
- cinna_cli-0.3.0/tests/test_kit_contract.py +433 -0
- cinna_cli-0.3.0/tests/test_local_import.py +1238 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_sync.py +42 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/uv.lock +1 -1
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/.claude/commands/cinna-cli.feature.doc.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/.github/workflows/publish.yml +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/LICENSE.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/account_workspace/account_workspace.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/account_workspace/account_workspace_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/account_workspace/account_workspace_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_api/agent_api.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_api/agent_api_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_api/agent_api_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_management/agent_management.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_management/agent_management_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_management/agent_management_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_schedules/agent_schedules.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_schedules/agent_schedules_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/agent_schedules/agent_schedules_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/doctor/doctor.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/doctor/doctor_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/doctor/doctor_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/git_versioning/git_versioning.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/git_versioning/git_versioning_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/git_versioning/git_versioning_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/improvement_requests/improvement_requests.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/improvement_requests/improvement_requests_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/improvement_requests/improvement_requests_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/live_sync/live_sync.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/live_sync/live_sync_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/live_sync/live_sync_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/mcp_integration/mcp_integration.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/mcp_integration/mcp_integration_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/mcp_integration/mcp_integration_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/remote_chat/remote_chat.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/remote_chat/remote_chat_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/remote_chat/remote_chat_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/remote_exec/remote_exec.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/remote_exec/remote_exec_acceptance.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/features/remote_exec/remote_exec_tech.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/interface.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/docs/mutagen_capabilities.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/scripts/check_docs_references.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/__init__.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/account.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/auth.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/bootstrap.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/chat.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/config.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/console.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/context.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/doctor.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/errors.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/git_versioning.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/improve.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/logging.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/mcp_proxy.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/mutagen_runtime.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/sync_session.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/sync_ssh_shim.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/sync_tui.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/templates/CHAT_TESTING.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/templates/CLAUDE.md.template +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/templates/GIT_VERSIONING.md +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/src/cinna/templates/__init__.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/__init__.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/conftest.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_account.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_auth.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_bootstrap.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_chat.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_client.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_config.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_context.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_doctor.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_git_versioning.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_improve.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_main.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_mutagen_runtime.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/tests/test_sync_session.py +0 -0
- {cinna_cli-0.2.6 → cinna_cli-0.3.0}/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.
|
|
3
|
+
Version: 0.3.0
|
|
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
|
|
@@ -259,6 +259,25 @@ works exactly as for a manually set-up agent. Synced agents also appear in `cinn
|
|
|
259
259
|
|
|
260
260
|
Detach a synced workspace: stop its sync session, revoke the minted CLI token server-side (via the account-scoped revoke endpoint, authenticated with the account token; idempotent), then perform the equivalent of `cinna disconnect`: remove `.cinna/`, generated files, and the registry entry. Workspace files under `agents/<slug>/workspace/` are preserved. The revoke degrades gracefully — if it fails (no connection, or a workspace synced before token-id tracking), a warning is printed and the local teardown still completes; the token then expires on its own or can be revoked from the agent's Integrations tab.
|
|
261
261
|
|
|
262
|
+
### `cinna agent import <path> [--name TEXT] [--workspace REF] [--update] [--dry-run] [--no-push] [--yes]`
|
|
263
|
+
|
|
264
|
+
Import an agent that was built **locally** with the [Local Agent Kit](docs/features/local_agent_import/local_agent_import.md) — a folder holding a `cinna-agent.json` manifest, typically `../Local/<slug>` next to this account workspace. Run it from the account workspace root (or any folder inside it).
|
|
265
|
+
|
|
266
|
+
Nine idempotent steps, each printed as `[n/9]`: read and validate the manifest → create (or resolve) the cloud agent → write its description, router trigger, example prompts and the three document prompts in one bulk write, plus the status refresh command → attach a local workspace (`cinna agent sync`) → copy the tree into `agents/<slug>/workspace/` honouring the contract's exclude list and secret rules, generating `workspace_requirements.txt` from `pyproject.toml` when the agent doesn't ship one → `cinna sync push` → create the credential drafts and attach them → create the schedules → record the publication in `publications.json` and print the summary.
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
cinna agent import ../Local/invoice-watcher --dry-run # see the plan, touch nothing
|
|
270
|
+
cinna agent import ../Local/invoice-watcher --yes
|
|
271
|
+
cinna chat --agent invoice-watcher "check invoices from last week"
|
|
272
|
+
|
|
273
|
+
# after more local work:
|
|
274
|
+
cinna agent import ../Local/invoice-watcher --update --yes
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The folder rules come from the kit's versioned contract, `.cinna-kit/layout.json` — the same file Cinna Desktop reads — so a correction published there reaches this command on the next kit refresh rather than waiting for a new cinna-cli. `credentials/` and `app-data/` are **never** copied, not even if the contract drops them, and on top of the exclude list the contract's `secret_files` rules withhold every dotenv shape (`.env`, `.env.prod`, `staging.env`) wherever it sits, while `.env.example` still travels. No secret value is ever read, sent, or printed: credentials are created as empty drafts and the command prints the URLs the user opens to fill them in the browser.
|
|
278
|
+
|
|
279
|
+
Where the folder has been published is recorded in `publications.json`, a **sibling** of `cinna-agent.json` holding one entry per Cinna instance — it cannot live inside the manifest, because each entry records a hash of the exported tree and the manifest is part of that tree. Every step is idempotent (agent by the entry whose `platform_url` matches the instance you are logged into, credentials by name, schedules by name), so a partial import is resumed with `--update` instead of duplicating anything. The publication is recorded **only after the push settled** — `--no-push`, a failed flush, or remaining conflicts leave it unrecorded, and the next run is a plain `--update`. A legacy `cloud` block is migrated into the ledger on the first write and is still read until then, so an older folder's `--update` never creates a second agent. `--dry-run` makes no platform call and writes nothing.
|
|
280
|
+
|
|
262
281
|
### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
|
|
263
282
|
|
|
264
283
|
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.
|
|
@@ -222,6 +222,25 @@ works exactly as for a manually set-up agent. Synced agents also appear in `cinn
|
|
|
222
222
|
|
|
223
223
|
Detach a synced workspace: stop its sync session, revoke the minted CLI token server-side (via the account-scoped revoke endpoint, authenticated with the account token; idempotent), then perform the equivalent of `cinna disconnect`: remove `.cinna/`, generated files, and the registry entry. Workspace files under `agents/<slug>/workspace/` are preserved. The revoke degrades gracefully — if it fails (no connection, or a workspace synced before token-id tracking), a warning is printed and the local teardown still completes; the token then expires on its own or can be revoked from the agent's Integrations tab.
|
|
224
224
|
|
|
225
|
+
### `cinna agent import <path> [--name TEXT] [--workspace REF] [--update] [--dry-run] [--no-push] [--yes]`
|
|
226
|
+
|
|
227
|
+
Import an agent that was built **locally** with the [Local Agent Kit](docs/features/local_agent_import/local_agent_import.md) — a folder holding a `cinna-agent.json` manifest, typically `../Local/<slug>` next to this account workspace. Run it from the account workspace root (or any folder inside it).
|
|
228
|
+
|
|
229
|
+
Nine idempotent steps, each printed as `[n/9]`: read and validate the manifest → create (or resolve) the cloud agent → write its description, router trigger, example prompts and the three document prompts in one bulk write, plus the status refresh command → attach a local workspace (`cinna agent sync`) → copy the tree into `agents/<slug>/workspace/` honouring the contract's exclude list and secret rules, generating `workspace_requirements.txt` from `pyproject.toml` when the agent doesn't ship one → `cinna sync push` → create the credential drafts and attach them → create the schedules → record the publication in `publications.json` and print the summary.
|
|
230
|
+
|
|
231
|
+
```bash
|
|
232
|
+
cinna agent import ../Local/invoice-watcher --dry-run # see the plan, touch nothing
|
|
233
|
+
cinna agent import ../Local/invoice-watcher --yes
|
|
234
|
+
cinna chat --agent invoice-watcher "check invoices from last week"
|
|
235
|
+
|
|
236
|
+
# after more local work:
|
|
237
|
+
cinna agent import ../Local/invoice-watcher --update --yes
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
The folder rules come from the kit's versioned contract, `.cinna-kit/layout.json` — the same file Cinna Desktop reads — so a correction published there reaches this command on the next kit refresh rather than waiting for a new cinna-cli. `credentials/` and `app-data/` are **never** copied, not even if the contract drops them, and on top of the exclude list the contract's `secret_files` rules withhold every dotenv shape (`.env`, `.env.prod`, `staging.env`) wherever it sits, while `.env.example` still travels. No secret value is ever read, sent, or printed: credentials are created as empty drafts and the command prints the URLs the user opens to fill them in the browser.
|
|
241
|
+
|
|
242
|
+
Where the folder has been published is recorded in `publications.json`, a **sibling** of `cinna-agent.json` holding one entry per Cinna instance — it cannot live inside the manifest, because each entry records a hash of the exported tree and the manifest is part of that tree. Every step is idempotent (agent by the entry whose `platform_url` matches the instance you are logged into, credentials by name, schedules by name), so a partial import is resumed with `--update` instead of duplicating anything. The publication is recorded **only after the push settled** — `--no-push`, a failed flush, or remaining conflicts leave it unrecorded, and the next run is a plain `--update`. A legacy `cloud` block is migrated into the ledger on the first write and is still read until then, so an older folder's `--update` never creates a second agent. `--dry-run` makes no platform call and writes nothing.
|
|
243
|
+
|
|
225
244
|
### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
|
|
226
245
|
|
|
227
246
|
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.
|
|
@@ -201,6 +201,10 @@ main.py (CLI commands — Click)
|
|
|
201
201
|
├── doctor.py — `cinna doctor`: reconcile registry ↔ Mutagen, delete stalled / terminate active sessions, refresh tokens
|
|
202
202
|
├── chat.py — `cinna chat`: session-backed conversation testing (poll + NDJSON) over the api-proxy
|
|
203
203
|
├── improve.py — `cinna improve`: improvement requests users shared about your agents (list/show/download/status)
|
|
204
|
+
├── local_import.py — `cinna agent import`: the Local Agent Kit go-cloud step
|
|
205
|
+
├── kit_contract.py — the Local Agent Kit contract as data (`.cinna-kit/layout.json`):
|
|
206
|
+
│ exclude patterns, secret-file rules, export walk, content_hash,
|
|
207
|
+
│ publications.json ledger, contract-version gate
|
|
204
208
|
├── config.py — .cinna/config.json: load/save/find
|
|
205
209
|
├── auth.py — JWT storage, Authorization headers
|
|
206
210
|
├── client.py — PlatformClient: HTTP + SSE stream_exec
|
|
@@ -289,6 +293,7 @@ authoring convention.
|
|
|
289
293
|
| **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) |
|
|
290
294
|
| **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) |
|
|
291
295
|
| **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) |
|
|
296
|
+
| **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) |
|
|
292
297
|
|
|
293
298
|
The sections below (Git Versioning, Sync Transport, Remote Exec, Remote Chat,
|
|
294
299
|
Bootstrap Flow) remain as in-README quick references and backend contracts; the
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Local Agent Import (`cinna agent import`)
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Take an agent that was built **entirely offline** — with any coding assistant,
|
|
6
|
+
by a user who did not yet have a Cinna account — and put it on the platform in
|
|
7
|
+
one verb: the cloud agent is created, its prompts and metadata are written, a
|
|
8
|
+
local workspace is attached, the files are copied and pushed, credential drafts
|
|
9
|
+
and schedules are created, and the local manifest is stamped with the cloud
|
|
10
|
+
link.
|
|
11
|
+
|
|
12
|
+
This is the "go-cloud" step of the **Local Agent Kit**: the platform serves a
|
|
13
|
+
public, unauthenticated kit (`GET /agent-start`) that teaches an assistant how to
|
|
14
|
+
scaffold `~/Documents/MyAgents/Local/<slug>/` — a folder whose layout is
|
|
15
|
+
byte-compatible with a cloud agent workspace and whose `cinna-agent.json`
|
|
16
|
+
manifest carries the definitional metadata a bundle revision carries. `cinna
|
|
17
|
+
agent import` is the only CLI verb that reads that manifest.
|
|
18
|
+
|
|
19
|
+
The kit's machine-readable half is a **versioned contract** (`layout.json`,
|
|
20
|
+
`CONTRACT_VERSION`, the JSON schemas), served separately at
|
|
21
|
+
`GET /api/agent-start/contract.tar.gz` with a cheap version poll at
|
|
22
|
+
`GET /api/agent-start/contract/version`. cinna-cli reads the copy installed at
|
|
23
|
+
`.cinna-kit/`; a shipped build should obtain it from those routes rather than
|
|
24
|
+
vendoring one.
|
|
25
|
+
|
|
26
|
+
Nothing platform-side is new: the import replays the manifest through the
|
|
27
|
+
existing account-scoped verbs (`agent create`, the bulk prompt write, `agent
|
|
28
|
+
sync`, `sync push`, `account credentials create`, `agent schedule create`,
|
|
29
|
+
`agent status set-command`).
|
|
30
|
+
|
|
31
|
+
## Mental model
|
|
32
|
+
|
|
33
|
+
- **The manifest is the plan.** `cinna-agent.json` (at the agent folder root)
|
|
34
|
+
says what the agent *is*: name, slug, description, example prompts, router
|
|
35
|
+
trigger, which files hold the three prompts, the status refresh command, the
|
|
36
|
+
credentials it needs, and its schedules. The import writes exactly that and
|
|
37
|
+
invents nothing.
|
|
38
|
+
- **Two folders, one direction.** `Local/<slug>/` is where the agent was built;
|
|
39
|
+
`Cloud/` is the account workspace (`cinna login <host> --dir Cloud`). Import
|
|
40
|
+
runs **from `Cloud/`** and pushes `Local/<slug>/` into
|
|
41
|
+
`Cloud/agents/<slug>/workspace/`. After a successful import the cloud copy is
|
|
42
|
+
the live one — keep iterating there with `cinna dev`, or keep experimenting
|
|
43
|
+
locally and re-import with `--update`.
|
|
44
|
+
- **The contract is the authority.** The folder rules — what may be copied to
|
|
45
|
+
the cloud, what can hold a credential value — are **data**, not prose:
|
|
46
|
+
`.cinna-kit/layout.json`, published by cinna-core and read by three hosts
|
|
47
|
+
(Cinna Desktop, a coding assistant's kit tools, and this CLI). Import reads
|
|
48
|
+
them from that file rather than re-deriving them, so a correction cinna-core
|
|
49
|
+
publishes reaches this tool on the next kit refresh instead of waiting for a
|
|
50
|
+
new cinna-cli. When the contract cannot be found the run falls back to a
|
|
51
|
+
built-in copy and says so as a **degradation**, not as a mode.
|
|
52
|
+
- **Secrets never travel.** `credentials/` (which holds the local `.env`) and
|
|
53
|
+
`app-data/` (runtime state) are never copied — not even if the contract is
|
|
54
|
+
edited to allow them. On top of the exclude list the contract carries rules a
|
|
55
|
+
glob list cannot express: every dotenv shape (`.env`, `.env.prod`,
|
|
56
|
+
`staging.env`) is withheld wherever it sits, while `.env.example`,
|
|
57
|
+
`.env.sample` and `.env.template` still travel. Credentials are created as
|
|
58
|
+
**empty drafts** and the command prints the URLs the user opens to fill each
|
|
59
|
+
one in the browser. No secret value is read, sent, or printed at any point.
|
|
60
|
+
- **The ledger is a sibling file, never a manifest key.** Where an agent folder
|
|
61
|
+
has been published is recorded in `publications.json` beside
|
|
62
|
+
`cinna-agent.json` — one entry per Cinna instance. It has to be a separate
|
|
63
|
+
file: each entry records a hash *of the exported tree*, the manifest is a
|
|
64
|
+
member of that tree, so a hash written into the manifest could never match the
|
|
65
|
+
tree it describes. The folder would read "1 unpublished change" the instant a
|
|
66
|
+
publish succeeded, and republishing to clear it would create the next
|
|
67
|
+
mismatch.
|
|
68
|
+
- **Every step is idempotent.** The agent is matched by the ledger entry for
|
|
69
|
+
this instance, credentials by name, schedules by name. A run that dies halfway
|
|
70
|
+
is resumed with `--update`; nothing is duplicated.
|
|
71
|
+
- **The record is the commit point.** `publications.json` is written **only
|
|
72
|
+
after the workspace push settled**. If the push failed, was skipped
|
|
73
|
+
(`--no-push`), or left conflicts, nothing is recorded and the re-run is a
|
|
74
|
+
plain `--update`.
|
|
75
|
+
|
|
76
|
+
## The nine steps
|
|
77
|
+
|
|
78
|
+
Each prints as `[n/9]`:
|
|
79
|
+
|
|
80
|
+
1. **Manifest** — load and validate `cinna-agent.json`; refuse a
|
|
81
|
+
`schema_version` newer than this CLI understands, a slug that disagrees with
|
|
82
|
+
the folder name, a malformed cron, a credential without a type. Then the
|
|
83
|
+
contract: refuse a folder built against a newer **major** contract version,
|
|
84
|
+
warn on an older one or on a folder that records none, resolve the exclude
|
|
85
|
+
list and the secret rules, plan the copy and compute the tree's
|
|
86
|
+
`content_hash`.
|
|
87
|
+
2. **Agent** — resolve by the `publications.json` entry for this instance
|
|
88
|
+
(requires `--update`) or create a new agent in the active user workspace
|
|
89
|
+
(`--workspace` overrides).
|
|
90
|
+
3. **Prompts + metadata** — one bulk write carrying `description`,
|
|
91
|
+
`router_trigger_prompt`, `example_prompts`, and the three document prompts
|
|
92
|
+
read from the files named in `prompts`; then the status refresh command.
|
|
93
|
+
4. **Workspace** — reuse the synced workspace under `agents/<slug>/` if the
|
|
94
|
+
agent already has one, otherwise run `cinna agent sync`.
|
|
95
|
+
5. **Copy** — copy the tree into `agents/<slug>/workspace/`, honouring the
|
|
96
|
+
contract's exclude list and secret rules, and generate
|
|
97
|
+
`workspace_requirements.txt` from `pyproject.toml` when the agent does not
|
|
98
|
+
ship one.
|
|
99
|
+
6. **Push** — `cinna sync push` equivalent (skipped by `--no-push`).
|
|
100
|
+
7. **Credentials** — one empty draft per spec, attached to the agent; an
|
|
101
|
+
existing credential of the same name is attached rather than recreated.
|
|
102
|
+
Setup URLs are collected for the summary.
|
|
103
|
+
8. **Schedules** — one per spec, name-idempotent; `--update` rewrites an
|
|
104
|
+
existing schedule in place.
|
|
105
|
+
9. **Record** — add or update this instance's entry in `publications.json`
|
|
106
|
+
(`platform_url`, `agent_id`, `workspace`, `imported_at`, `updated_at`,
|
|
107
|
+
`contract_version`, `content_hash`) and print the summary (agent id, web UI
|
|
108
|
+
link, workspace path, credential setup URLs, and the `cinna chat`
|
|
109
|
+
verification line).
|
|
110
|
+
|
|
111
|
+
## User flows
|
|
112
|
+
|
|
113
|
+
### First import
|
|
114
|
+
|
|
115
|
+
1. In the kit root: `cinna login <platform> --dir Cloud` (once).
|
|
116
|
+
2. `python3 .cinna-kit/tools/kit.py validate Local/<slug>` — must pass.
|
|
117
|
+
3. `cd Cloud && cinna agent import ../Local/<slug>`.
|
|
118
|
+
4. Confirm the plan (or pass `--yes`), then open the printed credential setup
|
|
119
|
+
URLs and fill the secrets in the browser.
|
|
120
|
+
5. Verify: `cinna chat --agent <slug> "<first example prompt>"`.
|
|
121
|
+
|
|
122
|
+
### Look before you leap
|
|
123
|
+
|
|
124
|
+
`cinna agent import ../Local/<slug> --dry-run` prints the whole plan — every
|
|
125
|
+
file that would be copied, every credential draft, every schedule — and makes
|
|
126
|
+
**zero** platform calls and zero local writes.
|
|
127
|
+
|
|
128
|
+
### Re-import after more local work
|
|
129
|
+
|
|
130
|
+
`cinna agent import ../Local/<slug> --update` — resolves the agent by the
|
|
131
|
+
`publications.json` entry whose `platform_url` matches the instance you are
|
|
132
|
+
logged into (not by workspace, and not by position in the list), rewrites
|
|
133
|
+
prompts and metadata, re-copies the tree, pushes, attaches existing credentials,
|
|
134
|
+
and updates the schedules in place.
|
|
135
|
+
|
|
136
|
+
### Resume a partial import
|
|
137
|
+
|
|
138
|
+
If the run died before the record was written, `--update` reattaches by a unique
|
|
139
|
+
name match (and refuses when several agents share the name — add the entry to
|
|
140
|
+
`publications.json` manually to disambiguate). Credentials and schedules already
|
|
141
|
+
created are reused, not duplicated.
|
|
142
|
+
|
|
143
|
+
## Options
|
|
144
|
+
|
|
145
|
+
| Flag | Effect |
|
|
146
|
+
|------|--------|
|
|
147
|
+
| `--name TEXT` | Override the manifest's display name for the *created* agent |
|
|
148
|
+
| `--workspace REF` | Target user workspace (name or id; `default` for the Default one) for the agent and its credential drafts |
|
|
149
|
+
| `--update` | Re-import into the agent the manifest points at (required for any second run) |
|
|
150
|
+
| `--dry-run` | Print the plan; no platform call, no local write |
|
|
151
|
+
| `--no-push` | Copy the files but skip the sync push (leaves the manifest unstamped) |
|
|
152
|
+
| `--yes` / `-y` | Skip the confirmation prompt |
|
|
153
|
+
|
|
154
|
+
## Failure modes and what they mean
|
|
155
|
+
|
|
156
|
+
| Situation | Behaviour |
|
|
157
|
+
|-----------|-----------|
|
|
158
|
+
| Not in an account workspace | The standard "not in a cinna account workspace" error — run `cinna login <host> --dir Cloud` first |
|
|
159
|
+
| `cinna-agent.json` missing | Refused, with the hint that the path must be the agent folder |
|
|
160
|
+
| `schema_version` newer than supported | Refused with an upgrade hint — never half-read |
|
|
161
|
+
| Folder built against a newer **major** contract version | Refused with an upgrade hint — a minor or patch difference is not a compatibility question and passes silently |
|
|
162
|
+
| Folder records no `contract_version` | Warned and imported — every folder created before contract 1.0.0 is in that state |
|
|
163
|
+
| Already imported to this instance, no `--update` | Refused, naming the agent id and where the link was recorded |
|
|
164
|
+
| The recorded agent id is not among your agents | Refused — you are logged into a different platform, or the agent was deleted |
|
|
165
|
+
| `--update` with several name matches | Refused, listing them; add the `publications.json` entry to disambiguate |
|
|
166
|
+
| Push failed or left conflicts | Warned, **nothing recorded**, with the resolve + `--update` hint |
|
|
167
|
+
| `.cinna-kit/layout.json` missing or unreadable | Imported with the built-in contract copy, the `Exclusions:` line marked `DEGRADED`, and **no `content_hash` recorded** — a hash over a file set another host would not select is worse than none |
|
|
168
|
+
| A travelling file or directory cannot be read | Refused — a complete-looking export with a subtree missing from it is the one outcome worth stopping for |
|
|
169
|
+
| `publications.json` unreadable | Refused, and the file is left exactly as it was |
|
|
170
|
+
| Credential/schedule 403 | The platform error surfaces verbatim; earlier steps stay done and `--update` resumes |
|
|
171
|
+
| A file under `credentials/` or `app-data/`, or any dotenv shape, reaches the copy plan | Hard refusal — the exclude list or the secret rules are broken, and that is a bug to report, not to work around |
|
|
172
|
+
|
|
173
|
+
## Related
|
|
174
|
+
|
|
175
|
+
- [Agent Management](../agent_management/agent_management.md) — `agent create`,
|
|
176
|
+
`agent sync`, `agent show`, `agent status set-command`.
|
|
177
|
+
- [Agent Schedules](../agent_schedules/agent_schedules.md) — the schedule verbs
|
|
178
|
+
the import replays.
|
|
179
|
+
- [Account Workspace](../account_workspace/account_workspace.md) — the `Cloud/`
|
|
180
|
+
workspace, credential drafts, and the bulk prompt write.
|
|
181
|
+
- [Live Sync](../live_sync/live_sync.md) — what `sync push` actually does.
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
# Local Agent Import — Acceptance Scenarios (live e2e)
|
|
2
|
+
|
|
3
|
+
Real-usage scenarios for an agent doing *integration* testing of `cinna agent
|
|
4
|
+
import` against a **live** platform: a real backend, a real account workspace,
|
|
5
|
+
and a real local agent folder. These are not unit tests — they exercise agent
|
|
6
|
+
creation, the bulk prompt write through the escape hatch, child-token minting,
|
|
7
|
+
Mutagen sync, credential drafting, and schedule CRUD end to end.
|
|
8
|
+
|
|
9
|
+
How to use: pick the scenarios relevant to the change, run the **Steps**
|
|
10
|
+
verbatim, assert the **Expected**, and watch for the **Watch for** failure
|
|
11
|
+
modes. Scenarios 3–6 (idempotency and the stamp) are the highest-value ones for
|
|
12
|
+
any change to the orchestrator.
|
|
13
|
+
|
|
14
|
+
## Preconditions
|
|
15
|
+
|
|
16
|
+
- A reachable platform with an **account workspace**: `Cloud/.cinna/account.json`
|
|
17
|
+
(created by `cinna login <platform> --dir Cloud`). Every command below runs
|
|
18
|
+
from inside `Cloud/`.
|
|
19
|
+
- The account's user can create agents (`agent-developer` role) — otherwise
|
|
20
|
+
step 2 returns 403.
|
|
21
|
+
- **Editable install** of the CLI under test:
|
|
22
|
+
`python3 -c "import cinna,os;print(os.path.dirname(cinna.__file__))"` points at
|
|
23
|
+
this repo's `src/cinna`; `cinna agent import --help` lists all six flags.
|
|
24
|
+
- Mutagen installed (the import pushes through a real sync session).
|
|
25
|
+
- A **local agent folder** at `../Local/<slug>/` holding a valid
|
|
26
|
+
`cinna-agent.json` — scaffold one with the kit
|
|
27
|
+
(`python3 .cinna-kit/tools/kit.py new <slug>`), fill the three prompt files,
|
|
28
|
+
declare one credential and one schedule, and put a throwaway secret in
|
|
29
|
+
`credentials/.env` so the secret-leak assertions are meaningful.
|
|
30
|
+
|
|
31
|
+
## Scenario catalog
|
|
32
|
+
|
|
33
|
+
### 1. Dry run shows the plan and touches nothing
|
|
34
|
+
|
|
35
|
+
- **Goal:** the user can inspect the import before committing to it.
|
|
36
|
+
- **Steps:**
|
|
37
|
+
```
|
|
38
|
+
cinna agent import ../Local/<slug> --dry-run
|
|
39
|
+
```
|
|
40
|
+
- **Expected:** the file list, credential drafts, and schedules are printed;
|
|
41
|
+
"Dry run — nothing was sent to the platform."; `cinna account agents` shows no
|
|
42
|
+
new agent; `../Local/<slug>/cinna-agent.json` still has `cloud.agent_id: null`.
|
|
43
|
+
- **Watch for:** any HTTP request in `-v` output; a workspace appearing under
|
|
44
|
+
`agents/`.
|
|
45
|
+
|
|
46
|
+
### 2. First import, end to end
|
|
47
|
+
|
|
48
|
+
- **Steps:**
|
|
49
|
+
```
|
|
50
|
+
cinna agent import ../Local/<slug> --yes
|
|
51
|
+
```
|
|
52
|
+
- **Expected:** nine `[n/9]` steps; the agent exists in the UI with the
|
|
53
|
+
manifest's description, example prompts, and router trigger (`cinna agent show
|
|
54
|
+
<slug> --prompts` matches the three local prompt files); `agents/<slug>/`
|
|
55
|
+
exists and `agents/<slug>/workspace/` holds the agent's files;
|
|
56
|
+
`cinna exec --agent <slug> ls` on the remote env lists the same files; the
|
|
57
|
+
credential appears as **needs setup** with the printed setup URL; the schedule
|
|
58
|
+
is listed by `cinna agent schedule list <slug>`; the status refresh command is
|
|
59
|
+
set (`cinna agent status show <slug>`); the manifest's `cloud` block now
|
|
60
|
+
carries `platform_url`, `agent_id`, `imported_at`.
|
|
61
|
+
- **Watch for:** `credentials/` or `app-data/` reaching the remote env (check
|
|
62
|
+
with `cinna exec --agent <slug> ls -a`); any secret value in the terminal
|
|
63
|
+
output; `workspace_requirements.txt` missing when the agent has a
|
|
64
|
+
`pyproject.toml`.
|
|
65
|
+
|
|
66
|
+
### 3. Second import without `--update` is refused
|
|
67
|
+
|
|
68
|
+
- **Steps:** re-run scenario 2's command.
|
|
69
|
+
- **Expected:** exit ≠ 0, the error names the agent id and tells you to pass
|
|
70
|
+
`--update`; nothing changed on the platform.
|
|
71
|
+
- **Watch for:** a **second** agent with the same name appearing.
|
|
72
|
+
|
|
73
|
+
### 4. `--update` re-imports without duplicating
|
|
74
|
+
|
|
75
|
+
- **Steps:** edit `docs/WORKFLOW_PROMPT.md` and a script locally, then <!-- nocheck: path inside the local agent folder, not this repo -->
|
|
76
|
+
```
|
|
77
|
+
cinna agent import ../Local/<slug> --update --yes
|
|
78
|
+
```
|
|
79
|
+
- **Expected:** the same agent id; `cinna agent show <slug> --prompts` shows the
|
|
80
|
+
edited workflow prompt; the edited script is live in the env; **no** second
|
|
81
|
+
credential (the existing one is re-attached) and **no** second schedule (it is
|
|
82
|
+
updated in place); `cinna account credentials list` count unchanged.
|
|
83
|
+
- **Watch for:** a duplicate empty credential draft shadowing the one the user
|
|
84
|
+
already filled — that would break the running agent.
|
|
85
|
+
|
|
86
|
+
### 5. The stamp only happens after a successful push
|
|
87
|
+
|
|
88
|
+
- **Steps:**
|
|
89
|
+
```
|
|
90
|
+
cinna agent import ../Local/<slug2> --no-push --yes
|
|
91
|
+
```
|
|
92
|
+
- **Expected:** files are copied into `agents/<slug2>/workspace/`, the manifest
|
|
93
|
+
is **not** stamped, and the output tells you to `cinna sync push --agent
|
|
94
|
+
<slug2>` and re-run with `--update`.
|
|
95
|
+
- **Watch for:** a stamped manifest pointing at an agent whose environment never
|
|
96
|
+
received the files.
|
|
97
|
+
|
|
98
|
+
### 6. Resume after a mid-run failure
|
|
99
|
+
|
|
100
|
+
- **Goal:** a partial import is recoverable.
|
|
101
|
+
- **Steps:** cause step 7 to fail (e.g. an unreachable platform, or a credential
|
|
102
|
+
type the account cannot create), then fix it and re-run with `--update`.
|
|
103
|
+
- **Expected:** the second run reuses the agent (by name when the manifest is
|
|
104
|
+
unstamped, by id when stamped), skips what already exists, and completes.
|
|
105
|
+
- **Watch for:** a second agent created because the name match was ambiguous —
|
|
106
|
+
the command must **refuse** with a "set cloud.agent_id" hint, not guess.
|
|
107
|
+
|
|
108
|
+
### 7. Targeting a user workspace
|
|
109
|
+
|
|
110
|
+
- **Steps:**
|
|
111
|
+
```
|
|
112
|
+
cinna account user-workspace list
|
|
113
|
+
cinna agent import ../Local/<slug3> --workspace "<Workspace Name>" --yes
|
|
114
|
+
```
|
|
115
|
+
- **Expected:** the agent and its credential drafts land in that workspace
|
|
116
|
+
(visible in the UI sidebar and in `cinna account agents`).
|
|
117
|
+
- **Watch for:** the agent in the target workspace but the credential in Default
|
|
118
|
+
— the agent then cannot see its own credential.
|
|
119
|
+
|
|
120
|
+
### 8. Manifest guards
|
|
121
|
+
|
|
122
|
+
- **Steps:** in a scratch copy of the agent folder, one at a time: bump
|
|
123
|
+
`schema_version` to `2`; rename the folder so it disagrees with `slug`; make
|
|
124
|
+
`cron_string` `"every morning"`; drop a credential's `type`.
|
|
125
|
+
- **Expected:** each is refused *before* any platform call, with a message that
|
|
126
|
+
names the offending field.
|
|
127
|
+
- **Watch for:** a half-applied import (agent created, then the manifest
|
|
128
|
+
rejected).
|
|
129
|
+
|
|
130
|
+
### 9. Secrets never leave the machine
|
|
131
|
+
|
|
132
|
+
- **Steps:** with a recognizable secret in `credentials/.env`, run a full import
|
|
133
|
+
with `-v`, then
|
|
134
|
+
```
|
|
135
|
+
cinna exec --agent <slug> "grep -r '<secret>' . | head"
|
|
136
|
+
```
|
|
137
|
+
- **Expected:** no match remotely; the secret appears nowhere in the CLI output
|
|
138
|
+
or logs; `credentials/` does not exist in the remote workspace.
|
|
139
|
+
- **Watch for:** a `.env` copied because it sat outside `credentials/` — the
|
|
140
|
+
import must refuse the plan outright in that case. Scenario 10b covers the
|
|
141
|
+
`.env.<suffix>` shapes that used to clear every gate.
|
|
142
|
+
|
|
143
|
+
### 10. The contract's exclude list is honoured, and it is `layout.json`
|
|
144
|
+
|
|
145
|
+
- **Goal:** a correction cinna-core publishes reaches this tool on the next kit
|
|
146
|
+
refresh instead of waiting for a new cinna-cli release.
|
|
147
|
+
- **Setup:** a `.cinna-kit/layout.json` above the agent, and a `notes/` folder
|
|
148
|
+
inside it.
|
|
149
|
+
- **Steps:** give `layout.json` a `cloud_import_excludes` list that adds
|
|
150
|
+
`notes/` **and** omits `credentials/`; import into a fresh agent. Then edit
|
|
151
|
+
the list on disk (add `config/`) and re-run with `--update`.
|
|
152
|
+
- **Expected:** `notes/` is absent remotely; `credentials/` is *still* absent
|
|
153
|
+
(the mandatory exclusion cannot be removed); the `Exclusions:` line names the
|
|
154
|
+
`layout.json` path it read and does **not** say `DEGRADED`; the second run
|
|
155
|
+
drops `config/` without any change to cinna-cli.
|
|
156
|
+
- **Watch for:** the built-in list being used silently when a `layout.json`
|
|
157
|
+
exists; and a `kit.json` `cloud_import.exclude` still being honoured —
|
|
158
|
+
decision D6 deleted that key and reading it back would be a second authority
|
|
159
|
+
for one list.
|
|
160
|
+
|
|
161
|
+
### 10a. A missing contract is reported as a degradation
|
|
162
|
+
|
|
163
|
+
- **Goal:** the failure that made the previous regression invisible. The old
|
|
164
|
+
line read `Exclusions: built-in default list` — it announced a *mode*, so
|
|
165
|
+
nobody saw an error and the first symptom was a `.env.prod` in a cloud
|
|
166
|
+
workspace.
|
|
167
|
+
- **Steps:** import an agent with **no** `.cinna-kit/` anywhere above it.
|
|
168
|
+
- **Expected:** the run succeeds; the `Exclusions:` line begins `DEGRADED —`
|
|
169
|
+
and says why; **no `content_hash` is recorded** in `publications.json` and the
|
|
170
|
+
run prints `Content hash: WITHHELD`.
|
|
171
|
+
- **Watch for:** a hash recorded anyway. "But the fallback is the same list" is
|
|
172
|
+
a claim about this build's contract, not about the one in the folder — which
|
|
173
|
+
is the only one the other host is reading.
|
|
174
|
+
|
|
175
|
+
### 10b. Dotenv suffixes never travel, examples still do
|
|
176
|
+
|
|
177
|
+
- **Goal:** the leak the contract's `secret_files` rules close. `.env.prod` and
|
|
178
|
+
`.env.local` clear every glob in the exclude list *and* the old
|
|
179
|
+
`endswith(".env")` check, at the agent root and at any depth.
|
|
180
|
+
- **Setup:** in the agent folder, outside `credentials/`, create `.env.prod`,
|
|
181
|
+
`config/.env.staging`, `staging.env` and `.env.example`, each with a
|
|
182
|
+
recognizable secret except the example.
|
|
183
|
+
- **Steps:**
|
|
184
|
+
```
|
|
185
|
+
cinna agent import ../Local/<slug> --dry-run
|
|
186
|
+
cinna agent import ../Local/<slug> --update -y
|
|
187
|
+
cinna exec --agent <slug> "ls -a; ls -a config 2>/dev/null"
|
|
188
|
+
```
|
|
189
|
+
- **Expected:** the dry-run plan lists `.env.example` and none of the others;
|
|
190
|
+
remotely only `.env.example` exists; the secret string appears nowhere in the
|
|
191
|
+
output.
|
|
192
|
+
- **Watch for:** `.env.example` being withheld too — that is the `unless` block
|
|
193
|
+
doing its job, and a fix written as a bare `startswith(".env.")` loses it.
|
|
194
|
+
|
|
195
|
+
### 10c. A withheld secret does not move the `content_hash`
|
|
196
|
+
|
|
197
|
+
- **Goal:** "unpublished changes forever" — a path withheld from the upload but
|
|
198
|
+
counted in the hash makes the hash move for a change that can never be
|
|
199
|
+
published.
|
|
200
|
+
- **Steps:** import once and note `publications.json`'s `content_hash`. Edit
|
|
201
|
+
`.env.prod` (a withheld file) and re-run `--update`; then edit
|
|
202
|
+
the agent's own `docs/WORKFLOW_PROMPT.md` (a travelling file) and re-run <!-- nocheck: a path inside the agent folder, not this repo -->
|
|
203
|
+
again.
|
|
204
|
+
- **Expected:** the hash is unchanged after the first edit and different after
|
|
205
|
+
the second. Immediately after any successful import, the recorded hash matches
|
|
206
|
+
a fresh scan of the folder — the folder is never "behind" the instant a
|
|
207
|
+
publish succeeds.
|
|
208
|
+
- **Watch for:** a hash that moves on the very first re-run with no edits at
|
|
209
|
+
all. That means the manifest was written *after* the hash was computed.
|
|
210
|
+
|
|
211
|
+
### 10d. The ledger is a sibling file, and the legacy `cloud` block migrates
|
|
212
|
+
|
|
213
|
+
- **Goal:** `publications.json` is written, `cloud` is retired behind it, and no
|
|
214
|
+
`--update` ever creates a second agent.
|
|
215
|
+
- **Setup:** an agent folder carrying a legacy `cloud` block naming a real
|
|
216
|
+
`platform_url` and `agent_id`, and no `publications.json`.
|
|
217
|
+
- **Steps:** run `--update` against that instance, then inspect both files, then
|
|
218
|
+
run `--update` once more.
|
|
219
|
+
- **Expected:** the first run resolves the agent from the `cloud` block and does
|
|
220
|
+
**not** create a new one; afterwards `cinna-agent.json` has no `cloud` key,
|
|
221
|
+
`publications.json` holds an entry for that `platform_url`, and
|
|
222
|
+
`publications.json` is **not** present in the remote workspace. The second run
|
|
223
|
+
resolves from the ledger and is a no-op on identity.
|
|
224
|
+
- **Watch for:** a second agent appearing on the platform (the deprecated read
|
|
225
|
+
was dropped too early); a `cloud` block deleted while the ledger recorded
|
|
226
|
+
nothing (a destination must be reached before the source may be removed); a
|
|
227
|
+
`publications.json` that travelled.
|
|
228
|
+
|
|
229
|
+
### 10e. Two instances, resolved by `platform_url`
|
|
230
|
+
|
|
231
|
+
- **Goal:** the resolution key is part of the contract — not `workspace`, not
|
|
232
|
+
position in the array.
|
|
233
|
+
- **Setup:** two account workspaces for two different platform hosts.
|
|
234
|
+
- **Steps:** import the same agent folder into instance A, then into instance B,
|
|
235
|
+
then re-run `--update` against A.
|
|
236
|
+
- **Expected:** `publications.json` holds two entries; the A re-run updates A's
|
|
237
|
+
agent and leaves B's entry byte-identical; an entry whose `workspace` key is
|
|
238
|
+
absent entirely still resolves (that is the shape Cinna Desktop writes).
|
|
239
|
+
- **Watch for:** the second import being refused as "already imported", or the
|
|
240
|
+
A re-run updating B's entry.
|
|
241
|
+
|
|
242
|
+
### 10f. The `contract_version` gate
|
|
243
|
+
|
|
244
|
+
- **Steps:** set the manifest's `contract_version` to `2.0.0` and import; then
|
|
245
|
+
to `1.4.2` and import; then remove the key entirely and import.
|
|
246
|
+
- **Expected:** `2.0.0` is refused before any platform call, naming `2.x`;
|
|
247
|
+
`1.4.2` passes **silently** (same major — a minor contract change is additive
|
|
248
|
+
by definition and reaches a non-adopting reader as silence); the missing key
|
|
249
|
+
warns about a re-stamp and imports anyway.
|
|
250
|
+
- **Watch for:** a warning on the same-major pair. That is the gate's shape, not
|
|
251
|
+
a bug: anything an old reader must notice needs a major bump.
|
|
252
|
+
|
|
253
|
+
### 11. Verification loop the guide promises
|
|
254
|
+
|
|
255
|
+
- **Steps:**
|
|
256
|
+
```
|
|
257
|
+
cinna chat --agent <slug> "<first example prompt>"
|
|
258
|
+
```
|
|
259
|
+
- **Expected:** the agent answers using its imported workflow prompt and
|
|
260
|
+
scripts. With the credential still empty, it should say what it needs rather
|
|
261
|
+
than crash.
|
|
262
|
+
- **Watch for:** prompts that landed in the DB but not in the env — if the env
|
|
263
|
+
was already running, `cinna api POST agents/<id>/sync-prompts` is the manual
|
|
264
|
+
push.
|