@shardflux/cli 0.23.1 → 0.24.1

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 (60) hide show
  1. package/CHANGELOG.md +139 -0
  2. package/README.md +249 -4
  3. package/dist/account-args.js +8 -3
  4. package/dist/account-commands.js +15 -2
  5. package/dist/account-pool.js +5 -1
  6. package/dist/acp-journal.d.ts +2 -2
  7. package/dist/acp-journal.js +14 -4
  8. package/dist/acp-permission-elsewhere.d.ts +38 -0
  9. package/dist/acp-permission-elsewhere.js +85 -0
  10. package/dist/acp-rpc.d.ts +2 -1
  11. package/dist/acp-rpc.js +11 -2
  12. package/dist/acp.js +125 -30
  13. package/dist/agent-credentials.d.ts +3 -0
  14. package/dist/agent-credentials.js +7 -0
  15. package/dist/args.d.ts +1 -1
  16. package/dist/args.js +154 -8
  17. package/dist/board-args.d.ts +6 -0
  18. package/dist/board-args.js +155 -0
  19. package/dist/board-commands.d.ts +62 -0
  20. package/dist/board-commands.js +395 -0
  21. package/dist/codex-cloud.d.ts +30 -3
  22. package/dist/codex-cloud.js +86 -12
  23. package/dist/conversation-commands.d.ts +30 -0
  24. package/dist/conversation-commands.js +312 -9
  25. package/dist/conversation-transcript.d.ts +44 -0
  26. package/dist/conversation-transcript.js +206 -0
  27. package/dist/conversations.d.ts +5 -2
  28. package/dist/conversations.js +20 -10
  29. package/dist/default-volume-commands.d.ts +3 -0
  30. package/dist/default-volume-commands.js +35 -0
  31. package/dist/device-approvals.js +4 -3
  32. package/dist/device-bridge.d.ts +42 -1
  33. package/dist/device-bridge.js +241 -14
  34. package/dist/device-folders.d.ts +58 -0
  35. package/dist/device-folders.js +228 -0
  36. package/dist/device-helper.d.ts +6 -0
  37. package/dist/device-helper.js +87 -14
  38. package/dist/drive-commands.d.ts +25 -0
  39. package/dist/drive-commands.js +201 -0
  40. package/dist/filesystem-commands.js +2 -1
  41. package/dist/handlers.js +6 -1
  42. package/dist/local-sessions.d.ts +7 -1
  43. package/dist/local-sessions.js +38 -0
  44. package/dist/main.js +3 -0
  45. package/dist/move-files.d.ts +39 -0
  46. package/dist/move-files.js +167 -0
  47. package/dist/move-interrupt.d.ts +18 -0
  48. package/dist/move-interrupt.js +193 -0
  49. package/dist/move.js +28 -17
  50. package/dist/onboarding.d.ts +6 -2
  51. package/dist/onboarding.js +24 -6
  52. package/dist/project-git.d.ts +7 -1
  53. package/dist/project-git.js +16 -3
  54. package/dist/project-preparation.d.ts +7 -0
  55. package/dist/project-preparation.js +14 -0
  56. package/dist/project-template-setup.js +32 -11
  57. package/dist/router-commands.d.ts +22 -0
  58. package/dist/router-commands.js +179 -0
  59. package/dist/worktree-twin-engine.js +16 -6
  60. package/package.json +7 -5
package/CHANGELOG.md CHANGED
@@ -1,5 +1,144 @@
1
1
  # Changelog
2
2
 
3
+ ## Unreleased
4
+
5
+ - Faster conversation starts (0.24.1): the project's dependency fingerprint is read from Git's own file list instead of walking the whole folder, so ignored and untracked trees beside the project (agent worktrees, build output) no longer hold a start on **Getting your project…**. A folder with 1.2 million such files took 32 s per start; it now takes 0.3 s, with the same fingerprint.
6
+ - A folder without Git that `shard` makes a repository of (a first chat, `shard projects link`) starts with its files as
7
+ the first commit (0.25.0+; `.gitignore` respected, large media stay untracked), so a conversation starts from the
8
+ same commit as the folder and its work comes back by fast-forward, as in any repository. Before, an empty first
9
+ commit left every file to the starting snapshot and the landing asked the agent to merge. A repository of your own
10
+ keeps its staged and untracked files as they are.
11
+ - Device requests (0.25.0+) are answered between turns and as a turn ends; only an archived conversation refuses them.
12
+ - Background preparation of a project with no build or test command ends ready (0.25.0+): there is nothing to check,
13
+ so `shard projects sync list` and the folders `shard projects link` adds no longer show an error.
14
+ - `shard move` (0.25.0+) brings the files the session used outside the project: files you @-mentioned or named in a
15
+ prompt, files it read or edited, and the files a handed file names (a `~/Downloads` handoff and what it points to).
16
+ Each goes to the same place under the VM's home, and this computer's home path leads there, so the history's paths
17
+ open in the cloud. Before, only the checkout and the transcript moved, and a moved session stopped at a handoff it
18
+ could no longer open. Key and sign-in stores (`~/.ssh`, `~/.aws`, `.env` files, private keys) stay here. Up to 64
19
+ files, 64 MiB in all, 16 MiB each; the command names what it brings and what stays, and `--json` adds `files` and
20
+ `files_left`.
21
+
22
+ - Tunnel recovery (0.24.1, SDK 0.27.3): a foreground reverse tunnel replaces its guest helper automatically when replay is lost. Existing TCP peers reset; fresh connections reach the new listener. SSH and device port-forward connections close cleanly for a new connection instead of looping on an unrecoverable cursor.
23
+ - Folders on this computer (0.25.0+): an agent can list a folder here (`dir_list`: names, kinds, sizes and Git
24
+ remotes, never file contents), make one of your projects from it (`project_link`, with an optional first task), and
25
+ move a file to or from anywhere here with an absolute or `~/` path. Nothing runs before you approve it: **Allow once**,
26
+ or **Always for this folder**, which covers that kind of request for that folder and what is inside it, from any of
27
+ your conversations. A path outside every such folder asks again, and commands never run under a folder grant. The
28
+ background helper now answers for your signed-in project (and, signed in without a key, every project you can reach),
29
+ so a home conversation or an Orchestrator task can ask too, and reads every project's requests in one poll.
30
+ - `shard projects link <path> [--prompt <text>] [--app claude|codex]` (0.25.0+) makes a local folder one of your
31
+ projects: prepared in the background and kept as this computer's live copy; `--prompt` also starts a conversation
32
+ there. `--json`: `{project: {project_id, repo_remote, folder, name, conversation_id?}}`.
33
+ - `shard device-helper` (0.25.0+) asks the desktop app first when it serves `device-approver.sock` in the config folder,
34
+ so approvals show there as Needs-you items; otherwise the menu bar, an editor (ACP) or `shard device approve` as before.
35
+
36
+ - `shard route <text...>` and `shard ask <question...>` (0.25.0+, SDK 0.28.0): the board's input bar in a terminal.
37
+ `shard route` prints where the input goes (its project or your home) and whether it is a task, a question or a steer
38
+ for an open task, with each task in your words and its title when the input asks for several; `shard ask` streams
39
+ the answer to a quick question about your work as it is written. `--task <task-id>` names the selected task, `--org`
40
+ and `--project` the scope (signed in: your organization's projects; with `SHARDFLUX_API_KEY` set: that key's
41
+ project), and `--json` prints the API's `RouterResult` or the whole answer.
42
+ - The board (0.25.0+, SDK 0.28.0): `shard board` shows what waits on you first, then every task in Now, Next and
43
+ Later with its state, title or words and project; `--follow` prints each change as it happens, until Ctrl-C
44
+ (`--json`: the board, then one feed answer per line). `shard task add <words...>` (`--next`, `--later`, `--first`,
45
+ `--after <task-id>`, `--home`, `--project`), `show`, `edit` (`--text`, `--title`, `--now|--next|--later`,
46
+ `--first|--after`, `--keep`, `--clear`), `rm`, `steer` and `clear-done` manage tasks in your own words, and `shard
47
+ task show` lists each report with its evidence. `shard needs-you` lists everything your agents wait on, and `shard
48
+ needs-you answer <id>` answers any of it (`allow|always|deny`, `--everywhere`, `--pick`, `--text`,
49
+ `land|keep|discard`, `--keep <task-id>`). `shard approvals` shows the approval mode and always-allow rules; `set
50
+ <ask|routine|trusted|default> [--project <id>|--default]`, `allow <tool> <pattern...> [--project <id>|--everywhere]`
51
+ and `remove <rule-id>` change them.
52
+
53
+ - Project drive (0.25.0+, SDK 0.28.0): `shard drive put <local-file> [<drive-path>]` saves a file to the project's
54
+ drive as a new version and prints its path and sha256 (the drive path defaults to the file's name; `-` reads standard
55
+ input; `--content-type`); `shard drive get <path>` writes its bytes to standard output or with `-o` to a file, checked
56
+ against their SHA-256, and `--sha256` reads exactly that version, also after the path was overwritten or removed;
57
+ `shard drive ls [prefix]` lists the current files (size, saved time, type, path; `--limit`, `--cursor`, `--all`);
58
+ `shard drive rm <path>`. The API key's project or `--project`; signed in, any of your projects by id or slug.
59
+ `--json` everywhere.
60
+ - Model template (0.25.0+, SDK 0.28.0): `shard models` prints which harness, model and effort each role of a task
61
+ (master, builder, hard, light, reviewer) runs with, until when a role waits for its account's next usage window and
62
+ what the other provider would run now; `--harness` and `--attempt` show a running task's and a retry's. `shard models
63
+ primary claude|codex|clear` sets the project's primary provider, `--org <id|slug>` the organization's default.
64
+ - `shard conversations list` and `shard conversations feed` (0.25.0+, SDK 0.28.0): signed in without an API key, every
65
+ project you work in, across your organizations, on one cursor; `--org <id|slug>` narrows them to one organization
66
+ (and uses your sign-in when you have one). With an API key they cover its project, as before.
67
+
68
+ - `shard conversations answer <id> <permission-id> --pick <option>` (0.25.0+) answers the agent's question (Claude Code's
69
+ AskUserQuestion) with exactly the picked option; repeat `--pick` for a multi-select question, `--pick
70
+ "<question>=<option>"` when it asks several; `deny` declines it.
71
+
72
+ - `shard acp`: a conversation computer on a newer template keeps the models the session offered until its live list
73
+ arrives, so the editor's first model choice is taken; before, the list went empty for a few seconds after
74
+ `session/new` and `session/set_model` answered "This model is no longer available".
75
+
76
+ - Codex accounts (0.25.0+, SDK 0.28.0): `shard agents open` of a Codex Cloud conversation checks the Codex app on this
77
+ computer first. When the app is signed out or signed in to another ChatGPT account than the thread's, it says so in
78
+ one line with the account to sign in as and the thread's web link, instead of opening a link the app cannot show.
79
+ Earlier threads are listed with their accounts (`Earlier history: <url> (account)`). `--json` adds `account`,
80
+ `codex_app_login`, `account_mismatch` (`line`, `sign_in_as`, `web_url`) and each earlier thread's `account`.
81
+ `shard agents accounts use` of a Codex account says when the Codex app here is signed in to another account
82
+ (`--json`: `codex_app`).
83
+ - Codex sign-in (0.25.0+, SDK 0.28.0): when Codex Cloud refuses the copied ChatGPT login, `shard setup --inspect`
84
+ says so in one line with the next step: `codex login` (Sign in to Codex again), then, once this computer has the new
85
+ sign-in, Copy sign-in (`shard agents credentials sync`); `--connect` copies it. `shard ./ codex` and
86
+ `shard agents codex enable` copy this computer's login and continue when it is new, or stop at once with
87
+ `codex_sign_in_required` and that line. The Codex remote device is never restarted for a sign-in (its Cloud threads
88
+ keep running), and one starting again with the new login reads as connecting.
89
+
90
+
91
+ - `shard conversations start` starts a conversation without a folder: from the project hub (`--repo`, `--branch`), or with `--home` a coordinator on your machine that spawns threads in the project's repositories. `--app`, `--model`, `--effort`, `--title`, `--project`; `--prompt` sends the first message, with `--file` attachments. Prints the conversation's id, key and state (`--json`: the conversation, its workspace and the message).
92
+
93
+ - `shard conversations send <id> <text> --file <path>` (repeatable, up to 20 files of 64 MiB each) puts each file in the conversation's computer before the message, which lists their paths, and prints `Attached <path>` for each. The text may be left out when files are attached, and unquoted words are sent as one message.
94
+
95
+ - Home conversations list as `home` in `shard conversations list` and `get`, and open without a repository in `/home/user`.
96
+
97
+ - `shard acp`: a permission prompt answered through the API (the Shard client, `shard conversations answer`, MCP)
98
+ while the editor shows it is withdrawn from the editor (`$/cancel_request`) and the turn goes on in the editor; before,
99
+ the editor saw nothing more of the turn until it answered its own prompt.
100
+
101
+ - Permission requests (0.25.0+, SDK 0.28.0): `shard conversations permissions <id>` lists what a conversation's agent
102
+ asked permission for before running a tool (Claude Code, Codex and ACP agents), oldest first, with `--state`,
103
+ `--all` and `--json`; `shard conversations answer <id> <permission-id> allow|always|deny` answers one.
104
+ `shard conversations get` names the requests a `needs_you` conversation waits on (`Waiting on: Run npm test`).
105
+
106
+ - `shard conversations watch <id>` shows a conversation's terminal live and read-only: its last 256 KiB, then the agent's
107
+ output as it works, until its session exits or Ctrl-C. It never types into the terminal and never keeps the computer
108
+ awake while the agent is idle.
109
+ - `shard conversations get <id>` prints one line per port a process listens on in the conversation's computer: the
110
+ port, the program, and its URL once exposed (or `127.0.0.1 only`). `--json` includes `pty_id`, `listening_ports` and
111
+ `listening_ports_at`.
112
+
113
+ - `shard conversations feed` (0.25.0+): each conversation of the project as it changes, until interrupted; `--json`
114
+ prints one `{conversation, cursor}` object per line, `--cursor` continues after one. `shard conversations list
115
+ --updated-after <time>` filters by `updated_at`.
116
+ - Conversation titles in human output are the agent's own short title (`title_short`) once it has one.
117
+
118
+ - `shard acp` opens a conversation by its id: `session/load` with a `conv_…` session id attaches an ACP client to that conversation's live session in its computer, with its history replayed by the agent and no local folder synced.
119
+
120
+ - `shard conversations transcript <id>` prints a conversation’s transcript, the agent’s own Claude Code or Codex JSONL byte for byte, for working, parked and finished conversations alike (`--offset`, `--session`; `--follow` keeps printing new lines). `shard conversations events <id>` follows it live: state changes, new transcript lines, tool calls and the reply as it streams; `--json` prints one event per line.
121
+
122
+ ## 0.24.0
123
+
124
+ - Finish all active device port-forward cleanup and close the local listener and cell before returning the original tunnel-close error. Repeated stop and abort keep the same error and promise.
125
+
126
+ - SSH and port tunnel reattachment stops on permanent HTTP upgrade refusals through the SDK, retaining the actual HTTP status instead of repeatedly refreshing a valid token.
127
+
128
+ - Recheck the guest ACP supervisor before reusing an idle editor connection. After its owner changes, initialize and load the retained native session; a stable owner keeps its session and cursor.
129
+
130
+ - Bind ACP input to the loaded supervisor using its Unix peer PID/start identity before provider stdin. A replacement between the owner check and request delivery requires loading the retained session again; no uncertain turn is replayed.
131
+
132
+ - `shard volumes attach/detach --machine <volume>` and `--template <volume> [--path <repo>]` manage default shared mounts. Machine/template status show their defaults; JSON includes live and skipped results.
133
+
134
+ - Finished Codex Cloud conversations land in your checkout like every other conversation: each turn that ends with its work committed lands in the branch the thread started from, and the thread goes on. A thread waiting on an approval or an answer lands once that turn finishes.
135
+
136
+ - `shard move` interrupts a running local Claude Code or Codex reply and continues it automatically in the cloud, redoing the interrupted step. An agent calling `move_to_cloud` inside its own turn still waits for that turn to end. The local turn and its running commands stop before the project snapshot is taken.
137
+
138
+ ## 0.23.2 (2026-10-14)
139
+
140
+ - Preserve API-key credential burst refusals (`tool_token_budget`, scope `api_key`) and their Retry-After without treating them as pre-handler admission or replaying arbitrary POSTs. Plan rollout preserves unmarked Free legacy hourly/burst behavior and uses effective workspace capacity for eligible shared hourly budgets; no client-side quota or extra retry layer.
141
+
3
142
  ## 0.23.1
4
143
 
5
144
  - Pin the Ubuntu desktop packages to 0.1.11. Read the desktop account snapshot with the selected project key so organization, usage, spend and connector metadata match the macOS Settings app, including API-key-only profiles. Optional missing metadata stays unavailable. Conversation lists support --all with bounded cursor handling; the desktop reads all pages of conversations and workspace states.
package/README.md CHANGED
@@ -4,6 +4,8 @@ Historical template file lists: reads queue checksum-verified reconstruction whe
4
4
 
5
5
  Plan-derived API budgets (0.23.0+, SDK 0.27.0): `--json` error output includes `rate_limit` and `retry_after_seconds`; the text hint names the shared scope and delay. API request pacing follows the SDK admission timeout and preserves the request body. Reuse workspace credentials and reduce status polling when the returned budget asks for a delay.
6
6
 
7
+ Credential key pacing (0.23.2+): when the service enables plan budgets for an eligible finite non-Free grant, workspace credentials share organization and workspace hourly allowances, plus a separate API-key burst. A `tool_token_budget` refusal can name `api_key`; honor `Retry-After` and reuse valid credentials. Unmarked Free grants retain the legacy principal hourly/burst policy. It does not promise that lifecycle work has not started, so it never authorizes replay of an arbitrary POST.
8
+
7
9
  Exec survives a dropped connection (0.23.0+): a lost start response is retried with the same session ID and reattaches to the command that already started, and a cut-off JSON answer is retried within the same safe retry boundary.
8
10
 
9
11
  Suspension controls are available from 0.22.0. `shard operations progress <operation-id>` reads the actual phase, host deadline, cancellation window and safe next action. `shard operations cancel <operation-id>` requests cancellation; `--wait --timeout 5m` waits for confirmed `suspend_canceled` recovery of the original workspace. Asynchronous acceptance is labeled separately. The owning host refuses cancellation outside validated preparation; after capture, wait for the outcome and resume if it succeeded. These commands never reset, replace or launch a provider turn. `--json` preserves the reported values; unavailable progress is explicit.
@@ -142,6 +144,21 @@ shard ./ --on fix-login --review # a read-only Claude reviewer of it
142
144
  the new thread. Codex threads are Codex Cloud threads, on the web and your phone too. `shard agents list` shows each
143
145
  thread's conversation and computer.
144
146
 
147
+ Start a conversation without a folder (0.25.0+): its repository is checked out from the project's saved branches, or
148
+ with `--home` a coordinator starts on your machine and spawns threads in the project's repositories.
149
+
150
+ ```sh
151
+ shard conversations start --prompt "Add CSV export to the invoices page" --file mockup.png
152
+ shard conversations start --home --title "Plan the billing work"
153
+ shard conversations send conv_6qz2dmu4kd3xa7v5l2hgmfnpjy "Here is the failing run" --file ci.log
154
+ ```
155
+
156
+ `--repo` and `--branch` choose the repository and the branch it starts from (default: the project's most recently
157
+ active repository, from `main`, then `master`); `--app`, `--model` and `--effort` work as for `shard ./`. It prints the
158
+ conversation's id, key and state; `shard agents open <id>` opens it. `--file` (repeatable, up to 20 files of 64 MiB
159
+ each) puts each file in the conversation's computer before the message and prints where it landed; `--json` returns
160
+ the stored paths.
161
+
145
162
  Choose the model and reasoning effort a new thread starts with (0.17.0+):
146
163
 
147
164
  ```sh
@@ -180,7 +197,7 @@ backup of later edits in other worktrees. External object alternates, nested rep
180
197
  resolution before preserve-all can upload; a registration whose folder is gone has nothing to copy and is listed as
181
198
  `stale_registrations`.
182
199
 
183
- A running turn finishes here first. Uncommitted edits come along, and your working tree, index and branch stay as
200
+ With CLI 0.24.0+, a move during a reply stops the local turn and continues the same reply in the cloud automatically, redoing the interrupted step. An agent calling `move_to_cloud` inside its own turn waits for that turn to finish. Uncommitted edits come along, and your working tree, index and branch stay as
184
201
  they are. The cloud work comes back to your checkout by itself, and typing in the local Claude Code copy shows one line
185
202
  that it continues in the cloud. Sessions from Claude Desktop's Code tab and the Codex app move the same way; in the
186
203
  macOS menu bar, a project's recent sessions have **Move to cloud** in their ⋯ menu, and with the Shardflux MCP server
@@ -233,6 +250,8 @@ shard conversations try <conversation>
233
250
  Your branch opens in `.shard/worktrees/<name>`, syncs both ways, and its dev servers appear at localhost.
234
251
  Ctrl-C stops local sync. Your main checkout stays available for your own work.
235
252
  Device tools ask for approval on your computer before preparing the conversation’s local worktree. With CLI 0.15.0 and menu bar 0.3.0, Review shows the exact command, file direction, paths or port mapping; access outside the conversation folder only offers Allow once. Live transcripts stay in
253
+
254
+ From 0.25.0, an agent can also list a folder on your computer, make one of your projects from it, and move a file to or from anywhere on it by an absolute or `~/` path. Each asks first: **Allow once**, or **Always for this folder**, which covers that kind of request for that folder and everything inside it. `shard projects link <path> [--prompt <text>]` does the same from a terminal: the folder is prepared in the background, kept as this computer’s live copy, and `--prompt` starts a conversation there.
236
255
  `~/.config/shardflux/conversations/`; ended sessions enter Claude Code’s or Codex’s native history. Transcript files up to 64 MiB are supported (0.14.0+).
237
256
  From 0.14.3, transcript copying runs independently of device approvals. Unknown entries are skipped, and failed copies retry with backoff; diagnostics name the conversation and affected entry.
238
257
 
@@ -249,7 +268,8 @@ Finished conversations land in your checkout by themselves (0.16.0+, with the me
249
268
  conversation's turn ends with its work committed, the branch it started from moves to its commits and the changed
250
269
  files are written in place, next to your own uncommitted edits, which stay as they are (where both touched the same
251
270
  lines, your text is kept). `git status` keeps showing only your edits. A conversation started from a checkout with
252
- uncommitted edits starts from them too. Every rewritten file is backed up first:
271
+ uncommitted edits starts from them too. A Codex Cloud thread lands after each turn it finishes (0.24.0+); one waiting
272
+ on an approval or an answer lands once that turn finishes. Every rewritten file is backed up first:
253
273
 
254
274
  ```sh
255
275
  shard conversations landing # auto (default), pr or off for this project
@@ -643,7 +663,7 @@ Projects that depend on `@shardflux/sdk` or the Python `shardflux` package keep
643
663
  `shard` tells you when a newer version exists, on stderr after the command's output:
644
664
 
645
665
  ```text
646
- @shardflux/cli 0.13.5 is outdated: 0.14.0 is available. Update: shard update
666
+ @shardflux/cli 0.23.0 is outdated: 0.23.1 is available. Update: shard update
647
667
  ```
648
668
 
649
669
  or that the API no longer supports it (`... is no longer supported by the Shardflux API (minimum
@@ -684,6 +704,7 @@ shows the status; `shard version --check` asks now (`--json`: `{"cli", "sdk", "u
684
704
  | `version [--check]` | Print the CLI and SDK versions and the update status (`--check` asks the API now). |
685
705
  | `usage [--org <id>] [--estimate \| --series \| --workspace <id\|key>] [--from T] [--to T] [--granularity hour\|day] [--meter M]` | Usage summary for the current period: plan, meters and allowances. `--estimate`: the bill so far and projected allowance use; `--series`: usage over time; `--workspace`: one workspace's usage over time (0.5.0+). The summary includes the [overage](#usage-and-overage) block (0.5.1+). |
686
706
  | `feedback [--yes] [--category C] [--workspace W] [--request-id ID] [--error-code CODE] [--command TEXT] [--from-file F\|-] <message...>` | Preview feedback; `--yes` sends after explicit user authorization (0.22.0+). See [Feedback](#feedback-preview-and-user-approval). |
707
+ | `route [--task ID] [--org O] [--project P] <text...>`, `ask [--task ID] [--org O] [--project P] <question...>` | The board's input bar (0.25.0+): where a line of input goes (its project or your home; a task, a question or a steer, split per task), and a quick question about your work, answered as it is written. |
687
708
  | `workspaces open <key> --template <slug> [--secret NAME]... [--input NAME=value]... [--input-file F] [--lifetime persistent\|session] [--mode processful\|file-first] [--cpu-millis N --memory-mib N --disk-gib N] [--allocation-mode fixed\|elastic] [--memory-held N] [--no-wait] [--timeout 5m] [--timing]` | Open by key. Creates the workspace on first use and reconnects or resumes it afterwards, never resetting it. Waits until it is ready (the template's start commands and services included) unless you pass `--no-wait`. `--secret` binds secret names (replacing the binding of an existing workspace). `--input` (0.4.0+) passes the template's text inputs (replacing those of an existing workspace); `--input-file` reads `NAME=value` lines or a JSON object. `--lifetime session`: the workspace is discarded when the session ends (`workspaces close` or the idle timeout), and the key then opens a new workspace. `--mode file-first` (0.5.0+) opens a [file-first workspace](#file-first-workspaces); the default is processful for a new key and the stored mode for an existing one. `--allocation-mode elastic` (0.8.0+): [elastic memory](#elastic-memory-080). |
688
709
  | `workspaces list [--state S] [--prefix P] [--lifetime persistent\|session\|any] [--purpose standard\|template_draft\|template_test\|any] [--include-deleted] [--all] [--limit N] [--cursor C]` | One page, or every page with `--all`. The next cursor is printed on stderr. By default only persistent standard workspaces are listed. A file-first workspace's state reads `running (file-first)` (0.5.0+). |
689
710
  | `workspaces get <id\|key>` | One workspace (deleted ones included), with the startup of its template's start commands and services (0.4.0+): a failed step shows its exit code, reason and output tail. Its mode, and a file-first workspace's tree revision (0.5.0+). Its memory (0.8.0+): `fixed 4096 MiB`, or `elastic: promised …, held …, plugged +… MiB`. The template version whose [immutable paths](#immutable-paths-080) it has mounted (0.8.0+): `immutable paths v3 (mounted read-only)`. |
@@ -1443,7 +1464,7 @@ A message from claude.ai or the Claude app wakes a waiting Claude conversation a
1443
1464
 
1444
1465
  Run `shard ./` in your project to start Claude Code or Codex, usable immediately while setup runs in the background.
1445
1466
 
1446
- New project folders are ready to use: `shard ./` and `shard agents new` initialize Git on `main` when needed and create an empty initial commit (CLI 0.14.2+). Existing files and staged work stay yours; the initial commit contains no files. Repositories without commits keep their current branch. If Git identity is missing, that one commit uses `Shardflux <noreply@shardflux.dev>` without changing your Git configuration.
1467
+ New project folders are ready to use: `shard ./` and `shard agents new` initialize Git on `main` when needed (CLI 0.14.2+). From 0.25.0 a folder `shard` makes a repository of gets its files as the first commit (`.gitignore` respected; large media stay untracked), so a conversation's work comes back by fast-forward; a repository of your own without commits keeps its staged and untracked files behind an empty initial commit. Repositories without commits keep their current branch. If Git identity is missing, that one commit uses `Shardflux <noreply@shardflux.dev>` without changing your Git configuration.
1447
1468
 
1448
1469
  Machine saves require a machine source VM (`labels.shard_layer = "machine"`). On your computer, run
1449
1470
  `shard machine open --json`, make changes in that VM, then `shard machine save --wait`. The computer's CLI
@@ -1556,6 +1577,40 @@ account, make another one active first; your own you can always remove. `--json`
1556
1577
 
1557
1578
  From 0.14.0, `shard conversations list|get|send|wait|land|move` manages project conversations. `shard c` is the short form. Conversations retain their branch and transcript as their VM binding changes.
1558
1579
 
1580
+ Watch an agent work: `shard conversations watch <id>` shows the conversation's terminal live, read-only. It starts
1581
+ with the last 256 KiB the agent printed, then streams everything it prints, until its session exits or you press
1582
+ Ctrl-C. Nothing you type reaches the agent, and watching never keeps its computer awake while the agent is idle.
1583
+ `shard conversations get <id>` lists the servers running in the conversation's computer, one line per port, with
1584
+ each port's URL once exposed (`shard ws port expose`); `--json` returns them as `listening_ports`, and the terminal as
1585
+ `pty_id`.
1586
+
1587
+ ```sh
1588
+ shard conversations watch conv_a7k2m4q6s3t5v7x2z4b6d3f5h7
1589
+ shard conversations get conv_a7k2m4q6s3t5v7x2z4b6d3f5h7
1590
+ # Fix login
1591
+ # working
1592
+ # shard/fix-login
1593
+ # 5173 node https://5173-<handle>.shardflux.app (exposed)
1594
+ # 5432 postgres 127.0.0.1 only
1595
+ ```
1596
+
1597
+ `shard conversations transcript <id>` (0.25.0+) prints the conversation's transcript: the agent's own Claude Code or
1598
+ Codex JSONL, byte for byte, for a working, parked or finished conversation alike, without waking its VM. `--follow`
1599
+ keeps printing lines as the agent writes them; `--offset` starts at a byte offset and `--session` reads an earlier
1600
+ session. `shard conversations events <id>` (0.25.0+) follows the conversation live: its state, new transcript lines,
1601
+ tool calls and the reply as it streams; `--json` prints one event per line.
1602
+
1603
+ ```sh
1604
+ shard c transcript conv_k3m5q7s2u4w6y3b5d7f2h4j6ln > session.jsonl
1605
+ shard c transcript conv_k3m5q7s2u4w6y3b5d7f2h4j6ln --follow | jq -c 'select(.type=="assistant")'
1606
+ shard c events conv_k3m5q7s2u4w6y3b5d7f2h4j6ln
1607
+ # state: working
1608
+ # Looking at the login test.
1609
+ # tool: Read login.test.ts (pending)
1610
+ # tool: Read login.test.ts (completed)
1611
+ # state: done
1612
+ ```
1613
+
1559
1614
 
1560
1615
  Desktop project connections (0.14.0+): `shard agents open codex-app <project>/<workspace>` and
1561
1616
  `shard agents open claude-desktop <project>/<workspace>` register **Shardflux · <project>** in the app.
@@ -1565,6 +1620,49 @@ Conversation JSON includes `join_mode` and the worktree used by reviewers and wo
1565
1620
 
1566
1621
  `shard conversations resume <id>` continues a saved conversation in a fresh VM under the same identity (0.14.0+). `shard ./ --resume` uses this same path. `shard conversations reader <project-id>` opens a small read-only workspace for saved branches and transcripts; the Mac transcript mirror opens it automatically when no project VM is live. Adaptive idle reclaims the reader.
1567
1622
 
1623
+ ### Route input and ask about your work (0.25.0+)
1624
+
1625
+ `shard route` reads a line of input the way the board's input bar does: which project it belongs to (or your home),
1626
+ whether it is a new task, a question or a steer for an open task, and each task in your own words when it asks for
1627
+ several. `shard ask` answers a quick question about your work from the board, printing the answer as it is written.
1628
+
1629
+ ```sh
1630
+ shard route fix the hero image on the landing page and add a GPU row to the pricing table
1631
+ # 2 tasks in site
1632
+ # 1. Fix the landing page hero image
1633
+ # fix the hero image on the landing page
1634
+ # 2. Add a GPU pricing row
1635
+ # add a GPU row to the pricing table
1636
+ shard route --task task_healthzhealthzhealthzhealt return 503 instead of 500 when the database is down
1637
+ # steer task_healthzhealthzhealthzhealt in status-board
1638
+ # return 503 instead of 500 when the database is down
1639
+ shard ask what is the healthz work doing right now
1640
+ # Your “Add /healthz endpoint” agent is working on shard/healthz with 2 commits and was last active 4 minutes ago. …
1641
+ ```
1642
+
1643
+ Signed in, both read every project of your organization (`--org <id|slug>` when you belong to several, `--project` to
1644
+ lean to one); with `SHARDFLUX_API_KEY` set they read that key's project. `--task` names the selected task. `--json`
1645
+ prints the API's object: the route (`kind`, `parts`, `task_ref`, `uncertain`, …) or the whole answer (`text`,
1646
+ `needs_work`, `sources`, …). An answer that needs work ends with "Make it a task?". Neither starts work or wakes a
1647
+ computer.
1648
+
1649
+ ### Permission requests (0.25.0+)
1650
+
1651
+ When a conversation's agent asks before it runs a tool (Claude Code, Codex or an ACP agent), the conversation is
1652
+ `needs_you` and `shard conversations get` names what it waits on. Answer it from any terminal:
1653
+
1654
+ ```sh
1655
+ shard c permissions <conversation-id> --state pending
1656
+ # 01a1300c-… pending Run npm test npm test
1657
+ shard c answer <conversation-id> <permission-id> allow # or always, deny
1658
+ # Allowed once: Run npm test
1659
+ ```
1660
+
1661
+ Each line is the request id, its state, what the agent wants to do, the command, path or URL, and the decision once
1662
+ answered. Without `--state` every request is listed, oldest first; `--all` reads every page and `--json` prints `data`
1663
+ and `nextCursor`. `always` lets the agent's harness stop asking for this kind of call. The agent goes on with its turn
1664
+ as soon as you answer, and a parked computer wakes to take the answer.
1665
+
1568
1666
  ### Starting from your working state (0.22.0+)
1569
1667
 
1570
1668
  A new conversation starts from your worktree as it is: committed history, uncommitted edits, untracked files and your
@@ -1605,6 +1703,52 @@ With CLI 0.23.0+, a full conversation key or id opens that same Claude or Codex
1605
1703
 
1606
1704
  Use `shard agents open <workspace> --in auto --json` to resolve where a conversation opens: a saved target first; then, for Claude (0.15.1+), the conversation itself in Claude Desktop when it is installed (`{"target":"claude-desktop","url":"claude://code/session_…","app_bundle_id":…}`), else Claude Code in the terminal (`{"target":"terminal","command":["shard","agents","open","<workspace>"]}`; without `--json` it attaches in this terminal). Codex Cloud opens its current thread in your browser (CLI 0.23.0+). claude.ai opens with `--in web`. The caller opens the `url` or app, or runs the command in a terminal. Explicit `--in terminal` retains terminal attachment. The menu uses this resolver for new and existing conversations, without provider URLs in Swift.
1607
1705
 
1706
+ ## The board (0.25.0+)
1707
+
1708
+ The board is one list of what you asked your agents for, the same on every device: what waits on you first, then every
1709
+ task in Now, Next and Later, each in your own words. The agent that does the work seals a report with its evidence, and
1710
+ reading the board never wakes a computer. The board commands use your project API key: the board is its project's.
1711
+
1712
+ ```sh
1713
+ shard task add Fix the wake race in resume # your words, kept exactly; the project's master starts the work
1714
+ # task_h7g4d7tqyv2pz3a2npyo4rpgzq now queued
1715
+ shard board
1716
+ # Needs you
1717
+ # perm_01a1300c-… Run npm test? npm test
1718
+ #
1719
+ # Now
1720
+ # task_h7g4d7tqyv2pz3a2npyo4rpgzq working Fix the wake race in resume 01a1415f-…
1721
+ shard needs-you answer perm_01a1300c-… always # allow, always or deny
1722
+ # Always allowed: Run npm test (rule rule_ym4xywjgj7vvvrhkqlduotkvb4)
1723
+ shard task show task_h7g4d7tqyv2pz3a2npyo4rpgzq # the plan, then each report with its evidence
1724
+ shard board --follow # each change as it happens, until Ctrl-C
1725
+ ```
1726
+
1727
+ **Tasks.** `shard task add <words...>` adds a task to Now (`--next`, `--later`), last in its bucket unless `--first` or
1728
+ `--after <task-id>` places it; `--home` runs a personal task on the project's machine home. `shard task edit <id>`
1729
+ changes the words (`--text`, and the task's master hears it), the title (`--title`), the bucket (`--now`, `--next`,
1730
+ `--later`) and the order (`--first`, `--after`), keeps an agent's suggestion as your own (`--keep`) or clears the task
1731
+ (`--clear`; `--no-clear` brings it back). `shard task steer <id> <words...>` sends steering words to the task's master,
1732
+ `shard task rm <id>` deletes the task, and `shard task clear-done` clears every finished one. `shard task show <id>`
1733
+ prints the task, its plan and each report: milestone, outcome and evidence (`tests 12/12 (pnpm test)`).
1734
+
1735
+ **Needs you.** `shard needs-you` lists permission requests, questions, credential requests, finished work to land and
1736
+ Later tasks to keep, oldest first. `shard needs-you answer <id>` answers any of them: `allow`, `always` (also adds the
1737
+ always-allow rule for the project; `--everywhere` for every project) or `deny` for a permission request, `--pick` for an
1738
+ agent's question in one, `--text` for a question, `land`, `keep` or `discard` for finished work, and `--keep <task-id>`
1739
+ (or `keep`, `discard`) for Later tasks. The agent goes on as soon as you answer.
1740
+
1741
+ **Approvals.** The approval mode answers your agents' permission requests before they reach you: `ask` sends every one
1742
+ to you, `routine` (the default) approves tests, builds, reads and installs, `trusted` approves everything except
1743
+ deploys, pushes to main and spending. `shard approvals` shows the modes and rules. `shard approvals set <mode>` sets the
1744
+ project's mode (`--project <id>`; `default` puts it back on the default mode), `shard approvals allow <tool>
1745
+ <pattern...>` always allows a tool for commands that start with the pattern (`shard approvals allow Bash pnpm test`),
1746
+ and `shard approvals remove <rule-id>` removes a rule. Requests already waiting that a new mode or rule covers are
1747
+ answered at once.
1748
+
1749
+ Each line is one item, tab-separated, id first. `--json` prints the API's objects; `shard board --follow --json` prints
1750
+ the board on its first line, then one feed answer per line.
1751
+
1608
1752
  ## Codex Cloud (0.15.0+)
1609
1753
 
1610
1754
  New Codex conversations use `shard agents new --app codex --path <directory>` or `shard ./ codex`. Each gets a fresh conversation VM and a genuine Codex Cloud thread. `shard agents open <conversation-id>` opens the existing thread without waking its VM. These paths create no SSH device entries and need no pairing.
@@ -1629,3 +1773,104 @@ its result while descendant drain can remain pending. TS/Python agent tools and
1629
1773
  Older clients read the same session response fields.
1630
1774
 
1631
1775
  Cloud history (0.23.0+): `shard agents open <conversation-id> --original` opens the original chat; `--history <thread-id>` opens an earlier linked chat. `--json` includes `previous_threads` with each history URL. Sign in with the Codex account that owns the selected chat.
1776
+
1777
+ Codex accounts (0.25.0+): before opening a Cloud thread, `shard agents open` checks which ChatGPT account the Codex app on this computer is signed in to. When it is not the thread's account, it names the account to sign in as and prints the thread's web link. Earlier threads are listed with their accounts.
1778
+
1779
+ ## Complete conversation lists (0.23.1+)
1780
+
1781
+ `shard conversations feed` (0.25.0+) prints each conversation of the project the moment it changes, until you stop it:
1782
+ `<id> <title> <state>`, or with `--json` one `{conversation, cursor}` object per line (`--cursor` continues after
1783
+ it). `shard conversations list --updated-after <time>` lists only conversations changed since then. Titles show the
1784
+ agent's own short name for a conversation once it has one (`title_short`).
1785
+
1786
+ Signed in (`shard auth login`) without an API key, `shard conversations list` and `shard conversations feed` (0.25.0+)
1787
+ cover every project you work in, across your organizations, with one cursor; `--org <id|slug>` narrows them to one
1788
+ organization and `--project` to one project. With an API key they cover the key's project, and `--org` uses your
1789
+ sign-in when you have one:
1790
+
1791
+ ```sh
1792
+ shard c list --org acme --state needs_you
1793
+ shard c feed --org acme --json
1794
+ ```
1795
+
1796
+ `shard conversations list --all` reads every page, keeping the selected project, state and parent filters. `--json` returns `data` and `nextCursor`. Linux desktop 0.1.11+ reads all conversation and computer pages and shows the selected project key’s organization, usage, spend and connector status in Settings.
1797
+
1798
+ ## Project drive (0.25.0+)
1799
+
1800
+ `shard drive` keeps a project's files: the screenshots, logs, reports and notes your agents and people save and read by
1801
+ path, the same from a phone, a script or another agent whether the project's computers are working, asleep or gone.
1802
+ Paths are relative to the project space root, the names agents see under `/shardflux/project`
1803
+ (`tasks/t_ab12/evidence/healthz.png`, `BOARD.md`).
1804
+
1805
+ ```sh
1806
+ shard drive put ./out/healthz.png tasks/t_ab12/evidence/healthz.png
1807
+ # Saved tasks/t_ab12/evidence/healthz.png (48.2 KB, image/png)
1808
+ # sha256 <sha256>
1809
+ shard drive put BOARD.md # saved as BOARD.md
1810
+ git diff | shard drive put - tasks/t_ab12/review.diff
1811
+ shard drive ls tasks/t_ab12/
1812
+ # SIZE SAVED TYPE PATH
1813
+ # 48.2 KB 2026-10-15 23:13:47Z image/png tasks/t_ab12/evidence/healthz.png
1814
+ # 24 bytes 2026-10-15 23:13:48Z text/markdown; charset=utf-8 tasks/t_ab12/notes.md
1815
+ shard drive get tasks/t_ab12/evidence/healthz.png -o healthz.png
1816
+ shard drive get tasks/t_ab12/notes.md --sha256 <sha256> # exactly that version
1817
+ shard drive rm tasks/t_ab12/notes.md
1818
+ ```
1819
+
1820
+ - Every `put` is a new version named by the SHA-256 of its bytes, printed after the path; saving the bytes that are
1821
+ already current keeps that version. `get --sha256` reads any version, also after its path was overwritten or
1822
+ removed, so a report that names a file's sha256 always opens the bytes it was written with.
1823
+ - `get` writes the bytes to standard output, or to a file with `-o`, once they match their SHA-256; with `--json` it
1824
+ returns `content_base64` with `sha256`, `size` and `content_type`.
1825
+ - `put <local-file> [<drive-path>]`: the drive path defaults to the file's name, and `-` reads standard input. The
1826
+ type comes from the extension (`.png` image/png, `.md` text/markdown) unless `--content-type` names one. `--json`
1827
+ returns the saved `file`.
1828
+ - `ls [prefix]` lists the current files by path, 200 to a page (`--limit` up to 1000, `--cursor`, `--all`); `--json`
1829
+ returns `data` (each file with its `sha256`) and `next_cursor`.
1830
+ - The drive is the API key's project, or `--project <id>`. Signed in (`shard auth login`), `--project` takes any of
1831
+ your projects by id or slug, and without an API key the drive is your default project's.
1832
+
1833
+ ## Model template (0.25.0+)
1834
+
1835
+ `shard models` shows which harness, model and effort each role of a task runs with, from the Claude and Codex accounts
1836
+ your organization connected: master plans and coordinates, builder does the work, hard takes the hardest parts, light
1837
+ the small ones and reviewer reviews with a fresh context.
1838
+
1839
+ ```sh
1840
+ shard models
1841
+ # Primary: claude (connected first), secondary: codex
1842
+ # Connected: claude (max), codex (pro)
1843
+ #
1844
+ # ROLE HARNESS MODEL EFFORT WAITS UNTIL
1845
+ # master claude opus medium (plans high) -
1846
+ # builder claude opus high -
1847
+ # hard claude fable high -
1848
+ # light codex gpt-6.1-sol low -
1849
+ # reviewer codex gpt-6.1-sol medium -
1850
+ shard models primary codex # this project's primary
1851
+ shard models primary claude --org acme # the organization's default
1852
+ shard models primary clear
1853
+ ```
1854
+
1855
+ - A role's harness, model and effort go straight to `shard conversations start --app <harness> --model <model>
1856
+ --effort <effort>` (`default`: leave out `--effort`).
1857
+ - `--harness claude|codex` keeps a running task's master, builder and hard work on its provider; `--attempt <n>` (1 to
1858
+ 10) shows the template for a retry, on the other provider or one tier up.
1859
+ - A role whose account waits for its next usage window shows until when, and what the other provider would run now,
1860
+ to offer the person; nothing switches on its own.
1861
+ - The primary provider is the project's choice when it has one, else the organization's, else the provider connected
1862
+ first. `shard models primary` sets the project's (owners and admins, or the project's API key), and with `--org` the
1863
+ organization's default (owners and admins, signed in); `clear` removes the choice. `--json` returns the template:
1864
+ `primary`, `primary_source`, `secondary`, `connected` and `roles`.
1865
+
1866
+ ## Default shared volumes (0.24.0+)
1867
+
1868
+ ```sh
1869
+ shard volumes attach --machine datasets --mount /mnt/datasets
1870
+ shard volumes attach --template cache --mount /mnt/cache --read-only --path ./repo
1871
+ shard machine --json
1872
+ shard template --json
1873
+ shard volumes detach --machine datasets
1874
+ ```
1875
+
1876
+ Defaults mount in new and running conversations. Removal waits for current work to finish and preserves manual attachments. Add `--json` to read propagation results.
@@ -269,6 +269,11 @@ export const ACCOUNT_COMMANDS = [
269
269
  options: { confirm: { type: 'string', value: '<slug>', description: 'The organization’s slug, typed out (required).' }, ...YES('the deletion') },
270
270
  },
271
271
  { path: ['projects', 'list'], summary: 'List the organization’s projects (* marks the default)', positionals: [], options: { ...ORG } },
272
+ { path: ['projects', 'link'], summary: 'Make a local folder one of your projects, prepared in the background and kept as this computer’s live copy (0.25.0+)', positionals: [{ name: 'path', required: true }], options: {
273
+ prompt: { type: 'string', value: '<text>', description: 'Also start a conversation in that folder with this first message.' },
274
+ 'prompt-file': { type: 'string', value: '<file|->', description: 'Read that first message from a UTF-8 file or stdin (-), up to 256 KiB.' },
275
+ app: { type: 'string', value: '<claude|codex>', description: 'Agent app of that conversation (default claude).' },
276
+ } },
272
277
  { path: ['projects', 'sync', 'list'], summary: 'Show selected local projects and their preparation state', positionals: [], options: {} },
273
278
  { path: ['projects', 'sync', 'add'], summary: 'Prepare and keep this selected local project synced', positionals: [{ name: 'path', required: true }], options: { wait: { type: 'boolean', description: 'Prepare now and wait for its published environment; otherwise the device helper prepares it in the background.' } } },
274
279
  { path: ['projects', 'sync', 'pause'], summary: 'Pause background preparation of this local project', positionals: [{ name: 'path', required: true }], options: {} },
@@ -463,11 +468,11 @@ export const ACCOUNT_COMMANDS = [
463
468
  {
464
469
  path: ['volumes', 'attach'],
465
470
  summary: 'Attach a volume to a workspace at a path',
466
- positionals: [{ name: 'ws', required: true }, { name: 'volume', required: true }],
467
- options: { mount: { type: 'string', value: '<path>', description: 'Absolute guest path, e.g. /mnt/data (required).' }, 'read-only': { type: 'boolean', description: 'Mount read-only (default read-write).' }, ...VOLUME_WAIT },
471
+ positionals: [{ name: 'ws|volume' }, { name: 'volume' }],
472
+ options: { machine: { type: 'boolean', description: 'Apply to every machine conversation.' }, template: { type: 'boolean', description: 'Apply to this repository’s conversations.' }, path: { type: 'string', value: '<path>', description: 'Repository folder for --template (default cwd).' }, mount: { type: 'string', value: '<path>', description: 'Absolute guest path, e.g. /mnt/data (required).' }, 'read-only': { type: 'boolean', description: 'Mount read-only (default read-write).' }, ...VOLUME_WAIT },
468
473
  examples: ['shard volumes attach acme/demo datasets --mount /mnt/datasets --read-only'],
469
474
  },
470
- { path: ['volumes', 'detach'], summary: 'Detach a volume from a workspace', positionals: [{ name: 'ws', required: true }, { name: 'volume', required: true }], options: { ...VOLUME_WAIT } },
475
+ { path: ['volumes', 'detach'], summary: 'Detach a volume from a workspace', positionals: [{ name: 'ws|volume' }, { name: 'volume' }], options: { machine: { type: 'boolean', description: 'Remove a machine default.' }, template: { type: 'boolean', description: 'Remove a project template default.' }, path: { type: 'string', value: '<path>', description: 'Repository folder for --template.' }, ...VOLUME_WAIT } },
471
476
  // ---- templates with the session ------------------------------------------------------------------------
472
477
  {
473
478
  path: ['templates', 'publish'],
@@ -1,3 +1,4 @@
1
+ import { defaultVolumeCommand } from "./default-volume-commands.js";
1
2
  import { prepareOnboardingSample } from "./onboarding-sample.js";
2
3
  import { onboardingReport, onboardingText } from "./onboarding.js";
3
4
  /**
@@ -1950,7 +1951,13 @@ export const ACCOUNT_HANDLERS = {
1950
1951
  out(ctx, page.data.length ? attachmentTable(page.data) : `${v.name} is not attached.\n`, { data: page.data, next_cursor: page.nextCursor });
1951
1952
  return 0;
1952
1953
  },
1953
- async 'volumes attach'(ctx, values, [wsRef, volRef]) {
1954
+ async 'volumes attach'(ctx, values, args) {
1955
+ const done = await defaultVolumeCommand(ctx, values, args, 'attach');
1956
+ if (done !== undefined)
1957
+ return done;
1958
+ const [wsRef, volRef] = args;
1959
+ if (!wsRef || !volRef)
1960
+ throw new UsageError('Supply a workspace and volume, or --machine/--template with one volume');
1954
1961
  const mount = need(values, 'mount', '--mount /mnt/data');
1955
1962
  if (!mount.startsWith('/'))
1956
1963
  throw new UsageError('--mount must be an absolute path, e.g. /mnt/data');
@@ -1961,7 +1968,13 @@ export const ACCOUNT_HANDLERS = {
1961
1968
  out(ctx, `Attached ${v.name} to ${ws.key} at ${a.mount_path} (${a.mode}): ${a.state}${a.mounted ? ', mounted' : ', mounted on the next start'}.\n`, r);
1962
1969
  return 0;
1963
1970
  },
1964
- async 'volumes detach'(ctx, values, [wsRef, volRef]) {
1971
+ async 'volumes detach'(ctx, values, args) {
1972
+ const done = await defaultVolumeCommand(ctx, values, args, 'detach');
1973
+ if (done !== undefined)
1974
+ return done;
1975
+ const [wsRef, volRef] = args;
1976
+ if (!wsRef || !volRef)
1977
+ throw new UsageError('Supply a workspace and volume, or --machine/--template with one volume');
1965
1978
  const ws = await resolveWorkspace(ctx, wsRef);
1966
1979
  const v = await resolveVolume(ctx, volRef);
1967
1980
  const r = await waitedVolume(ctx, () => ctx.cloud().volumes.detach(ws.id, v.id, { wait: volumeWait(ctx, values) }));
@@ -2,6 +2,7 @@ import { out, resolveWorkspace } from "./commands.js";
2
2
  import { UsageError } from "./errors.js";
3
3
  import { claudeLoginUrls, requireClaudeTerminal, runWorkspaceLogin } from "./claude-cloud-login.js";
4
4
  import { openMachine } from "./filesystem-commands.js";
5
+ import { codexAppAccountNotice } from "./codex-cloud.js";
5
6
  const PROVIDERS = { claude: 'Claude', codex: 'ChatGPT / Codex' };
6
7
  const WINDOW_LABELS = { five_hour: '5-hour', seven_day: 'Weekly', seven_day_opus: 'Weekly Opus', seven_day_sonnet: 'Weekly Sonnet', primary: 'Primary', secondary: 'Secondary' };
7
8
  /** A window's length as a title, as Claude's windows read: "Weekly", "5-hour", "2-day", "90-minute". */
@@ -245,7 +246,10 @@ export const ACCOUNT_POOL_HANDLERS = {
245
246
  const follows = account.provider === 'claude'
246
247
  ? 'New conversations start with it; running ones continue on it, and Remote Control turns back on by itself.'
247
248
  : `New conversations and every running conversation VM use it; Codex Cloud moves to it${previous ? ` (Cloud threads started on ${name(previous)} stay in that ChatGPT account)` : ''}.`;
248
- out(ctx, previous || !isActive(account) ? `${name(active)} is the active ${PROVIDERS[account.provider]} account. ${follows}\n` : `${name(active)} is already the active ${PROVIDERS[account.provider]} account.\n`, { ...list, account: active });
249
+ // codex_app (0.25.0+): the Codex app here opens the account's Cloud threads only once it is signed in to it.
250
+ const app = account.provider === 'codex' ? await codexAppAccountNotice(ctx, active) : null;
251
+ out(ctx, (previous || !isActive(account) ? `${name(active)} is the active ${PROVIDERS[account.provider]} account. ${follows}\n` : `${name(active)} is already the active ${PROVIDERS[account.provider]} account.\n`)
252
+ + (app?.line ? `${app.line}\n` : ''), { ...list, account: active, ...(account.provider === 'codex' ? { codex_app: app ? { login: app.login, notice: app.line } : null } : {}) });
249
253
  return 0;
250
254
  },
251
255
  };
@@ -5,12 +5,12 @@ export interface AcpCursor {
5
5
  from_seq: number;
6
6
  prompt_id?: RpcId;
7
7
  }
8
- export declare const ACP_INPUT_SCRIPT = "import json, os, socket, sys\npayload = sys.stdin.buffer.readline((2 << 20) + 1024)\nif not payload.endswith(b'\\n') or len(json.dumps(json.loads(payload)['message'], separators=(',', ':'), ensure_ascii=False).encode()) > 2 << 20:\n raise ValueError('ACP input exceeds 2 MiB')\nfolder = os.open(sys.argv[1], os.O_RDONLY)\ntry:\n with socket.socket(socket.AF_UNIX) as peer:\n peer.settimeout(10)\n address = os.path.join(sys.argv[1], 'control.sock')\n if len(address.encode()) >= 108: address = '/proc/self/fd/%d/control.sock' % folder\n peer.connect(address)\n peer.sendall(payload)\n response = peer.makefile('rb').readline(65536)\n result = json.loads(response)\n if result.get('accepted') is not True: raise ValueError(result.get('error', 'ACP input was not acknowledged'))\n sys.stdout.buffer.write(response)\nfinally:\n os.close(folder)\n";
8
+ export declare const ACP_INPUT_SCRIPT = "import json, os, socket, struct, sys\npayload = sys.stdin.buffer.readline((2 << 20) + 1024)\nif not payload.endswith(b'\\n') or len(json.dumps(json.loads(payload)['message'], separators=(',', ':'), ensure_ascii=False).encode()) > 2 << 20:\n raise ValueError('ACP input exceeds 2 MiB')\nfolder = os.open(sys.argv[1], os.O_RDONLY)\ntry:\n with socket.socket(socket.AF_UNIX) as peer:\n peer.settimeout(10)\n address = os.path.join(sys.argv[1], 'control.sock')\n if len(address.encode()) >= 108: address = '/proc/self/fd/%d/control.sock' % folder\n peer.connect(address)\n expected_pid, expected_start = sys.argv[2].split(':', 1)\n pid, uid, _ = struct.unpack('3i', peer.getsockopt(socket.SOL_SOCKET, socket.SO_PEERCRED, struct.calcsize('3i')))\n with open('/proc/%d/stat' % pid) as process:\n start = process.read().rsplit(')', 1)[1].split()[19]\n if uid != os.getuid() or str(pid) != expected_pid or start != expected_start:\n raise ValueError('ACP supervisor changed before input; load this session before continuing')\n peer.sendall(payload)\n response = peer.makefile('rb').readline(65536)\n result = json.loads(response)\n if result.get('accepted') is not True: raise ValueError(result.get('error', 'ACP input was not acknowledged'))\n sys.stdout.buffer.write(response)\nfinally:\n os.close(folder)\n";
9
9
  /** Replay is finite; a live follower exists only while a turn/RPC needs output. Native supervision is CVB's. */
10
10
  export declare class AcpJournal {
11
11
  #private;
12
12
  readonly rpc: AcpRpc;
13
- constructor(cell: CellClient, conversation: string, _harness: 'claude' | 'codex', cwd: string, cursor: AcpCursor, save: () => void, signal: AbortSignal);
13
+ constructor(cell: CellClient, conversation: string, _harness: 'claude' | 'codex', cwd: string, cursor: AcpCursor, save: () => void, signal: AbortSignal, owner: string);
14
14
  start(): Promise<void>;
15
15
  pause(): void;
16
16
  resume(): void;