cinna-cli 0.2.1__tar.gz → 0.2.3__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 (48) hide show
  1. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/PKG-INFO +71 -1
  2. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/README.md +70 -0
  3. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/docs/README.md +48 -0
  4. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/pyproject.toml +1 -1
  5. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/account.py +354 -11
  6. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/bootstrap.py +1 -0
  7. cinna_cli-0.2.3/src/cinna/chat.py +476 -0
  8. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/client.py +166 -22
  9. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/context.py +27 -0
  10. cinna_cli-0.2.3/src/cinna/doctor.py +454 -0
  11. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/main.py +444 -59
  12. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/sync_session.py +30 -0
  13. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/templates/ACCOUNT_CLAUDE.md.template +14 -1
  14. cinna_cli-0.2.3/src/cinna/templates/CHAT_TESTING.md +56 -0
  15. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/templates/CLAUDE.md.template +4 -1
  16. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_account.py +214 -0
  17. cinna_cli-0.2.3/tests/test_chat.py +406 -0
  18. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_context.py +7 -0
  19. cinna_cli-0.2.3/tests/test_doctor.py +245 -0
  20. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/uv.lock +1 -1
  21. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/.github/workflows/publish.yml +0 -0
  22. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/.gitignore +0 -0
  23. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/LICENSE.md +0 -0
  24. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/docs/interface.md +0 -0
  25. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/docs/mutagen_capabilities.md +0 -0
  26. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/__init__.py +0 -0
  27. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/auth.py +0 -0
  28. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/config.py +0 -0
  29. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/console.py +0 -0
  30. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/errors.py +0 -0
  31. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/logging.py +0 -0
  32. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/mcp_proxy.py +0 -0
  33. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/mutagen_runtime.py +0 -0
  34. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/sync.py +0 -0
  35. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/sync_ssh_shim.py +0 -0
  36. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/sync_tui.py +0 -0
  37. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/src/cinna/templates/__init__.py +0 -0
  38. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/__init__.py +0 -0
  39. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/conftest.py +0 -0
  40. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_auth.py +0 -0
  41. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_bootstrap.py +0 -0
  42. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_client.py +0 -0
  43. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_config.py +0 -0
  44. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_main.py +0 -0
  45. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_mutagen_runtime.py +0 -0
  46. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_sync.py +0 -0
  47. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_sync_session.py +0 -0
  48. {cinna_cli-0.2.1 → cinna_cli-0.2.3}/tests/test_sync_ssh_shim.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cinna-cli
3
- Version: 0.2.1
3
+ Version: 0.2.3
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
@@ -112,6 +112,31 @@ cd hr-manager-agent/
112
112
  cinna set-token yWo36tbkdAOzrALxOEKq31_OA2iMelEg
113
113
  ```
114
114
 
115
+ ### `cinna login [domain]`
116
+
117
+ Sign in to an account workspace in the browser — **no setup token to paste**. One command serves two cases:
118
+
119
+ - **Resume** — run it from inside an existing account workspace and it refreshes the stored account CLI token **in place** (reusing the platform URL + machine name; other settings like the active user workspace are preserved).
120
+ - **Connect new** — run it anywhere else and it bootstraps a fresh account workspace. It asks for the platform **domain** (or pass it as an argument — protocol optional, `localhost` recognized), then creates the workspace **in the current folder if it's empty**, or **in a subfolder you name** if it isn't. So you can `mkdir my-cinna && cd my-cinna && cinna login`, or just `cinna login app.example.com` straight into the current directory.
121
+
122
+ Either way it opens a browser authorization URL (OAuth 2.0 device flow); once you click **Authorize** (already signed in to the platform) the CLI receives a fresh token and writes `.cinna/account.json`.
123
+
124
+ ```bash
125
+ # Resume the account workspace you're in:
126
+ cd my-cinna/ && cinna login
127
+
128
+ # Connect a new account from a fresh folder:
129
+ mkdir my-cinna && cd my-cinna && cinna login # prompts for the domain
130
+ cinna login app.example.com # or pass it directly
131
+ cinna login app.example.com --dir my-cinna # always into a named subfolder
132
+ # Your verification code: WX7K-9Q2P
133
+ # Open this URL and click Authorize:
134
+ # https://app.example.com/device?code=WX7K-9Q2P
135
+ # ✓ Account workspace ready.
136
+ ```
137
+
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.
139
+
115
140
  ### `cinna account setup <token_or_url>`
116
141
 
117
142
  Initialize an **account workspace** — a multi-agent root from which you can discover your agents and attach per-agent workspaces without going back to the UI. Setup is initiated from **Settings → Channels → Local Development** on the platform, which emits a `curl | python3` one-liner (same pattern as the per-agent flow):
@@ -230,6 +255,30 @@ cinna api PATCH agents/3fa85f64-5717-4562-b3fc-2c963f66afa6 --json '{"descriptio
230
255
  cinna api POST tasks --data @task.json
231
256
  ```
232
257
 
258
+ ### `cinna chat [--agent <ref>] [--resume <session_id>] [--file PATH ...] [MESSAGE...]`
259
+
260
+ Talk to an agent through a **real platform session** — the same conversation pipeline production uses (permission checks, agent-env calls, the model/SDK the platform selects), not a local mock. Built for a local coding agent to test the agent it is building: it can prepare a prompt, attach files, and read the reply back as structured data.
261
+
262
+ Run it from the account workspace (or any synced agent folder under it). The reply is observed by **polling** the backend rather than reading a live stream, so it is robust to streaming/transport quirks.
263
+
264
+ - `--agent <name|slug|id>` picks the agent; omit it inside a synced agent workspace to infer it. `--resume <session_id>` continues an existing conversation instead of opening a new one (default mode for a new session is `conversation`; `--mode building` opens a building session).
265
+ - The message is the positional argument; if omitted it is read from stdin, or you are prompted for it interactively in a TTY.
266
+ - `--file PATH` (repeatable) uploads a local file and attaches it to the message.
267
+ - Output is **NDJSON** by default — one JSON event per line (`session`, `upload`, `message`, `status`, `done`), trivially parseable by another agent. `--pretty` switches to a human-readable transcript.
268
+ - Each `message` carries the agent's reasoning/tool trace under **`events`** — an ordered list of the `thinking` blocks, `tool` calls (with their full `tool_input` payload) and tool results behind the reply, so you see *what the agent did*, not just its final `content`. Pass `--no-events` to drop the trace and keep only the final text.
269
+ - Files the agent attaches to its replies are downloaded under `./cinna-chat-files/<session_id>/` (override with `--download-dir`, or skip with `--no-download` to just report the file ids). Downloads are bounded by the api-proxy's 8 MiB response cap.
270
+ - `--interval` / `--timeout` tune the poll cadence and the maximum wait for a turn. Ctrl-C interrupts the agent's turn and exits.
271
+
272
+ ```bash
273
+ cinna chat --agent crm-agent "Summarize today's leads"
274
+ cinna chat --agent crm-agent --file report.csv "Validate this export"
275
+ cinna chat --resume 3fa85f64-5717-4562-b3fc-2c963f66afa6 "Now break it down by region"
276
+ echo "ping" | cinna chat --agent crm-agent # message from stdin
277
+ cinna chat --agent crm-agent "hi" | jq -c 'select(.event=="message")'
278
+ ```
279
+
280
+ The session id is printed in the first `session` event — capture it to drive a multi-turn conversation with `--resume`.
281
+
233
282
  ### `cinna dev`
234
283
 
235
284
  Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
@@ -285,6 +334,27 @@ List every agent registered on this machine (from `~/.cinna/agents.json`). Three
285
334
  2. **Location** — workspace path on top, platform UI link below. Missing directories are flagged in red.
286
335
  3. **Sync** — Mutagen session state on top (`active` / `paused` / `connecting` / `error`), plus a per-agent backend probe (`valid token` / `expired token` / `no connection`) on the bottom. The probes run in parallel with a short timeout so the view stays snappy even with many registered agents.
287
336
 
337
+ ### `cinna doctor`
338
+
339
+ Diagnose and repair stale sync state across the whole machine. Over time the per-user registry (`~/.cinna/agents.json`) and the Mutagen daemon drift out of sync as agents are deleted, environments are spun down, and tokens expire — `cinna doctor` reconciles the two and heals the leftovers in one pass. Run it from anywhere; it is not workspace-scoped.
340
+
341
+ It detects and fixes:
342
+
343
+ - **Deleted workspaces** — registry entries whose workspace folder (or its `.cinna/config.json`) is gone. The entry is removed, along with any leftover Mutagen session.
344
+ - **Halted sessions** — sessions stopped on `halted-on-root-deletion` (the local `workspace/` root was deleted) while the agent dir is otherwise intact. Terminated; `cinna dev` recreates a clean one.
345
+ - **Dead-remote sessions** — sessions stuck retrying a remote env that no longer exists (`connecting-beta` / beta polling error). Mutagen has **no** "give up after N failures" option — a session retries forever until paused or terminated — so `doctor` is the cleanup path for these. Terminated.
346
+ - **Orphaned sessions** — `cinna-*` sessions with no registry entry at all. Terminated.
347
+ - **Expired tokens** — for **account-managed** workspaces (those under an account root), the CLI token is re-minted automatically through the parent account token, no pasting required. **Standalone** workspaces (set up via `cinna setup`) can only be refreshed with a pasted setup token, so they are reported with a `cinna set-token` hint rather than changed.
348
+ - **Expired account token** — a sub-agent token can only be re-minted while the **account** token that mints it is still valid. When the account token has itself expired, doctor probes it once and surfaces a single _"renew the account token — run `cinna login`"_ finding (listing the blocked sub-agents) instead of a pile of re-mints that would all fail with 401. Run `cinna login`, then re-run `cinna doctor` to re-mint the dependents.
349
+
350
+ ```bash
351
+ cinna doctor # diagnose, then apply all fixes behind one confirmation
352
+ cinna doctor --dry-run # report problems only; change nothing
353
+ cinna doctor --yes # apply every fix non-interactively
354
+ ```
355
+
356
+ The diagnosis is split into two tables: **Will fix** (everything doctor can repair — deleted-workspace cleanup, halted/dead/orphaned session termination, account token re-mints) and **No automatic fix — manual action needed** (standalone expired tokens, which need a pasted setup token and are never touched). Everything actionable is applied together behind a single `Apply N fix(es)?` confirmation, so the count always matches the "Will fix" table.
357
+
288
358
  ### `cinna disconnect`
289
359
 
290
360
  Stop sync, remove `.cinna/` config and generated files (`CLAUDE.md`, `BUILDING_AGENT.md`, `.mcp.json`, `opencode.json`, `mutagen.yml`). Workspace files are preserved.
@@ -75,6 +75,31 @@ cd hr-manager-agent/
75
75
  cinna set-token yWo36tbkdAOzrALxOEKq31_OA2iMelEg
76
76
  ```
77
77
 
78
+ ### `cinna login [domain]`
79
+
80
+ Sign in to an account workspace in the browser — **no setup token to paste**. One command serves two cases:
81
+
82
+ - **Resume** — run it from inside an existing account workspace and it refreshes the stored account CLI token **in place** (reusing the platform URL + machine name; other settings like the active user workspace are preserved).
83
+ - **Connect new** — run it anywhere else and it bootstraps a fresh account workspace. It asks for the platform **domain** (or pass it as an argument — protocol optional, `localhost` recognized), then creates the workspace **in the current folder if it's empty**, or **in a subfolder you name** if it isn't. So you can `mkdir my-cinna && cd my-cinna && cinna login`, or just `cinna login app.example.com` straight into the current directory.
84
+
85
+ Either way it opens a browser authorization URL (OAuth 2.0 device flow); once you click **Authorize** (already signed in to the platform) the CLI receives a fresh token and writes `.cinna/account.json`.
86
+
87
+ ```bash
88
+ # Resume the account workspace you're in:
89
+ cd my-cinna/ && cinna login
90
+
91
+ # Connect a new account from a fresh folder:
92
+ mkdir my-cinna && cd my-cinna && cinna login # prompts for the domain
93
+ cinna login app.example.com # or pass it directly
94
+ cinna login app.example.com --dir my-cinna # always into a named subfolder
95
+ # Your verification code: WX7K-9Q2P
96
+ # Open this URL and click Authorize:
97
+ # https://app.example.com/device?code=WX7K-9Q2P
98
+ # ✓ Account workspace ready.
99
+ ```
100
+
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.
102
+
78
103
  ### `cinna account setup <token_or_url>`
79
104
 
80
105
  Initialize an **account workspace** — a multi-agent root from which you can discover your agents and attach per-agent workspaces without going back to the UI. Setup is initiated from **Settings → Channels → Local Development** on the platform, which emits a `curl | python3` one-liner (same pattern as the per-agent flow):
@@ -193,6 +218,30 @@ cinna api PATCH agents/3fa85f64-5717-4562-b3fc-2c963f66afa6 --json '{"descriptio
193
218
  cinna api POST tasks --data @task.json
194
219
  ```
195
220
 
221
+ ### `cinna chat [--agent <ref>] [--resume <session_id>] [--file PATH ...] [MESSAGE...]`
222
+
223
+ Talk to an agent through a **real platform session** — the same conversation pipeline production uses (permission checks, agent-env calls, the model/SDK the platform selects), not a local mock. Built for a local coding agent to test the agent it is building: it can prepare a prompt, attach files, and read the reply back as structured data.
224
+
225
+ Run it from the account workspace (or any synced agent folder under it). The reply is observed by **polling** the backend rather than reading a live stream, so it is robust to streaming/transport quirks.
226
+
227
+ - `--agent <name|slug|id>` picks the agent; omit it inside a synced agent workspace to infer it. `--resume <session_id>` continues an existing conversation instead of opening a new one (default mode for a new session is `conversation`; `--mode building` opens a building session).
228
+ - The message is the positional argument; if omitted it is read from stdin, or you are prompted for it interactively in a TTY.
229
+ - `--file PATH` (repeatable) uploads a local file and attaches it to the message.
230
+ - Output is **NDJSON** by default — one JSON event per line (`session`, `upload`, `message`, `status`, `done`), trivially parseable by another agent. `--pretty` switches to a human-readable transcript.
231
+ - Each `message` carries the agent's reasoning/tool trace under **`events`** — an ordered list of the `thinking` blocks, `tool` calls (with their full `tool_input` payload) and tool results behind the reply, so you see *what the agent did*, not just its final `content`. Pass `--no-events` to drop the trace and keep only the final text.
232
+ - Files the agent attaches to its replies are downloaded under `./cinna-chat-files/<session_id>/` (override with `--download-dir`, or skip with `--no-download` to just report the file ids). Downloads are bounded by the api-proxy's 8 MiB response cap.
233
+ - `--interval` / `--timeout` tune the poll cadence and the maximum wait for a turn. Ctrl-C interrupts the agent's turn and exits.
234
+
235
+ ```bash
236
+ cinna chat --agent crm-agent "Summarize today's leads"
237
+ cinna chat --agent crm-agent --file report.csv "Validate this export"
238
+ cinna chat --resume 3fa85f64-5717-4562-b3fc-2c963f66afa6 "Now break it down by region"
239
+ echo "ping" | cinna chat --agent crm-agent # message from stdin
240
+ cinna chat --agent crm-agent "hi" | jq -c 'select(.event=="message")'
241
+ ```
242
+
243
+ The session id is printed in the first `session` event — capture it to drive a multi-turn conversation with `--resume`.
244
+
196
245
  ### `cinna dev`
197
246
 
198
247
  Start a foreground dev session — creates / resumes the Mutagen sync session for this workspace and attaches the terminal to a two-tab TUI (status + raw Mutagen details). Ctrl-C terminates the session; sync does not outlive the TUI. To observe sync from another terminal without affecting it, use `cinna sync status`.
@@ -248,6 +297,27 @@ List every agent registered on this machine (from `~/.cinna/agents.json`). Three
248
297
  2. **Location** — workspace path on top, platform UI link below. Missing directories are flagged in red.
249
298
  3. **Sync** — Mutagen session state on top (`active` / `paused` / `connecting` / `error`), plus a per-agent backend probe (`valid token` / `expired token` / `no connection`) on the bottom. The probes run in parallel with a short timeout so the view stays snappy even with many registered agents.
250
299
 
300
+ ### `cinna doctor`
301
+
302
+ Diagnose and repair stale sync state across the whole machine. Over time the per-user registry (`~/.cinna/agents.json`) and the Mutagen daemon drift out of sync as agents are deleted, environments are spun down, and tokens expire — `cinna doctor` reconciles the two and heals the leftovers in one pass. Run it from anywhere; it is not workspace-scoped.
303
+
304
+ It detects and fixes:
305
+
306
+ - **Deleted workspaces** — registry entries whose workspace folder (or its `.cinna/config.json`) is gone. The entry is removed, along with any leftover Mutagen session.
307
+ - **Halted sessions** — sessions stopped on `halted-on-root-deletion` (the local `workspace/` root was deleted) while the agent dir is otherwise intact. Terminated; `cinna dev` recreates a clean one.
308
+ - **Dead-remote sessions** — sessions stuck retrying a remote env that no longer exists (`connecting-beta` / beta polling error). Mutagen has **no** "give up after N failures" option — a session retries forever until paused or terminated — so `doctor` is the cleanup path for these. Terminated.
309
+ - **Orphaned sessions** — `cinna-*` sessions with no registry entry at all. Terminated.
310
+ - **Expired tokens** — for **account-managed** workspaces (those under an account root), the CLI token is re-minted automatically through the parent account token, no pasting required. **Standalone** workspaces (set up via `cinna setup`) can only be refreshed with a pasted setup token, so they are reported with a `cinna set-token` hint rather than changed.
311
+ - **Expired account token** — a sub-agent token can only be re-minted while the **account** token that mints it is still valid. When the account token has itself expired, doctor probes it once and surfaces a single _"renew the account token — run `cinna login`"_ finding (listing the blocked sub-agents) instead of a pile of re-mints that would all fail with 401. Run `cinna login`, then re-run `cinna doctor` to re-mint the dependents.
312
+
313
+ ```bash
314
+ cinna doctor # diagnose, then apply all fixes behind one confirmation
315
+ cinna doctor --dry-run # report problems only; change nothing
316
+ cinna doctor --yes # apply every fix non-interactively
317
+ ```
318
+
319
+ The diagnosis is split into two tables: **Will fix** (everything doctor can repair — deleted-workspace cleanup, halted/dead/orphaned session termination, account token re-mints) and **No automatic fix — manual action needed** (standalone expired tokens, which need a pasted setup token and are never touched). Everything actionable is applied together behind a single `Apply N fix(es)?` confirmation, so the count always matches the "Will fix" table.
320
+
251
321
  ### `cinna disconnect`
252
322
 
253
323
  Stop sync, remove `.cinna/` config and generated files (`CLAUDE.md`, `BUILDING_AGENT.md`, `.mcp.json`, `opencode.json`, `mutagen.yml`). Workspace files are preserved.
@@ -132,6 +132,13 @@ Key properties:
132
132
  - **Probeable** — `cinna list` and `cinna status` call `GET /sync-runtime` as a cheap authenticated probe and label the token `valid` / `expired` / `no connection`.
133
133
  - **Refreshable in place** — `cinna set-token <token_or_url>` re-exchanges a fresh setup token through `POST /api/cli-setup/{token}` and rewrites both stores without re-cloning the workspace. The refresh is bound to the agent already in the workspace: if the exchanged token belongs to a different agent, the command aborts.
134
134
 
135
+ ### Account CLI Token
136
+
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
+
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
+ - **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
+
135
142
  ### Knowledge Source
136
143
 
137
144
  A documentation/data source attached to an agent. Queried via the MCP proxy's `knowledge_query` tool, backed by the platform's vector search.
@@ -190,6 +197,9 @@ An open protocol for connecting AI tools to external data/capabilities. The CLI
190
197
  main.py (CLI commands — Click)
191
198
  │
192
199
  ├── bootstrap.py — setup orchestration
200
+ ├── account.py — account workspace; `cinna login` (device auth), `cinna account`, `cinna agent`
201
+ ├── doctor.py — `cinna doctor`: reconcile registry ↔ Mutagen, repair stale state, refresh tokens
202
+ ├── chat.py — `cinna chat`: session-backed conversation testing (poll + NDJSON) over the api-proxy
193
203
  ├── config.py — .cinna/config.json: load/save/find
194
204
  ├── auth.py — JWT storage, Authorization headers
195
205
  ├── client.py — PlatformClient: HTTP + SSE stream_exec
@@ -347,6 +357,31 @@ To run an actual remote shell snippet (pipes, redirects, `&&`), pass it explicit
347
357
 
348
358
  ---
349
359
 
360
+ ## Remote Chat (`cinna chat`)
361
+
362
+ `cinna chat` lets a local coding agent **test the agent it is building** by driving a real platform conversation session — exercising the production path (permission checks, agent-env calls, the model/SDK the platform selects) rather than a local mock. It lives in `chat.py` and runs entirely through the **account workspace's api-proxy** (`AccountClient`), so it needs an account workspace (`.cinna/account.json`) — found by walking up from the cwd, exactly like the other account verbs, so it works from a synced `agents/<slug>/` folder too.
363
+
364
+ ### Why polling, not streaming
365
+
366
+ The platform's send-message route (`POST /sessions/{id}/messages/stream`) returns a **JSON ack immediately** and runs the agent turn asynchronously; the live events go out over a Socket.IO room *and* are persisted onto each message's `message_metadata.streaming_events`. The api-proxy is a buffered JSON hatch (it rejects `text/event-stream`), so `cinna chat` never reads the stream. Instead it:
367
+
368
+ 1. Creates the session (`POST /sessions/`, mode `conversation` by default) — or resumes the one passed to `--resume`.
369
+ 2. Uploads each `--file` and collects the returned file ids.
370
+ 3. Records the current message count as a cursor, then sends the message (`file_ids` carry the attachments).
371
+ 4. **Polls** `GET /sessions/{id}/messages?offset=<cursor>` (messages are ordered ascending by `sequence_number`, so `offset` is the cursor) and `GET …/messages/streaming-status` (`{is_streaming}`) until the turn settles — `is_streaming` false with no message flagged `streaming_in_progress`. A start-grace window covers env wake / queueing before the turn begins; an overall `--timeout` bounds the wait.
372
+
373
+ Each finalized message is emitted as one NDJSON line (`session` / `upload` / `message` / `status` / `done`); the in-progress assistant message is held back (its content is still growing) and emitted once final. Every `message` also carries the agent's reasoning/tool trace under **`events`** — the normalized `streaming_events` (thinking blocks, `tool` calls with their full `tool_input` payloads, tool results), with the bookkeeping/`attachment` entries stripped (attachments are surfaced separately). The final coalesced text stays in `content`; the trace shows *how* the agent got there. `--no-events` drops the trace; `--pretty` swaps NDJSON for a Rich transcript. Ctrl-C calls `POST …/messages/interrupt` and exits 130.
374
+
375
+ ### Attachments
376
+
377
+ Agents attach workspace files to replies via `<cinna_attach>` tags; the backend materializes them and both injects an `attachment` streaming event (`metadata.file_id` / `filename` / `mime_type` / `size`) and lists them under the message's `files[]` with `source == "agent_attachment"`. `chat.py` collects attachments from the streaming events (preferred) with the `files[]` list as a replay fallback, dedups by file id, and downloads each via the proxy (`GET /files/{id}/download`) into `./cinna-chat-files/<session_id>/`. Because the proxy buffers the response, downloads are bounded by its **8 MiB** response cap; larger files surface a clear `PlatformError` instead of a partial write.
378
+
379
+ ### File upload — the one dedicated route
380
+
381
+ The api-proxy is JSON-only and cannot carry a multipart body, and neither the account token nor a per-agent token may call `/files/upload` directly. So uploading a local attachment uses a dedicated account-CLI route, **`POST /api/v1/cli/account/files/upload`** (multipart, account-token auth), added alongside the other `/cli/account/*` routes; it creates a `File` owned by the account user and returns `FileUploadPublic` whose `id` goes into the message's `file_ids`. This is the only part of `cinna chat` that does not ride the api-proxy.
382
+
383
+ ---
384
+
350
385
  ## Bootstrap Flow
351
386
 
352
387
  ```
@@ -393,6 +428,10 @@ Backend validates on every request:
393
428
 
394
429
  When a CLI token expires (or is revoked) the normal remedy is `cinna set-token <token_or_url>` from inside the agent directory. Internally it reuses `_exchange_setup_token()` from `bootstrap.py` — the same helper that backs `cinna setup` — so the server side is a plain re-run of `POST /api/cli-setup/{token}`. The workspace's existing `platform_url` is used as the fallback when a bare token is pasted, which lets agents registered against different platforms each refresh from their own directory without extra flags. The workspace tarball is **not** re-downloaded, and `CLAUDE.md` / `BUILDING_AGENT.md` / `.mcp.json` / `opencode.json` are left in place — only the stored CLI token changes.
395
430
 
431
+ **Account tokens** refresh without a paste. `cinna login` (run inside an account workspace) drives the platform's RFC 8628 device-authorization flow: `POST /account/login/start` returns a short code + verification URL, the user clicks **Authorize** in the browser (already signed in), and the CLI polls `POST /account/login/poll` until it receives the fresh token, which it writes back into `.cinna/account.json` in place. The poll endpoint always returns HTTP 200 with a `status` field (`authorization_pending` / `slow_down` / `authorized` / `access_denied` / `expired_token`) — a deliberate divergence from RFC 8628's 400+`error` shape. Run from a fresh folder, the same command bootstraps a new account workspace instead of resuming one.
432
+
433
+ **Bulk repair** is `cinna doctor`. It reconciles the `~/.cinna/agents.json` registry against the Mutagen daemon and heals the state that drifts as agents come and go — registry entries whose workspace was deleted, sessions halted on a deleted local root or stuck retrying a dead remote env, and orphaned sessions (Mutagen has no "stop after N failures" knob, so these retry forever until terminated). Expired **per-agent** tokens under an account workspace are re-minted automatically through the parent account token; when the **account** token has itself expired, doctor groups the blocked agents into a single "run `cinna login`" finding instead of attempting re-mints that would 401. Standalone agents are reported for a manual `cinna set-token`.
434
+
396
435
  ### Authorization
397
436
 
398
437
  | Resource | Rule |
@@ -423,6 +462,15 @@ When a CLI token expires (or is revoked) the normal remedy is `cinna set-token <
423
462
  | GET | `/api/v1/cli/agents/{id}/sync-runtime` | CLI JWT | Required Mutagen version + hash (also used by `cinna list` / `cinna status` as a cheap token-validity probe) |
424
463
  | POST | `/api/v1/cli/agents/{id}/exec` | CLI JWT | Streaming SSE command execution |
425
464
  | WSS | `/api/v1/cli/agents/{id}/sync-stream` | CLI JWT | Mutagen transport tunnel |
465
+ | POST | `/api/v1/cli/account/login/start` | None | Begin a `cinna login` device-authorization request |
466
+ | POST | `/api/v1/cli/account/login/poll` | None | Poll a `cinna login` request — always HTTP 200 + `status` |
467
+ | POST | `/api/v1/cli/account/agents/{id}/mint` | Account token | Mint a per-agent CLI token (`cinna agent sync`, `cinna doctor` re-mint) |
468
+ | POST | `/api/v1/cli/account/api-proxy` | Account token | Buffered JSON escape hatch — `cinna api`, and the transport for every `cinna chat` session/message call |
469
+ | POST | `/api/v1/cli/account/files/upload` | Account token | Multipart upload for `cinna chat --file` (the proxy can't carry multipart) |
470
+
471
+ `cinna chat` reaches the conversation API **through** the api-proxy (so these are inner routes, not CLI routes): `POST /sessions/`, `GET /sessions/{id}`, `GET /sessions/{id}/messages`, `POST /sessions/{id}/messages/stream`, `GET /sessions/{id}/messages/streaming-status`, `POST /sessions/{id}/messages/interrupt`, and `GET /files/{id}/download`.
472
+
473
+ The account-workspace surface adds the broader `/api/v1/cli/account/*` route group (login, agents, credentials, connect, schedules, status, api-proxy, files/upload); only the routes the sync / login / doctor / chat paths use are listed here.
426
474
 
427
475
  Endpoints that were part of the old Docker-replica model (`build-context`, `workspace` POST, `workspace/manifest`, `credentials`) have been removed from the backend and from this CLI.
428
476
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "cinna-cli"
3
- version = "0.2.1"
3
+ version = "0.2.3"
4
4
  description = "Local development CLI for Cinna Core agents"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"