cinna-cli 0.2.6__tar.gz → 0.4.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.
Files changed (103) hide show
  1. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/.gitignore +1 -0
  2. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/PKG-INFO +41 -5
  3. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/README.md +40 -4
  4. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/README.md +16 -5
  5. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace.md +85 -7
  6. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace_acceptance.md +110 -3
  7. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace_tech.md +92 -12
  8. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding.md +44 -6
  9. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md +45 -1
  10. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_tech.md +97 -6
  11. cinna_cli-0.4.0/docs/features/local_agent_import/local_agent_import.md +181 -0
  12. cinna_cli-0.4.0/docs/features/local_agent_import/local_agent_import_acceptance.md +264 -0
  13. cinna_cli-0.4.0/docs/features/local_agent_import/local_agent_import_tech.md +389 -0
  14. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/pyproject.toml +1 -1
  15. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/account.py +228 -30
  16. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/bootstrap.py +1 -2
  17. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/chat.py +1 -1
  18. cinna_cli-0.4.0/src/cinna/cli_version.py +111 -0
  19. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/client.py +14 -0
  20. cinna_cli-0.4.0/src/cinna/console.py +187 -0
  21. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/doctor.py +46 -3
  22. cinna_cli-0.4.0/src/cinna/errors.py +212 -0
  23. cinna_cli-0.4.0/src/cinna/kit_contract.py +857 -0
  24. cinna_cli-0.4.0/src/cinna/local_import.py +1131 -0
  25. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/main.py +223 -25
  26. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/mutagen_runtime.py +40 -9
  27. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/sync.py +20 -1
  28. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/sync_session.py +2 -1
  29. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/sync_tui.py +6 -9
  30. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +5 -1
  31. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/conftest.py +10 -0
  32. cinna_cli-0.4.0/tests/test_cli_version.py +55 -0
  33. cinna_cli-0.4.0/tests/test_kit_contract.py +433 -0
  34. cinna_cli-0.4.0/tests/test_local_import.py +1238 -0
  35. cinna_cli-0.4.0/tests/test_onboarding.py +800 -0
  36. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_sync.py +42 -0
  37. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/uv.lock +1 -1
  38. cinna_cli-0.2.6/src/cinna/console.py +0 -39
  39. cinna_cli-0.2.6/src/cinna/errors.py +0 -66
  40. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/.claude/commands/cinna-cli.feature.doc.md +0 -0
  41. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/.github/workflows/publish.yml +0 -0
  42. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/LICENSE.md +0 -0
  43. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_api/agent_api.md +0 -0
  44. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_api/agent_api_acceptance.md +0 -0
  45. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_api/agent_api_tech.md +0 -0
  46. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_management/agent_management.md +0 -0
  47. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_management/agent_management_acceptance.md +0 -0
  48. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_management/agent_management_tech.md +0 -0
  49. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_schedules/agent_schedules.md +0 -0
  50. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_schedules/agent_schedules_acceptance.md +0 -0
  51. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/agent_schedules/agent_schedules_tech.md +0 -0
  52. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/doctor/doctor.md +0 -0
  53. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/doctor/doctor_acceptance.md +0 -0
  54. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/doctor/doctor_tech.md +0 -0
  55. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/git_versioning/git_versioning.md +0 -0
  56. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/git_versioning/git_versioning_acceptance.md +0 -0
  57. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/git_versioning/git_versioning_tech.md +0 -0
  58. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/improvement_requests/improvement_requests.md +0 -0
  59. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/improvement_requests/improvement_requests_acceptance.md +0 -0
  60. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/improvement_requests/improvement_requests_tech.md +0 -0
  61. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/live_sync/live_sync.md +0 -0
  62. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/live_sync/live_sync_acceptance.md +0 -0
  63. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/live_sync/live_sync_tech.md +0 -0
  64. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/mcp_integration/mcp_integration.md +0 -0
  65. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/mcp_integration/mcp_integration_acceptance.md +0 -0
  66. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/mcp_integration/mcp_integration_tech.md +0 -0
  67. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/remote_chat/remote_chat.md +0 -0
  68. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/remote_chat/remote_chat_acceptance.md +0 -0
  69. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/remote_chat/remote_chat_tech.md +0 -0
  70. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/remote_exec/remote_exec.md +0 -0
  71. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/remote_exec/remote_exec_acceptance.md +0 -0
  72. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/features/remote_exec/remote_exec_tech.md +0 -0
  73. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/interface.md +0 -0
  74. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/docs/mutagen_capabilities.md +0 -0
  75. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/scripts/check_docs_references.py +0 -0
  76. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/__init__.py +0 -0
  77. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/auth.py +0 -0
  78. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/config.py +0 -0
  79. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/context.py +0 -0
  80. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/git_versioning.py +0 -0
  81. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/improve.py +0 -0
  82. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/logging.py +0 -0
  83. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/mcp_proxy.py +0 -0
  84. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/sync_ssh_shim.py +0 -0
  85. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/templates/CHAT_TESTING.md +0 -0
  86. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/templates/CLAUDE.md.template +0 -0
  87. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/templates/GIT_VERSIONING.md +0 -0
  88. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/src/cinna/templates/__init__.py +0 -0
  89. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/__init__.py +0 -0
  90. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_account.py +0 -0
  91. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_auth.py +0 -0
  92. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_bootstrap.py +0 -0
  93. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_chat.py +0 -0
  94. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_client.py +0 -0
  95. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_config.py +0 -0
  96. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_context.py +0 -0
  97. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_doctor.py +0 -0
  98. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_git_versioning.py +0 -0
  99. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_improve.py +0 -0
  100. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_main.py +0 -0
  101. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_mutagen_runtime.py +0 -0
  102. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_sync_session.py +0 -0
  103. {cinna_cli-0.2.6 → cinna_cli-0.4.0}/tests/test_sync_ssh_shim.py +0 -0
@@ -8,4 +8,5 @@ __pycache__/
8
8
  dist/
9
9
  build/
10
10
  cinna.log
11
+ cinna.log.*
11
12
  .claude/settings.local.json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: cinna-cli
3
- Version: 0.2.6
3
+ Version: 0.4.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
@@ -63,7 +63,11 @@ Your Editor / Claude Code
63
63
  ## Prerequisites
64
64
 
65
65
  - **Python 3.10+**
66
- - **[Mutagen](https://mutagen.io)** (version pinned by the platform — `cinna setup` checks and prompts to install)
66
+ - **[Mutagen](https://mutagen.io)** (version pinned by the platform — `cinna setup` checks and prompts to install; set `CINNA_MUTAGEN_BIN=/abs/path/mutagen` to use a specific binary instead of the one on PATH)
67
+
68
+ ### Cinna Desktop
69
+
70
+ [Cinna Desktop](https://github.com/opencinna/cinna-desktop) installs its **own pinned copy** of cinna-cli (with `uv` and Mutagen alongside, inside its data folder) and drives it as a child process to create and keep alive an account workspace under `<AgentsHome>/Cloud/<host>/` right after you sign in — no `cinna login`, no token to paste. That private copy is not on your `PATH` unless you opt in from the desktop's settings; installing cinna-cli yourself (`uv tool install cinna-cli`) for terminal use is fine and both operate on the same workspace. The version the platform pins is reported by `cinna account status` / `cinna doctor`.
67
71
 
68
72
  ## Getting Started
69
73
 
@@ -135,7 +139,7 @@ cinna login app.example.com --dir my-cinna # always into a named sub
135
139
  # ✓ Account workspace ready.
136
140
  ```
137
141
 
138
- Use it when `cinna account status` or `cinna doctor` reports the account token has expired. Because the per-agent tokens minted from an account (`cinna agent sync`) can only be re-minted while the account token is valid, the flow is: `cinna login` (refresh the account), then `cinna doctor` (re-mint the dependent sub-agent tokens). If the platform doesn't expose the device-login endpoints yet, `cinna login` says so and points you at the `cinna account setup` paste fallback.
142
+ Use it when `cinna account status` or `cinna doctor` reports the account token has expired. Because the per-agent tokens minted from an account (`cinna agent sync`) can only be re-minted while the account token is valid, the flow is: `cinna login` (refresh the account), then `cinna doctor` (re-mint the dependent sub-agent tokens). If the platform doesn't expose the device-login endpoints yet, `cinna login` says so and points you at the `cinna account set-token` paste fallback.
139
143
 
140
144
  ### `cinna account setup <token_or_url>`
141
145
 
@@ -145,7 +149,7 @@ Initialize an **account workspace** — a multi-agent root from which you can di
145
149
  curl -sL https://your-platform.com/api/cli-setup/account/TOKEN | python3 -
146
150
  ```
147
151
 
148
- Accepts the same input forms as `cinna setup` (full curl command, URL, or bare token — bare tokens need `CINNA_PLATFORM_URL`). Creates `my-cinna/` (override with `--dir`) containing:
152
+ Accepts the same input forms as `cinna setup` (full curl command, URL, or bare token — bare tokens need `CINNA_PLATFORM_URL`). Creates a folder named after the platform domain (override with `--dir`: an absolute path is used as is with parents created, a relative one lands under the current directory; an existing account workspace there is refused before the token is spent) containing:
149
153
 
150
154
  ```
151
155
  my-cinna/
@@ -159,6 +163,17 @@ Setup also downloads the **context package** into `context/` — curated platfor
159
163
 
160
164
  The account token is only used for the account-level endpoints (listing agents, minting per-agent tokens, the context package). Per-agent work always runs on each child workspace's own token. Revoking the account session in Settings disconnects every agent synced from it.
161
165
 
166
+ `account setup`, `account set-token` and `account status` take `--no-input` (never prompt: defaults, or fail with code `needs_input`; also `CINNA_NO_INPUT=1`, accepted before or after the subcommand) and `--json` (one JSON object per line on stdout — progress `{"step","total","status","message"}` lines, then a final `{"result":"ok"|"error",…}`; implies `--no-input`). Exit codes are stable for every command: `0` ok, `10` setup token invalid/expired/used, `11` token for another account, `12` platform unreachable or 5xx, `1` anything else (the JSON `code` says what), `2` usage.
167
+
168
+ ### `cinna account set-token <token_or_url>`
169
+
170
+ Refresh the **account** token in place from a fresh account setup token — the account counterpart of `cinna set-token`, and the paste alternative to `cinna login`. Run inside the account workspace; it re-exchanges under the stored machine name and rewrites only `account_token` (plus a refreshed platform/frontend URL) in `.cinna/account.json`. The active user workspace, machine name, `context/` and every synced child under `agents/` are untouched; run `cinna doctor` afterwards to re-mint expired child tokens. A token for a different account is refused (exit `11`) and nothing is written. Bare tokens reuse the stored platform URL.
171
+
172
+ ```bash
173
+ cd my-cinna/
174
+ cinna account set-token 'curl -sL https://your-platform.com/api/cli-setup/account/TOKEN | python3 -'
175
+ ```
176
+
162
177
  ### `cinna account agents`
163
178
 
164
179
  List the agents your account can access (run from inside the account workspace). For each agent: display name + ID, building rights (`✓ can build`, `view-only`, or `foreign install` — installed bundles are publisher-managed and can't be synced), whether a remote environment is active, and whether a local workspace already exists under `agents/`.
@@ -167,7 +182,9 @@ List the agents your account can access (run from inside the account workspace).
167
182
 
168
183
  One-shot summary of the account workspace: platform/frontend URLs, machine name, synced-agent count, and an account-token probe (`valid token` / `expired token` / `no connection`) — the account-level counterpart of `cinna status`.
169
184
 
170
- Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there.
185
+ Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there. Likewise it compares the installed **cinna-cli version** with the one the platform pins (its `/.well-known/cinna-desktop` discovery document) and suggests `uv tool install cinna-cli==<pin>` when behind; no pin means "unknown", not an error.
186
+
187
+ With `--json` it prints a single line: `{"result":"ok","workspace",…,"token":"valid|expired|unreachable","synced_agents":N,"agents":[…],"context_package":{"local","remote","state"},"cli":{"installed","required","state"}}`.
171
188
 
172
189
  ### `cinna account refresh-context`
173
190
 
@@ -259,6 +276,25 @@ works exactly as for a manually set-up agent. Synced agents also appear in `cinn
259
276
 
260
277
  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
278
 
279
+ ### `cinna agent import <path> [--name TEXT] [--workspace REF] [--update] [--dry-run] [--no-push] [--yes]`
280
+
281
+ 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).
282
+
283
+ 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.
284
+
285
+ ```bash
286
+ cinna agent import ../Local/invoice-watcher --dry-run # see the plan, touch nothing
287
+ cinna agent import ../Local/invoice-watcher --yes
288
+ cinna chat --agent invoice-watcher "check invoices from last week"
289
+
290
+ # after more local work:
291
+ cinna agent import ../Local/invoice-watcher --update --yes
292
+ ```
293
+
294
+ 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.
295
+
296
+ 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.
297
+
262
298
  ### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
263
299
 
264
300
  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.
@@ -26,7 +26,11 @@ Your Editor / Claude Code
26
26
  ## Prerequisites
27
27
 
28
28
  - **Python 3.10+**
29
- - **[Mutagen](https://mutagen.io)** (version pinned by the platform — `cinna setup` checks and prompts to install)
29
+ - **[Mutagen](https://mutagen.io)** (version pinned by the platform — `cinna setup` checks and prompts to install; set `CINNA_MUTAGEN_BIN=/abs/path/mutagen` to use a specific binary instead of the one on PATH)
30
+
31
+ ### Cinna Desktop
32
+
33
+ [Cinna Desktop](https://github.com/opencinna/cinna-desktop) installs its **own pinned copy** of cinna-cli (with `uv` and Mutagen alongside, inside its data folder) and drives it as a child process to create and keep alive an account workspace under `<AgentsHome>/Cloud/<host>/` right after you sign in — no `cinna login`, no token to paste. That private copy is not on your `PATH` unless you opt in from the desktop's settings; installing cinna-cli yourself (`uv tool install cinna-cli`) for terminal use is fine and both operate on the same workspace. The version the platform pins is reported by `cinna account status` / `cinna doctor`.
30
34
 
31
35
  ## Getting Started
32
36
 
@@ -98,7 +102,7 @@ cinna login app.example.com --dir my-cinna # always into a named sub
98
102
  # ✓ Account workspace ready.
99
103
  ```
100
104
 
101
- Use it when `cinna account status` or `cinna doctor` reports the account token has expired. Because the per-agent tokens minted from an account (`cinna agent sync`) can only be re-minted while the account token is valid, the flow is: `cinna login` (refresh the account), then `cinna doctor` (re-mint the dependent sub-agent tokens). If the platform doesn't expose the device-login endpoints yet, `cinna login` says so and points you at the `cinna account setup` paste fallback.
105
+ Use it when `cinna account status` or `cinna doctor` reports the account token has expired. Because the per-agent tokens minted from an account (`cinna agent sync`) can only be re-minted while the account token is valid, the flow is: `cinna login` (refresh the account), then `cinna doctor` (re-mint the dependent sub-agent tokens). If the platform doesn't expose the device-login endpoints yet, `cinna login` says so and points you at the `cinna account set-token` paste fallback.
102
106
 
103
107
  ### `cinna account setup <token_or_url>`
104
108
 
@@ -108,7 +112,7 @@ Initialize an **account workspace** — a multi-agent root from which you can di
108
112
  curl -sL https://your-platform.com/api/cli-setup/account/TOKEN | python3 -
109
113
  ```
110
114
 
111
- Accepts the same input forms as `cinna setup` (full curl command, URL, or bare token — bare tokens need `CINNA_PLATFORM_URL`). Creates `my-cinna/` (override with `--dir`) containing:
115
+ Accepts the same input forms as `cinna setup` (full curl command, URL, or bare token — bare tokens need `CINNA_PLATFORM_URL`). Creates a folder named after the platform domain (override with `--dir`: an absolute path is used as is with parents created, a relative one lands under the current directory; an existing account workspace there is refused before the token is spent) containing:
112
116
 
113
117
  ```
114
118
  my-cinna/
@@ -122,6 +126,17 @@ Setup also downloads the **context package** into `context/` — curated platfor
122
126
 
123
127
  The account token is only used for the account-level endpoints (listing agents, minting per-agent tokens, the context package). Per-agent work always runs on each child workspace's own token. Revoking the account session in Settings disconnects every agent synced from it.
124
128
 
129
+ `account setup`, `account set-token` and `account status` take `--no-input` (never prompt: defaults, or fail with code `needs_input`; also `CINNA_NO_INPUT=1`, accepted before or after the subcommand) and `--json` (one JSON object per line on stdout — progress `{"step","total","status","message"}` lines, then a final `{"result":"ok"|"error",…}`; implies `--no-input`). Exit codes are stable for every command: `0` ok, `10` setup token invalid/expired/used, `11` token for another account, `12` platform unreachable or 5xx, `1` anything else (the JSON `code` says what), `2` usage.
130
+
131
+ ### `cinna account set-token <token_or_url>`
132
+
133
+ Refresh the **account** token in place from a fresh account setup token — the account counterpart of `cinna set-token`, and the paste alternative to `cinna login`. Run inside the account workspace; it re-exchanges under the stored machine name and rewrites only `account_token` (plus a refreshed platform/frontend URL) in `.cinna/account.json`. The active user workspace, machine name, `context/` and every synced child under `agents/` are untouched; run `cinna doctor` afterwards to re-mint expired child tokens. A token for a different account is refused (exit `11`) and nothing is written. Bare tokens reuse the stored platform URL.
134
+
135
+ ```bash
136
+ cd my-cinna/
137
+ cinna account set-token 'curl -sL https://your-platform.com/api/cli-setup/account/TOKEN | python3 -'
138
+ ```
139
+
125
140
  ### `cinna account agents`
126
141
 
127
142
  List the agents your account can access (run from inside the account workspace). For each agent: display name + ID, building rights (`✓ can build`, `view-only`, or `foreign install` — installed bundles are publisher-managed and can't be synced), whether a remote environment is active, and whether a local workspace already exists under `agents/`.
@@ -130,7 +145,9 @@ List the agents your account can access (run from inside the account workspace).
130
145
 
131
146
  One-shot summary of the account workspace: platform/frontend URLs, machine name, synced-agent count, and an account-token probe (`valid token` / `expired token` / `no connection`) — the account-level counterpart of `cinna status`.
132
147
 
133
- Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there.
148
+ Also reports the workspace's **context package version** against the platform's current one — the orchestrator guides ship in that tree, so a workspace set up before a guide existed silently lacks it. When it is behind, the command says so and points at `cinna account refresh-context`; `cinna improve list` prints the same nudge, since its playbook lives there. Likewise it compares the installed **cinna-cli version** with the one the platform pins (its `/.well-known/cinna-desktop` discovery document) and suggests `uv tool install cinna-cli==<pin>` when behind; no pin means "unknown", not an error.
149
+
150
+ With `--json` it prints a single line: `{"result":"ok","workspace",…,"token":"valid|expired|unreachable","synced_agents":N,"agents":[…],"context_package":{"local","remote","state"},"cli":{"installed","required","state"}}`.
134
151
 
135
152
  ### `cinna account refresh-context`
136
153
 
@@ -222,6 +239,25 @@ works exactly as for a manually set-up agent. Synced agents also appear in `cinn
222
239
 
223
240
  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
241
 
242
+ ### `cinna agent import <path> [--name TEXT] [--workspace REF] [--update] [--dry-run] [--no-push] [--yes]`
243
+
244
+ 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).
245
+
246
+ 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.
247
+
248
+ ```bash
249
+ cinna agent import ../Local/invoice-watcher --dry-run # see the plan, touch nothing
250
+ cinna agent import ../Local/invoice-watcher --yes
251
+ cinna chat --agent invoice-watcher "check invoices from last week"
252
+
253
+ # after more local work:
254
+ cinna agent import ../Local/invoice-watcher --update --yes
255
+ ```
256
+
257
+ 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.
258
+
259
+ 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.
260
+
225
261
  ### `cinna connect agent-api --producer <agent> --consumer <agent> [--label TEXT] [--read-only]`
226
262
 
227
263
  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.
@@ -137,6 +137,7 @@ Key properties:
137
137
  A second token type (`token_type="cli-account"`) issued to an **account workspace** (`.cinna/account.json`). Scoped only to the `/account/*` routes — it discovers agents and mints per-agent CLI tokens (`cinna agent sync`), but cannot itself sync or exec. Same 7-day rolling expiry as a CLI token.
138
138
 
139
139
  - **Refreshable without a paste** — `cinna login` runs an RFC 8628 device-authorization flow: the CLI prints a short code + URL, the user clicks **Authorize** in the browser (already signed in), and the CLI swaps the fresh token into `.cinna/account.json` in place. Run from an empty/new folder, the same command instead bootstraps a brand-new account workspace.
140
+ - **Refreshable from a setup token** — `cinna account set-token <token_or_url>` re-exchanges a fresh *account* setup token under the stored machine name and rewrites only `account_token` (same in-place swap). Bound to the same account: a different platform origin or token subject aborts with exit `11`. This is the path Cinna Desktop drives (`--no-input --json`) so the user never runs `cinna login`.
140
141
  - **Mints child tokens** — per-agent tokens minted from it carry its id as provenance and are re-mintable via `POST /account/agents/{id}/mint` (used by `cinna agent sync` and `cinna doctor`).
141
142
 
142
143
  ### Knowledge Source
@@ -201,19 +202,24 @@ main.py (CLI commands — Click)
201
202
  ├── doctor.py — `cinna doctor`: reconcile registry ↔ Mutagen, delete stalled / terminate active sessions, refresh tokens
202
203
  ├── chat.py — `cinna chat`: session-backed conversation testing (poll + NDJSON) over the api-proxy
203
204
  ├── improve.py — `cinna improve`: improvement requests users shared about your agents (list/show/download/status)
205
+ ├── local_import.py — `cinna agent import`: the Local Agent Kit go-cloud step
206
+ ├── kit_contract.py — the Local Agent Kit contract as data (`.cinna-kit/layout.json`):
207
+ │ exclude patterns, secret-file rules, export walk, content_hash,
208
+ │ publications.json ledger, contract-version gate
204
209
  ├── config.py — .cinna/config.json: load/save/find
205
210
  ├── auth.py — JWT storage, Authorization headers
206
211
  ├── client.py — PlatformClient: HTTP + SSE stream_exec
207
- ├── mutagen_runtime.py — detect/install Mutagen; gate on version match
212
+ ├── mutagen_runtime.py — detect/install Mutagen; gate on version match; `CINNA_MUTAGEN_BIN` override
213
+ ├── cli_version.py — installed vs platform-pinned cinna-cli version
208
214
  ├── sync_session.py — wrap the `mutagen` CLI (start/stop/status/conflicts)
209
215
  ├── sync_tui.py — live Textual TUI shown by `cinna dev` (Sync/Details/Conflicts tabs)
210
216
  ├── sync_ssh_shim.py — `cinna-sync-ssh` entry point (WebSocket transport)
211
217
  ├── sync.py — tarball/zip extraction helpers (initial clone only)
212
218
  ├── context.py — CLAUDE.md, BUILDING_AGENT.md, .mcp.json, opencode.json
213
219
  ├── mcp_proxy.py — MCP stdio server for knowledge_query
214
- ├── console.py — Rich helpers
220
+ ├── console.py — Rich helpers + the `--json` / `--no-input` switches (prompt/confirm wrappers, JSON lines)
215
221
  ├── logging.py — cinna.log (rotating file handler)
216
- └── errors.py — exception hierarchy
222
+ └── errors.py — exception hierarchy; `CinnaExit` = stable exit code + machine code
217
223
  ```
218
224
 
219
225
  ### Local Directory Layout
@@ -278,8 +284,8 @@ authoring convention.
278
284
 
279
285
  | Feature | Command surface | Docs |
280
286
  |---|---|---|
281
- | **Bootstrap & onboarding** | `cinna setup` / `set-token` / `login` / `list` / `status` / `disconnect[-all]` / `completion` / `dev` / `redev` | [business](features/bootstrap_onboarding/bootstrap_onboarding.md) · [tech](features/bootstrap_onboarding/bootstrap_onboarding_tech.md) · [acceptance](features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md) |
282
- | **Account workspace** | `cinna account` (setup, agents, status, refresh-context, user-workspace, credentials) | [business](features/account_workspace/account_workspace.md) · [tech](features/account_workspace/account_workspace_tech.md) · [acceptance](features/account_workspace/account_workspace_acceptance.md) |
287
+ | **Bootstrap & onboarding** | `cinna setup` / `set-token` / `login` / `list` / `status` / `disconnect[-all]` / `completion` / `dev` / `redev`; the no-TTY contract (`--no-input`, `--json`, exit codes, `CINNA_MUTAGEN_BIN`) | [business](features/bootstrap_onboarding/bootstrap_onboarding.md) · [tech](features/bootstrap_onboarding/bootstrap_onboarding_tech.md) · [acceptance](features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md) |
288
+ | **Account workspace** | `cinna account` (setup, set-token, agents, status, refresh-context, user-workspace, credentials); desktop-managed workspaces | [business](features/account_workspace/account_workspace.md) · [tech](features/account_workspace/account_workspace_tech.md) · [acceptance](features/account_workspace/account_workspace_acceptance.md) |
283
289
  | **Agent management** | `cinna agent` (sync, unsync, create, restart-env, show, status) | [business](features/agent_management/agent_management.md) · [tech](features/agent_management/agent_management_tech.md) · [acceptance](features/agent_management/agent_management_acceptance.md) |
284
290
  | **Agent schedules** | `cinna agent schedule` (list, generate, create, update, run, logs, delete) | [business](features/agent_schedules/agent_schedules.md) · [tech](features/agent_schedules/agent_schedules_tech.md) · [acceptance](features/agent_schedules/agent_schedules_acceptance.md) |
285
291
  | **Live sync** | `cinna sync` (status, conflicts, push, pull, resolve) + Mutagen transport | [business](features/live_sync/live_sync.md) · [tech](features/live_sync/live_sync_tech.md) · [acceptance](features/live_sync/live_sync_acceptance.md) |
@@ -289,6 +295,7 @@ authoring convention.
289
295
  | **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
296
  | **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
297
  | **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) |
298
+ | **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
299
 
293
300
  The sections below (Git Versioning, Sync Transport, Remote Exec, Remote Chat,
294
301
  Bootstrap Flow) remain as in-README quick references and backend contracts; the
@@ -600,6 +607,8 @@ When a CLI token expires (or is revoked) the normal remedy is `cinna set-token <
600
607
  | Method | Route | Auth | Purpose |
601
608
  |--------|-------|------|---------|
602
609
  | POST | `/api/cli-setup/{token}` | Token | Exchange setup token for CLI token |
610
+ | POST | `/api/cli-setup/account/{token}` | Token | Exchange an **account** setup token (`cinna account setup` / `cinna account set-token`); 4xx ⇒ exit 10, 5xx ⇒ exit 12 |
611
+ | GET | `/.well-known/cinna-desktop` | None | Desktop discovery document; `local_dev.cinna_cli_version` is the cinna-cli pin `cinna account status` / `cinna doctor` compare against (absent ⇒ "unknown") |
603
612
  | GET | `/api/v1/cli/agents/{id}/workspace` | CLI JWT | One-shot tarball for initial clone |
604
613
  | GET | `/api/v1/cli/agents/{id}/building-context` | CLI JWT | Assembled building prompt |
605
614
  | POST | `/api/v1/cli/agents/{id}/knowledge/search` | CLI JWT | Knowledge base search (via MCP proxy) |
@@ -640,6 +649,8 @@ uv run ruff format --check src/
640
649
 
641
650
  The CLI is published to [PyPI](https://pypi.org/project/cinna-cli/) by `.github/workflows/publish.yml`, which runs on any pushed tag matching `v*` (and via manual `workflow_dispatch`). It builds the sdist + wheel with `uv build`, runs `twine check`, then publishes through PyPI **Trusted Publishing** (OIDC — no stored API token) from the GitHub `pypi` environment.
642
651
 
652
+ Cinna Desktop installs a **pinned** version (`uv tool install cinna-cli==<version>`) — the version each cinna-core instance advertises as `local_dev.cinna_cli_version` in its discovery document (`CINNA_CLI_VERSION` setting). A release that changes the driver contract (exit codes, `--json` shapes, the three desktop-driven verbs) must be published to PyPI **before** cinna-core's pin moves to it.
653
+
643
654
  ### Cutting a release
644
655
 
645
656
  Versioning is SemVer (`MAJOR.MINOR.PATCH`); a patch release is the common case. From a clean `main` with the changes you want to ship already merged:
@@ -43,7 +43,16 @@ each with its own token, registry entry, and Mutagen session.
43
43
 
44
44
  - **Account CLI token** — `cli-account` JWT in `.cinna/account.json`. Same 7-day
45
45
  rolling expiry as a per-agent token. Refreshed in place by `cinna login` (a
46
- browser device-authorization flow — no paste). Mints child tokens; never syncs.
46
+ browser device-authorization flow — no paste) or by `cinna account set-token`
47
+ (paste / hand over a fresh account setup token). Mints child tokens; never
48
+ syncs.
49
+ - **Desktop-managed workspace** — an account workspace that Cinna Desktop
50
+ created and keeps alive by driving `cinna` as a child process with no
51
+ terminal: `account setup` once (into `<AgentsHome>/Cloud/<host>/`), then
52
+ `account set-token` whenever it mints a new setup token with its own
53
+ session, and `account status --json` to reconcile. The user never runs
54
+ `cinna login`, yet the workspace is byte-identical to a terminal-made one —
55
+ the user's own terminal can `cd` in and use every command.
47
56
  - **`agents/` directory** — where `cinna agent sync` materializes per-agent
48
57
  checkouts. Account commands that touch local state (`status`, `agents`,
49
58
  `refresh-context`) walk this tree to find synced children.
@@ -74,11 +83,52 @@ each with its own token, registry entry, and Mutagen session.
74
83
  `agents/`, the orchestrator `CLAUDE.md` + `.claude/settings.json`, the
75
84
  knowledge MCP wiring, and the `context/` package. The folder name defaults to
76
85
  the platform domain (e.g. `demo-core_opencinna_io`); `--dir` or the prompt
77
- overrides it.
86
+ overrides it. `--dir` is a contract: an **absolute** path is used exactly as
87
+ given (missing parents are created), a relative one lands under the current
88
+ directory. Either way an existing `.cinna/account.json` at the target is
89
+ refused before the token is spent.
78
90
  3. Alternatively, `cinna login <domain>` from an empty folder bootstraps the same
79
91
  workspace via the browser device flow (no paste); run inside an existing
80
92
  account workspace it refreshes the token in place.
81
93
 
94
+ ### Refresh the token from a setup token — `cinna account set-token`
95
+ 1. Mint a fresh **account** setup token (Settings → Local Development, or Cinna
96
+ Desktop does it with its own session).
97
+ 2. Inside the account workspace, `cinna account set-token <token-or-url>`
98
+ re-exchanges it under the **stored** machine name and swaps only
99
+ `account_token` (plus a server-refreshed platform / frontend URL) into
100
+ `.cinna/account.json`. The active user workspace, machine name, `context/`
101
+ and every child under `agents/` are untouched — the same in-place contract
102
+ `cinna login` gives, minus the browser. A bare token reuses the stored
103
+ platform URL.
104
+ 3. The new token must be for the **same account**: a different platform origin,
105
+ or a different `sub` on the token, is refused (exit 11) and nothing is written.
106
+ 4. Child tokens are not touched; `cinna doctor` re-mints expired ones as usual.
107
+
108
+ ### Driven by Cinna Desktop (no terminal)
109
+ 1. The desktop obtains a setup token from the platform with its own session and
110
+ spawns `cinna account setup "<setup_command>" --dir <absolute> --name <machine>
111
+ --no-input --json`.
112
+ 2. `--no-input` guarantees the process never waits for a human: every prompt
113
+ takes its default (machine name, folder), or fails with the machine code
114
+ `needs_input`. `CINNA_NO_INPUT=1` does the same for any command. `--json`
115
+ implies it.
116
+ 3. `--json` turns the output into one JSON object per line on stdout: progress
117
+ lines `{step, total, status: start|ok|warn|fail, message}` mirroring the
118
+ numbered steps, then exactly one final line — `{"result": "ok", …}` with the
119
+ workspace path, platform / frontend URLs, machine name and context-package
120
+ outcome, or `{"result": "error", "code", "detail"}`. Nothing else touches
121
+ stdout; logs stay in `cinna.log`.
122
+ 4. The process exit code is stable: `0` ok · `10` setup token invalid / expired /
123
+ already used · `11` the token is for another account · `12` the platform is
124
+ unreachable or answered 5xx · `1` anything else, with a specific `code`
125
+ (`workspace_exists`, `needs_input`, `mutagen_missing`, `mutagen_mismatch`,
126
+ `not_an_account_workspace`, …) · `2` bad invocation.
127
+ 5. Later the desktop runs `cinna account set-token "<setup_command>" --no-input
128
+ --json` to refresh silently, and `cinna account status --json` to read token
129
+ validity, synced agents, context-package freshness and whether the installed
130
+ cinna-cli matches the version the platform pins.
131
+
82
132
  ### Discover and attach agents
83
133
  1. `cinna account agents` lists the agents the account can access — name + id,
84
134
  build rights (foreign bundle installs are view-only), whether the remote env
@@ -126,7 +176,25 @@ each with its own token, registry entry, and Mutagen session.
126
176
  sync/exec always use the per-agent child token via the per-agent client.
127
177
  - **Single-use setup token is guarded before it's burned.** `cinna account setup`
128
178
  refuses an existing-workspace target *before* exchanging the token, so a doomed
129
- run never wastes the one-time token.
179
+ run never wastes the one-time token (exit 1, code `workspace_exists`).
180
+ - **`set-token` is account-bound, fail-loud.** The refreshed token must match
181
+ the workspace's platform origin and (when both tokens carry one) the same
182
+ subject; otherwise nothing is written and the command exits 11
183
+ (`account_mismatch`). It never rebinds a workspace to another account.
184
+ - **Never hang without a terminal.** Under `--no-input` (or `CINNA_NO_INPUT=1`,
185
+ or `--json`) no command blocks on stdin: prompts take their default or fail
186
+ with `needs_input`; confirmations take their default (usually "No", which
187
+ aborts). The `/dev/tty` fallback the `curl | python3` bootstrap uses for the
188
+ folder prompt is skipped too.
189
+ - **`--json` is a pure stream.** Rich output is suppressed entirely; each stdout
190
+ line is one JSON object and the last one is always the `result` line, on
191
+ success and on failure alike. Human output is unchanged when the flag is off.
192
+ - **Exit codes are a contract.** 0 / 10 / 11 / 12 / 1 (+ `code`) / 2 as listed
193
+ in the desktop flow; every error the CLI raises on purpose maps onto it, so a
194
+ driver switches on the number, not on message text.
195
+ - **Version pin is advisory.** `account status` compares the running cinna-cli
196
+ with the version the platform publishes in its discovery document; a missing
197
+ pin is `unknown`, never an error.
130
198
  - **Fail-loud on backend errors.** Token exchange, mint, and every account call
131
199
  surface the backend's `detail` verbatim (expired/used token, foreign-install
132
200
  403, ambiguous agent). The CLI does not pre-judge build rights client-side —
@@ -173,7 +241,11 @@ cinna account setup ────────────────────
173
241
  ▼
174
242
  .cinna/account.json (account CLI token, cli-account scope)
175
243
  │
244
+ ├── cinna account set-token <token> ───────────────────► POST /cli-setup/account/<token>
245
+ │ (stored machine name; same-account check; rewrites account_token only)
246
+ ├── cinna login (device flow) ─────────────────────────► /api/v1/cli/account/login/*
176
247
  ├── cinna account agents / status / refresh-context ─► AccountClient ─► /api/v1/cli/account/*
248
+ │ (status: + GET /.well-known/cinna-desktop for the cinna-cli pin)
177
249
  ├── cinna account user-workspace … (client-side active selection)
178
250
  ├── cinna account credentials … (metadata-only drafts)
179
251
  │
@@ -205,11 +277,17 @@ cinna account setup ────────────────────
205
277
  publisher-install flag its ownership step depends on. See
206
278
  [improvement_requests](../improvement_requests/improvement_requests.md).
207
279
  - **Doctor / login** — `cinna doctor` re-mints expired per-agent tokens through
208
- the account token, and groups blocked agents under a single `cinna login` hint
209
- when the account token itself has expired.
280
+ the account token, and groups blocked agents under a single `cinna login` /
281
+ `cinna account set-token` hint when the account token itself has expired. It
282
+ also reports a cinna-cli that differs from the platform's pin.
283
+ - **Bootstrap / onboarding** — the no-terminal contract (`--no-input`, `--json`,
284
+ exit codes, `CINNA_MUTAGEN_BIN`) is CLI-wide and described in
285
+ [Bootstrap & Onboarding](../bootstrap_onboarding/bootstrap_onboarding.md);
286
+ this doc covers how the account verbs use it.
287
+ - **Cinna Desktop** — installs its own pinned cinna-cli (with uv and Mutagen) and
288
+ drives the three verbs above; the desktop-side plan lives in the cinna-desktop
289
+ repo (`plans/one-click-onboarding-desktop.md`). <!-- nocheck: cross-repo path -->
210
290
 
211
291
  Implementation: see [account_workspace_tech.md](account_workspace_tech.md).
212
292
  Real-usage e2e test scenarios: see
213
293
  [account_workspace_acceptance.md](account_workspace_acceptance.md).
214
- </content>
215
- </invoke>
@@ -26,8 +26,10 @@ the silent-secret and scope-drift bugs live.
26
26
  - **Editable install** of the CLI under test:
27
27
  `python3 -c "import cinna,os;print(os.path.dirname(cinna.__file__))"` must point
28
28
  at this repo's `src/cinna`. Confirm `which cinna` resolves and
29
- `cinna account --help` lists `setup / agents / status / refresh-context /
30
- user-workspace / credentials`.
29
+ `cinna account --help` lists `setup / set-token / agents / status /
30
+ refresh-context / user-workspace / credentials`.
31
+ - For the no-terminal scenarios (15–18): `jq` to inspect the JSON lines, and a
32
+ second **account** setup token per run (every exchange burns one).
31
33
  - `git` and `mutagen` on `PATH` (needed once a child agent is synced and
32
34
  exercised).
33
35
 
@@ -253,8 +255,114 @@ the silent-secret and scope-drift bugs live.
253
255
  - **Watch for:** user files deleted; the registry entry surviving; a revoke error
254
256
  aborting the command.
255
257
 
258
+ ### 15. Desktop-style setup: absolute `--dir`, `--no-input`, `--json`, no TTY
259
+
260
+ - **Goal:** Cinna Desktop creates the account workspace as a child process with
261
+ no terminal and parses the result.
262
+ - **Setup:** a fresh account setup token; note the `setup_command` string the
263
+ platform returns (`curl -sL …/api/cli-setup/account/<TOKEN> | python3 -`).
264
+ - **Steps:**
265
+ ```
266
+ TARGET="$HOME/CinnaAgents/Cloud/localhost" # parents must not exist yet
267
+ cinna account setup 'curl -sL http://localhost:8000/api/cli-setup/account/<TOKEN> | python3 -' \
268
+ --dir "$TARGET" --name desktop-test --no-input --json < /dev/null > out.jsonl; echo "exit $?"
269
+ cat out.jsonl | jq -c .
270
+ ls -a "$TARGET"; cat "$TARGET/.cinna/account.json"
271
+ ```
272
+ - **Expected:** exit `0`. Every line of `out.jsonl` parses; the first three are
273
+ `{"step":1..3,"total":3,"status":"start",…}`, the last is `{"result":"ok",
274
+ "workspace":"<TARGET>","platform_url":…,"frontend_url":…,"machine_name":
275
+ "desktop-test","context_package":"ok"}`. The workspace sits exactly at
276
+ `$TARGET` (dots in the host kept, parents created), with the same files as
277
+ scenario 1. No table, hint or spinner text on stdout.
278
+ - **Watch for:** the workspace landing under cwd instead of the absolute path;
279
+ a non-JSON line on stdout (Rich leaking); the process waiting on stdin
280
+ (folder / machine-name prompt); `context_package` not reflecting a failed
281
+ download (should be `failed` with a preceding `"status":"warn"` line, exit
282
+ still 0).
283
+
284
+ ### 16. Desktop-style refresh: `account set-token` in place, same account only
285
+
286
+ - **Goal:** the desktop refreshes an expiring account token silently; a token
287
+ for another account can never be swapped in.
288
+ - **Setup:** scenario 15's workspace; `cinna account user-workspace activate
289
+ <name>` so a client-side selection exists; one synced child (scenario 5).
290
+ Mint two fresh account setup tokens: one as the **same** user, one as a
291
+ **different** user on the same platform.
292
+ - **Steps:**
293
+ ```
294
+ cd "$TARGET"; cp .cinna/account.json /tmp/before.json
295
+ cinna account set-token 'curl -sL http://localhost:8000/api/cli-setup/account/<SAME_USER_TOKEN> | python3 -' \
296
+ --no-input --json < /dev/null; echo "exit $?"
297
+ diff <(jq 'del(.account_token)' /tmp/before.json) <(jq 'del(.account_token)' .cinna/account.json)
298
+ cat agents/<slug>/.cinna/config.json | jq .cli_token # unchanged
299
+ cinna account set-token 'curl -sL http://localhost:8000/api/cli-setup/account/<OTHER_USER_TOKEN> | python3 -' \
300
+ --no-input --json < /dev/null; echo "exit $?"
301
+ cinna account set-token '<SAME_USER_TOKEN again>' --json; echo "exit $?"
302
+ ```
303
+ - **Expected:** first call exits `0`, prints two `start` steps, an `ok` line
304
+ and `{"result":"ok",…,"context_package":"skipped"}`; only `account_token`
305
+ changed (the `diff` is empty), the child token is untouched, and the exchange
306
+ was made with the **stored** machine name (check the CLI token list in
307
+ Settings — no new machine). The other-user call exits `11` with
308
+ `{"result":"error","code":"account_mismatch",…}` and `account.json` is
309
+ unchanged. The reused (already burned) token exits `10` with
310
+ `code: "setup_token_invalid"` and the backend detail.
311
+ - **Watch for:** the mismatch being detected only after the file was written;
312
+ `user_workspace_id` or `machine_name` reset; a new machine appearing in the
313
+ platform's token list; exit `1` where `10` / `11` is required.
314
+
315
+ ### 17. Exit-code contract
316
+
317
+ - **Goal:** a driver can act on the exit code alone.
318
+ - **Steps:** run each from a scratch dir with `--no-input --json < /dev/null`
319
+ and record `$?` plus the final line's `code`:
320
+ ```
321
+ cinna account setup '<ALREADY_USED_TOKEN_URL>' --dir /tmp/x1 # 10 setup_token_invalid
322
+ cinna account setup '<VALID_URL>' --dir "$TARGET" # 1 workspace_exists (token NOT burned)
323
+ cinna account setup 'http://127.0.0.1:1/api/cli-setup/account/X' --dir /tmp/x2 # 12 network
324
+ cinna account status # 1 not_an_account_workspace
325
+ cinna --no-input login # 1 needs_input (no domain, human text)
326
+ cinna account setup # 2 usage
327
+ ```
328
+ Then, with the backend stopped: `cinna account set-token '<URL>' --json` → `12`.
329
+ - **Expected:** the codes in the comments. For the `workspace_exists` case,
330
+ re-use the same valid token afterwards in a fresh dir — it must still work.
331
+ - **Watch for:** any of these coming back as a bare `1`; a traceback instead of
332
+ a JSON error line; the guard case burning the token.
333
+
334
+ ### 18. `account status --json` and the cinna-cli version pin
335
+
336
+ - **Goal:** the desktop reconciles from one status line, including whether to
337
+ reinstall cinna-cli.
338
+ - **Steps:**
339
+ ```
340
+ cd "$TARGET"; cinna account status --json | jq .
341
+ curl -s http://localhost:8000/.well-known/cinna-desktop | jq .local_dev
342
+ cinna account status | tail -20
343
+ cinna doctor --dry-run
344
+ ```
345
+ - **Expected:** exactly one JSON line with `result`, `workspace`,
346
+ `platform_url`, `frontend_url`, `machine_name`, `active_workspace`
347
+ (`{id,name}` or `null`), `token` (`valid|expired|unreachable`),
348
+ `synced_agents`, `agents[]`, `context_package{local,remote,state}` and
349
+ `cli{installed,required,state}`. When the platform publishes
350
+ `local_dev.cinna_cli_version`, `cli.required` equals it and `state` is
351
+ `current` / `behind` / `ahead`; on an older platform `required` is `null`
352
+ and `state` is `unknown` — still exit 0. The human `status` shows the same
353
+ as a `cinna-cli` table row and a `uv tool install cinna-cli==<pin>` hint when
354
+ behind; `doctor` lists the platform under "manual action needed" only when
355
+ the pin differs.
356
+ - **Watch for:** a missing discovery document turning into an error; `doctor`
357
+ nagging when no pin is published; the JSON line missing `cli`.
358
+
256
359
  ## Cross-cutting invariants (must hold across all scenarios)
257
360
 
361
+ - **`--json` stdout is JSON only, last line is the verdict** — every stdout line
362
+ parses; exactly one `result` line; human hints never leak.
363
+ - **Never wait for a human without a terminal** — with `--no-input` / `--json`
364
+ / `CINNA_NO_INPUT=1` every command either proceeds or fails with
365
+ `needs_input`; nothing reads stdin or `/dev/tty`.
258
366
  - **No secret ever sent** — credential create/update bodies carry only metadata;
259
367
  the CLI cannot read or write a credential's secret value.
260
368
  - **Account token stays account-scoped** — it is used only on `/account/*`; every
@@ -284,4 +392,3 @@ the silent-secret and scope-drift bugs live.
284
392
  - Verify `~/.cinna/agents.json` has no leftover entries for the test children —
285
393
  especially if any helper ran **outside** pytest's global-state isolation (it
286
394
  would write the real registry).
287
- </content>