cinna-cli 0.3.0__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.
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/PKG-INFO +22 -5
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/README.md +21 -4
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/README.md +11 -5
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace.md +85 -7
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace_acceptance.md +110 -3
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace_tech.md +92 -12
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding.md +44 -6
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_acceptance.md +45 -1
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/bootstrap_onboarding/bootstrap_onboarding_tech.md +97 -6
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/pyproject.toml +1 -1
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/account.py +228 -30
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/bootstrap.py +1 -2
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/chat.py +1 -1
- cinna_cli-0.4.0/src/cinna/cli_version.py +111 -0
- cinna_cli-0.4.0/src/cinna/console.py +187 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/doctor.py +46 -3
- cinna_cli-0.4.0/src/cinna/errors.py +212 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/local_import.py +1 -1
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/main.py +163 -25
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/mutagen_runtime.py +40 -9
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/sync_session.py +2 -1
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/sync_tui.py +6 -9
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/conftest.py +10 -0
- cinna_cli-0.4.0/tests/test_cli_version.py +55 -0
- cinna_cli-0.4.0/tests/test_onboarding.py +800 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/uv.lock +1 -1
- cinna_cli-0.3.0/src/cinna/console.py +0 -39
- cinna_cli-0.3.0/src/cinna/errors.py +0 -66
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/.claude/commands/cinna-cli.feature.doc.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/.github/workflows/publish.yml +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/.gitignore +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/LICENSE.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_api/agent_api.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_api/agent_api_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_api/agent_api_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_management/agent_management.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_management/agent_management_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_management/agent_management_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_schedules/agent_schedules.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_schedules/agent_schedules_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/agent_schedules/agent_schedules_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/doctor/doctor.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/doctor/doctor_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/doctor/doctor_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/git_versioning/git_versioning.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/git_versioning/git_versioning_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/git_versioning/git_versioning_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/improvement_requests/improvement_requests.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/improvement_requests/improvement_requests_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/improvement_requests/improvement_requests_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/live_sync/live_sync.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/live_sync/live_sync_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/live_sync/live_sync_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/local_agent_import/local_agent_import.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/local_agent_import/local_agent_import_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/local_agent_import/local_agent_import_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/mcp_integration/mcp_integration.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/mcp_integration/mcp_integration_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/mcp_integration/mcp_integration_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/remote_chat/remote_chat.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/remote_chat/remote_chat_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/remote_chat/remote_chat_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/remote_exec/remote_exec.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/remote_exec/remote_exec_acceptance.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/remote_exec/remote_exec_tech.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/interface.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/mutagen_capabilities.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/scripts/check_docs_references.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/__init__.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/auth.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/client.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/config.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/context.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/git_versioning.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/improve.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/kit_contract.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/logging.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/mcp_proxy.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/sync.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/sync_ssh_shim.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/templates/CHAT_TESTING.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/templates/CLAUDE.md.template +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/templates/GIT_VERSIONING.md +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/src/cinna/templates/__init__.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/__init__.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_account.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_auth.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_bootstrap.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_chat.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_client.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_config.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_context.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_doctor.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_git_versioning.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_improve.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_kit_contract.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_local_import.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_main.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_mutagen_runtime.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_sync.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.0}/tests/test_sync_session.py +0 -0
- {cinna_cli-0.3.0 → cinna_cli-0.4.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.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
|
|
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
|
|
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
|
|
|
@@ -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
|
|
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
|
|
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
|
|
|
@@ -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
|
|
@@ -208,16 +209,17 @@ main.py (CLI commands — Click)
|
|
|
208
209
|
├── config.py — .cinna/config.json: load/save/find
|
|
209
210
|
├── auth.py — JWT storage, Authorization headers
|
|
210
211
|
├── client.py — PlatformClient: HTTP + SSE stream_exec
|
|
211
|
-
├── 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
|
|
212
214
|
├── sync_session.py — wrap the `mutagen` CLI (start/stop/status/conflicts)
|
|
213
215
|
├── sync_tui.py — live Textual TUI shown by `cinna dev` (Sync/Details/Conflicts tabs)
|
|
214
216
|
├── sync_ssh_shim.py — `cinna-sync-ssh` entry point (WebSocket transport)
|
|
215
217
|
├── sync.py — tarball/zip extraction helpers (initial clone only)
|
|
216
218
|
├── context.py — CLAUDE.md, BUILDING_AGENT.md, .mcp.json, opencode.json
|
|
217
219
|
├── mcp_proxy.py — MCP stdio server for knowledge_query
|
|
218
|
-
├── console.py — Rich helpers
|
|
220
|
+
├── console.py — Rich helpers + the `--json` / `--no-input` switches (prompt/confirm wrappers, JSON lines)
|
|
219
221
|
├── logging.py — cinna.log (rotating file handler)
|
|
220
|
-
└── errors.py — exception hierarchy
|
|
222
|
+
└── errors.py — exception hierarchy; `CinnaExit` = stable exit code + machine code
|
|
221
223
|
```
|
|
222
224
|
|
|
223
225
|
### Local Directory Layout
|
|
@@ -282,8 +284,8 @@ authoring convention.
|
|
|
282
284
|
|
|
283
285
|
| Feature | Command surface | Docs |
|
|
284
286
|
|---|---|---|
|
|
285
|
-
| **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) |
|
|
286
|
-
| **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) |
|
|
287
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) |
|
|
288
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) |
|
|
289
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) |
|
|
@@ -605,6 +607,8 @@ When a CLI token expires (or is revoked) the normal remedy is `cinna set-token <
|
|
|
605
607
|
| Method | Route | Auth | Purpose |
|
|
606
608
|
|--------|-------|------|---------|
|
|
607
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") |
|
|
608
612
|
| GET | `/api/v1/cli/agents/{id}/workspace` | CLI JWT | One-shot tarball for initial clone |
|
|
609
613
|
| GET | `/api/v1/cli/agents/{id}/building-context` | CLI JWT | Assembled building prompt |
|
|
610
614
|
| POST | `/api/v1/cli/agents/{id}/knowledge/search` | CLI JWT | Knowledge base search (via MCP proxy) |
|
|
@@ -645,6 +649,8 @@ uv run ruff format --check src/
|
|
|
645
649
|
|
|
646
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.
|
|
647
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
|
+
|
|
648
654
|
### Cutting a release
|
|
649
655
|
|
|
650
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)
|
|
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`
|
|
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>
|
{cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace_acceptance.md
RENAMED
|
@@ -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 /
|
|
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>
|
{cinna_cli-0.3.0 → cinna_cli-0.4.0}/docs/features/account_workspace/account_workspace_tech.md
RENAMED
|
@@ -29,15 +29,25 @@ and tests in `tests/test_account.py`.
|
|
|
29
29
|
- `src/cinna/mcp_proxy.py` — account-mode knowledge proxy
|
|
30
30
|
(`run_mcp_proxy` / `_resolve_proxy_context` / `create_account_mcp_server`)
|
|
31
31
|
wired by the account `.mcp.json`.
|
|
32
|
-
- `src/cinna/errors.py` — `
|
|
33
|
-
|
|
32
|
+
- `src/cinna/errors.py` — `CinnaExit` (stable exit code + machine `code`) and
|
|
33
|
+
its subclasses used here: `SetupTokenError` (10), `AccountMismatchError` (11),
|
|
34
|
+
`NetworkError` / `PlatformError` 5xx (12), `WorkspaceExistsError`,
|
|
35
|
+
`AccountConfigNotFoundError`.
|
|
36
|
+
- `src/cinna/console.py` — the `json_mode` / `no_input` switches, the JSON line
|
|
37
|
+
writer (`emit_json` / `emit_result`) and the `prompt` / `confirm` /
|
|
38
|
+
`interactive` wrappers every prompt site goes through.
|
|
39
|
+
- `src/cinna/cli_version.py` — installed-vs-pinned cinna-cli version
|
|
40
|
+
(`cli_version_status`, `fetch_required_cli_version`).
|
|
34
41
|
- `src/cinna/templates/ACCOUNT_CLAUDE.md.template` — the orchestrator
|
|
35
42
|
`CLAUDE.md` source.
|
|
36
43
|
- Tests: `tests/test_account.py` (setup, refresh-context, agents listing +
|
|
37
44
|
workspace scoping, status, agent sync/unsync, exec `--agent`, child-workspace
|
|
38
45
|
resolution incl. multi-segment subdir, user-workspace, credentials,
|
|
39
|
-
`AccountClient` HTTP-level), `tests/test_client.py` (account client),
|
|
40
|
-
MCP-proxy account-mode tests in `tests/test_account.py
|
|
46
|
+
`AccountClient` HTTP-level), `tests/test_client.py` (account client), the
|
|
47
|
+
MCP-proxy account-mode tests in `tests/test_account.py`, and
|
|
48
|
+
`tests/test_onboarding.py` (the driver contract: exit codes, absolute `--dir`,
|
|
49
|
+
`account set-token`, `--no-input`, `--json` line snapshots, version pin in
|
|
50
|
+
status / doctor).
|
|
41
51
|
|
|
42
52
|
## Command surface
|
|
43
53
|
|
|
@@ -46,6 +56,8 @@ Each verb → its handler in `src/cinna/main.py` → the body in
|
|
|
46
56
|
|
|
47
57
|
- `cinna account setup` → `src/cinna/main.py:account_setup()` →
|
|
48
58
|
`src/cinna/account.py:run_account_setup()`
|
|
59
|
+
- `cinna account set-token` → `src/cinna/main.py:account_set_token()` →
|
|
60
|
+
`src/cinna/account.py:run_account_set_token()`
|
|
49
61
|
- `cinna account agents` → `src/cinna/main.py:account_agents()` →
|
|
50
62
|
`src/cinna/account.py:run_account_agents()`
|
|
51
63
|
- `cinna account status` → `src/cinna/main.py:account_status()` →
|
|
@@ -80,6 +92,14 @@ Each verb → its handler in `src/cinna/main.py` → the body in
|
|
|
80
92
|
`src/cinna/main.py:account_credentials_share_with_agent()` →
|
|
81
93
|
`src/cinna/account.py:run_credentials_share()`
|
|
82
94
|
|
|
95
|
+
`setup`, `set-token` and `status` also take `--no-input` and `--json`
|
|
96
|
+
(`src/cinna/main.py:no_input_option()` / `json_option()` — eager, value-less
|
|
97
|
+
options whose callbacks flip `src/cinna/console.py:set_no_input()` /
|
|
98
|
+
`set_json_mode()`); the root group takes `--no-input` too (also
|
|
99
|
+
`CINNA_NO_INPUT=1`). The per-command copy exists because
|
|
100
|
+
`ignore_unknown_options` + the `nargs=-1` setup argument would otherwise
|
|
101
|
+
swallow a flag placed after the subcommand, which is how the desktop invokes it.
|
|
102
|
+
|
|
83
103
|
Related (documented here as integration points): `cinna agent sync` →
|
|
84
104
|
`src/cinna/account.py:run_agent_sync()`, `cinna agent unsync` →
|
|
85
105
|
`run_agent_unsync()`, `cinna login` → `run_login()`.
|
|
@@ -99,10 +119,32 @@ Related (documented here as integration points): `cinna agent sync` →
|
|
|
99
119
|
bare token falls back to `CINNA_PLATFORM_URL`.
|
|
100
120
|
- `src/cinna/account.py:default_account_dir_name()` — derives the default folder
|
|
101
121
|
from the platform host (collapses non-`[A-Za-z0-9-]` to `_`).
|
|
102
|
-
- `src/cinna/account.py:
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
122
|
+
- `src/cinna/account.py:resolve_account_dir()` — the `--dir` contract: absolute
|
|
123
|
+
(after `~` expansion) → as is, relative → under cwd. No symlink resolution, so
|
|
124
|
+
the path reported back equals the one passed.
|
|
125
|
+
- `src/cinna/account.py:run_account_setup()` — parse → derive/prompt the dir
|
|
126
|
+
(`_prompt_account_dir()` returns the default outright under `no_input`) →
|
|
127
|
+
`resolve_account_dir()` → **guard the target before**
|
|
128
|
+
`_exchange_account_setup_token()` (`WorkspaceExistsError`) → build
|
|
129
|
+
`AccountConfig` → `_write_account_files()` (creates parents) → best-effort
|
|
130
|
+
`_install_context_package()` → `console.emit_result()` with
|
|
131
|
+
`_account_result_fields()`.
|
|
132
|
+
- `src/cinna/account.py:_exchange_account_setup_token()` — the exchange with the
|
|
133
|
+
exit-code mapping: transport error → `NetworkError` (12), 5xx →
|
|
134
|
+
`PlatformError` (12), any other non-200 → `SetupTokenError` (10, backend
|
|
135
|
+
detail verbatim, `http_status` in the JSON error line).
|
|
136
|
+
- `src/cinna/account.py:run_account_set_token()` — `find_account_root()` →
|
|
137
|
+
`parse_account_setup_input(…, fallback_platform_url=<stored>/api)` →
|
|
138
|
+
exchange under the **stored** `machine_name` → `_same_origin()` check on
|
|
139
|
+
`platform_url` and `_jwt_claims()` `sub` comparison (both raise
|
|
140
|
+
`AccountMismatchError`, 11, before any write) → rewrite `account_token` +
|
|
141
|
+
refreshed `platform_url` / `frontend_url` → `save_account_config()`.
|
|
142
|
+
`user_workspace_*`, `machine_name`, `context/`, children untouched.
|
|
143
|
+
- `src/cinna/account.py:_jwt_claims()` — unverified base64 decode of a JWT
|
|
144
|
+
payload, only to compare `sub`; opaque tokens yield `None` (no comparison).
|
|
145
|
+
- `src/cinna/account.py:_account_result_fields()` — the shared `--json` final
|
|
146
|
+
line of setup / set-token (`workspace`, `platform_url`, `frontend_url`,
|
|
147
|
+
`machine_name`, `context_package: ok|failed|skipped`).
|
|
106
148
|
- `src/cinna/account.py:_write_account_files()` — mkdir + `save_account_config` +
|
|
107
149
|
`agents/` + `_write_account_claude_md` + `_write_account_claude_settings` +
|
|
108
150
|
`_write_account_mcp_config`.
|
|
@@ -118,7 +160,15 @@ Related (documented here as integration points): `cinna agent sync` →
|
|
|
118
160
|
**client-side** scope to `user_workspace_id` (unless `--all`), and annotate each
|
|
119
161
|
row with the local checkout from `list_child_workspaces()`.
|
|
120
162
|
- `src/cinna/account.py:run_account_status()` — `probe_account_token()` +
|
|
121
|
-
`
|
|
163
|
+
`context_package_status()` + `cli_version_status()`; in JSON mode emits the
|
|
164
|
+
single `result` line (`token`, `active_workspace`, `synced_agents`, `agents[]`,
|
|
165
|
+
`context_package{local,remote,state}`, `cli{installed,required,state}`) and
|
|
166
|
+
returns; otherwise the Rich table + `_synced_agents_table()` +
|
|
167
|
+
`_print_token_reauth_hint()` (which now names `cinna account set-token` next
|
|
168
|
+
to `cinna login`).
|
|
169
|
+
- `src/cinna/cli_version.py:cli_version_status()` — `GET
|
|
170
|
+
{origin}/.well-known/cinna-desktop` → `local_dev.cinna_cli_version`, compared
|
|
171
|
+
with the running `__version__` (`current` / `behind` / `ahead` / `unknown`).
|
|
122
172
|
- `src/cinna/account.py:probe_account_token()` — cheap `GET /account/agents`;
|
|
123
173
|
2xx → valid, 401 → expired, else → unreachable.
|
|
124
174
|
- `src/cinna/account.py:run_agent_sync()` — resolve via `_resolve_account_agent`,
|
|
@@ -185,7 +235,14 @@ All consumed by `src/cinna/client.py:AccountClient` with the account token
|
|
|
185
235
|
- `POST /cli-setup/account/{token}` — exchange the account setup token
|
|
186
236
|
(`src/cinna/account.py:_exchange_account_setup_token()`, plain `httpx.post`, not
|
|
187
237
|
the client). Body: `{machine_name, machine_info}`. Returns `account_token`,
|
|
188
|
-
`platform_url`, `frontend_url`, `machine_name`.
|
|
238
|
+
`platform_url`, `frontend_url`, `machine_name`. Used by both `account setup`
|
|
239
|
+
and `account set-token`; the CLI relies on the response being identical for a
|
|
240
|
+
first and a repeat exchange (no `user`/owner field is required — the
|
|
241
|
+
same-account check works from the origin and the token's own `sub`).
|
|
242
|
+
- `GET /.well-known/cinna-desktop` (unauthenticated, platform origin) — the
|
|
243
|
+
desktop discovery document; `local_dev.cinna_cli_version` is the cinna-cli
|
|
244
|
+
pin (`src/cinna/cli_version.py:fetch_required_cli_version()`). Absent block,
|
|
245
|
+
404 or no network → `unknown`, silently.
|
|
189
246
|
- `GET /api/v1/cli/account/agents` — accessible agents (`list_account_agents`;
|
|
190
247
|
also the token probe).
|
|
191
248
|
- `POST /api/v1/cli/account/agents/{id}/mint` — mint a per-agent child token
|
|
@@ -221,7 +278,31 @@ All consumed by `src/cinna/client.py:AccountClient` with the account token
|
|
|
221
278
|
- **Guard before burning the setup token** — `run_account_setup` checks the
|
|
222
279
|
target dir exists-check *before* calling `_exchange_account_setup_token`, so a
|
|
223
280
|
doomed run never spends the single-use token.
|
|
224
|
-
(`tests/test_account.py:test_account_setup_refuses_existing_workspace
|
|
281
|
+
(`tests/test_account.py:test_account_setup_refuses_existing_workspace`,
|
|
282
|
+
`tests/test_onboarding.py:test_setup_existing_absolute_dir_is_workspace_exists`)
|
|
283
|
+
- **Absolute `--dir` is used as is, parents created** — `resolve_account_dir()`;
|
|
284
|
+
the `workspace` reported in JSON is the path the caller passed.
|
|
285
|
+
(`tests/test_onboarding.py:test_setup_absolute_dir_used_as_is_and_parents_created`)
|
|
286
|
+
- **`set-token` never rebinds** — the origin / `sub` checks precede the write;
|
|
287
|
+
on mismatch `account.json` is byte-identical afterwards.
|
|
288
|
+
(`tests/test_onboarding.py:test_account_set_token_platform_mismatch_exits_11`,
|
|
289
|
+
`test_account_set_token_subject_mismatch_exits_11`)
|
|
290
|
+
- **`set-token` preserves everything but the token** — active user workspace,
|
|
291
|
+
machine name and child configs survive.
|
|
292
|
+
(`tests/test_onboarding.py:test_account_set_token_swaps_token_in_place`)
|
|
293
|
+
- **Exit codes are mapped centrally** — `src/cinna/main.py:CinnaGroup.invoke()`
|
|
294
|
+
wraps plain `ClickException`s, `httpx.TransportError`s and (in JSON mode)
|
|
295
|
+
unexpected exceptions into `CinnaExit`; `CinnaExit.show()` prints the JSON
|
|
296
|
+
error line instead of `Error: …` when `json_mode` is on.
|
|
297
|
+
(`tests/test_onboarding.py`, the "exit codes" block)
|
|
298
|
+
- **`--json` stdout is JSON only** — `set_json_mode()` swaps the Rich console
|
|
299
|
+
for a quiet one; `spinner()` is a no-op; every stdout line must parse.
|
|
300
|
+
(`tests/test_onboarding.py:test_setup_json_progress_and_result`,
|
|
301
|
+
`test_status_json`)
|
|
302
|
+
- **`--no-input` never blocks** — `_prompt_account_dir()` short-circuits, the
|
|
303
|
+
machine-name prompt takes its default, `console.confirm()` returns the
|
|
304
|
+
default. (`tests/test_onboarding.py:test_no_input_after_subcommand_skips_dir_prompt`,
|
|
305
|
+
`test_group_level_no_input_fails_needs_input`)
|
|
225
306
|
- **Context refresh is non-destructive** — `_install_context_package(replace=True)`
|
|
226
307
|
removes `context/` only after a successful download; the orchestrator/child
|
|
227
308
|
`CLAUDE.md` regeneration is independent of the download.
|
|
@@ -252,4 +333,3 @@ All consumed by `src/cinna/client.py:AccountClient` with the account token
|
|
|
252
333
|
- **Safe extraction** — the context tarball reuses the path-traversal/absolute/
|
|
253
334
|
symlink-rejecting extractor.
|
|
254
335
|
(`tests/test_account.py:test_context_extraction_rejects_malicious_members`)
|
|
255
|
-
</content>
|