@shardflux/cli 0.14.4 → 0.15.0
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.
- package/CHANGELOG.md +66 -0
- package/README.md +73 -11
- package/dist/account-args.js +28 -7
- package/dist/account-commands.js +140 -11
- package/dist/acp-catalog.js +2 -2
- package/dist/acp-login.d.ts +20 -0
- package/dist/acp-login.js +122 -0
- package/dist/acp.js +36 -13
- package/dist/agent-commands.js +58 -6
- package/dist/agent-git-commands.js +2 -1
- package/dist/agent-reachability.js +1 -1
- package/dist/agent-target-commands.js +26 -1
- package/dist/agent-targets.d.ts +7 -0
- package/dist/agent-targets.js +44 -32
- package/dist/agent-terminal.d.ts +24 -2
- package/dist/agent-terminal.js +82 -4
- package/dist/args.d.ts +3 -1
- package/dist/args.js +21 -9
- package/dist/browser-login.d.ts +35 -0
- package/dist/browser-login.js +145 -0
- package/dist/claude-cloud-login.js +2 -2
- package/dist/codex-cloud.d.ts +49 -0
- package/dist/codex-cloud.js +157 -0
- package/dist/command-center-actions.js +7 -0
- package/dist/command-center-renderer.d.ts +3 -0
- package/dist/command-center-renderer.js +42 -38
- package/dist/command-center.js +65 -11
- package/dist/conversation-terminal.js +19 -0
- package/dist/conversations.d.ts +1 -1
- package/dist/conversations.js +21 -8
- package/dist/credential-prompts.d.ts +4 -2
- package/dist/credential-prompts.js +38 -16
- package/dist/device-bridge.d.ts +13 -0
- package/dist/device-bridge.js +14 -4
- package/dist/device-helper.js +2 -1
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +15 -2
- package/dist/handlers.js +2 -1
- package/dist/initial-prompt.d.ts +26 -0
- package/dist/initial-prompt.js +196 -0
- package/dist/legal.d.ts +36 -0
- package/dist/legal.js +44 -0
- package/dist/main.js +6 -2
- package/dist/menubar.d.ts +8 -0
- package/dist/menubar.js +9 -1
- package/dist/onboarding-app/AGENTS.md +19 -0
- package/dist/onboarding-app/CLAUDE.md +19 -0
- package/dist/onboarding-app/README.md +12 -0
- package/dist/onboarding-app/dev.mjs +18 -0
- package/dist/onboarding-app/index.html +25 -0
- package/dist/onboarding-app/package.json +12 -0
- package/dist/onboarding-app/server.mjs +41 -0
- package/dist/onboarding-sample.d.ts +3 -0
- package/dist/onboarding-sample.js +29 -0
- package/dist/onboarding.d.ts +258 -0
- package/dist/onboarding.js +371 -0
- package/dist/terminal-brand.d.ts +5 -0
- package/dist/terminal-brand.js +18 -0
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,71 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.15.0
|
|
4
|
+
|
|
5
|
+
- The onboarding activity report includes the conversation's turn state for Claude and Codex, including while its VM is suspended.
|
|
6
|
+
|
|
7
|
+
- `shard setup --inspect`: a new Codex remote device reads as connecting while it finishes starting, instead of offering Retry. The live activity report lists only the ports the agent opened, not the computer's own services.
|
|
8
|
+
|
|
9
|
+
- The terms: `shard signup`, `shard auth register` and `shard auth login --codex` (which can create an account) need the agreement to the Terms of Service and Privacy Policy before they send anything: `--accept-terms`, `SHARDFLUX_ACCEPT_TERMS=1`, or "Continue? [Y/n]" on a terminal. Without it they stop with `terms_not_accepted` (exit 2; `details`: `terms_url`, `privacy_url`, `flag`). The account records the agreement. Signing in shows the same line, and `--json` sign-in and sign-up outputs carry `terms_url` and `privacy_url`.
|
|
10
|
+
|
|
11
|
+
- `shard ./` and `shard agents new` accept `--image <file|->` with an initial prompt: attach up to five PNG, JPEG, GIF or WebP images (10 MiB each) to the first message. Images are validated before the VM is created, written privately into the conversation VM under `/home/user/.shardflux/attachments/`, and named in the first message, so Claude Code attaches them natively and Codex opens them with its image viewer. `--image -` reads one image, or PNG images back to back, from standard input, so the menu bar's screen captures never touch the Mac's disk.
|
|
12
|
+
|
|
13
|
+
- `shard auth login --browser` signs in on the web: it opens the sign-in page with a code (`--no-open` prints the link), you sign in or create your account there, check the code and approve, and the session is saved as with a password. `--client-name` names the computer on the page. It prints the terms notice (Terms of Service, Privacy Policy) before the browser opens. `--json` prints NDJSON events for apps: `open` (link, code, `expires_in`, `terms_url`, `privacy_url`), then `signed_in` (email) or `error` (`access_denied`, `expired_token`, ...; exit 4). On a terminal, `shard auth login` without `--email` or `--codex`, and `shard signup` without a Codex login or `--email`, use the browser.
|
|
14
|
+
|
|
15
|
+
- `shard acp` signs in from the editor: it offers T3 Code and other ACP clients a "Sign in to Shardflux" method that runs in the editor's own terminal (`shard acp --login`). One step creates or signs in to the account with this Mac's Codex login (else an email), makes the API key, copies the Codex login and connects Claude Code. Signed out, `shard acp` answers ACP `auth_required`, so the editor offers it.
|
|
16
|
+
|
|
17
|
+
- T3 Code's provider probe and its post-sign-in check open no VM, and a conversation started from the probe's first model choice (`Codex · Default`, `Claude Code · Default`) opens on that app's own default model.
|
|
18
|
+
|
|
19
|
+
- `shard agents open` on a Claude conversation this computer just started returns its Remote Control URL as soon as Claude publishes it, waiting up to 30 s (or `--wake-timeout`, if shorter) while Claude registers.
|
|
20
|
+
|
|
21
|
+
- Resume an ACP conversation after its journal is reconstructed on the same VM: reset the old journal cursor when the native supervisor changes, while preserving the native session id.
|
|
22
|
+
|
|
23
|
+
- Keep legacy workspace opens, explicit terminal/T3 launches and template-copy launches on their existing paths; prepare local Git before Codex Cloud enrollment.
|
|
24
|
+
|
|
25
|
+
- Claude conversations wake from claude.ai or the Claude app at any time. `--reachable` (`agent_reachable_seconds` on the wire) is deprecated and ignored; the option remains accepted and is hidden from help.
|
|
26
|
+
|
|
27
|
+
- Idle Codex Cloud MCP sessions no longer keep the VM awake. Stdio servers retain their process and memory in the VM behind request-scoped HTTP, so normal adaptive suspend and wake preserve their state.
|
|
28
|
+
- New Codex Cloud threads receive the VM’s configured MCP servers and Shardflux computer, conversation and credential tools through the native remote executor. No separate connector setup is required.
|
|
29
|
+
- New Codex conversations from `shard ./` and `shard agents new --app codex --path <dir>` open genuine Codex Cloud threads under one account executor. `agents open` reopens Cloud history without waking its VM. Setup copies the existing Codex login and registers the billed account executor; `agents codex status|enable|disable` manages it. Existing local conversations retain their original route.
|
|
30
|
+
|
|
31
|
+
- Add `credentials approve --scope ask|project|machine` and scoped `credentials forget`. Share machine vault choices across projects in the current account/profile, with project choices taking precedence. Remember only the requested fields at the selected scope.
|
|
32
|
+
|
|
33
|
+
- Simplify the terminal command center to one-line live conversations, collapsed done sessions, and a contextual actions menu. New conversation asks for a task and project, then starts Claude or Codex with that first message. Existing open, target, log, workspace and Git actions remain available.
|
|
34
|
+
|
|
35
|
+
- Give the menu bar structured device approval details (exact argv, canonical path, file direction and port mapping) while retaining the summary for older clients. Outside-conversation requests still require one-time approval.
|
|
36
|
+
|
|
37
|
+
- Fresh conversations accept `--prompt` or `--prompt-file <path|->` as their first native user-authored message. Validate before allocation, deliver through acknowledged native terminal input, and preserve the created workspace key on a delivery failure. Prepare fresh Codex worktrees to avoid startup trust/update dialogs and use a usable terminal type for detached launchers.
|
|
38
|
+
- Emit device approval resolution events so the menu removes completed requests immediately.
|
|
39
|
+
|
|
40
|
+
- `agents new --path` starts a fresh conversation; named and `--like` agent flows keep their existing behavior.
|
|
41
|
+
- `agents open --in auto --json` returns the remembered desktop target or the agent app destination (`url` or `app_bundle_id`) for launchers such as menu bar 0.3.0.
|
|
42
|
+
|
|
43
|
+
- First run: `shard setup --inspect [--json] [--apps claude,codex] [--connect]` checks every connection live and
|
|
44
|
+
reports it: the Shardflux account, Claude (Claude itself accepts the account login) and Codex (the login is copied and
|
|
45
|
+
the account's Codex remote device is connected), for the apps you pick, or the ones found on this Mac. `--connect`
|
|
46
|
+
also copies this Mac's Codex login and enables the Codex remote device; `--retry-codex` starts a remote device that
|
|
47
|
+
failed or is stuck starting again, once (as `agents codex enable` now does). The Codex row reads its remote device as
|
|
48
|
+
connecting, connected, failed (its VM failed) or stuck (starting for more than five minutes). The apps checked are
|
|
49
|
+
`--apps`, else your saved pick, else the ones found on this Mac, including in their install folders (`~/.local/bin`
|
|
50
|
+
and others) when the PATH is minimal. Ready means signed in with at least one
|
|
51
|
+
agent app connected. When T3 Code is installed the report adds an optional `t3` entry: an ACP-capable build has
|
|
52
|
+
Shardflux as a provider (connected once signed in; its provider's Sign in finishes it otherwise), stable 0.0.45 is
|
|
53
|
+
set up per project over SSH (`agents new --in t3 --path <project>`) and names the environment to add. The report
|
|
54
|
+
lists the steps (sign in, connect, try it once on Daylight, your own project). `--prepare-sample` copies the Daylight sample app into a new local Git project,
|
|
55
|
+
"Daylight (sample)"; `--activity --workspace <key|conv_id>` lists a running VM's current commands and when they
|
|
56
|
+
started, listening ports, packages installed with sudo since it was created, its desktop's size and browser window,
|
|
57
|
+
and what changed in its repositories, without waking it.
|
|
58
|
+
- `agents new --computer` (and `shard <path> --computer`) gives the new conversation a desktop with a browser its agent
|
|
59
|
+
drives, for Claude and Codex conversations.
|
|
60
|
+
- A long first prompt (`--prompt`, `--prompt-file`) is submitted even when its start scrolls out of the agent's input box.
|
|
61
|
+
- `agents open --in web` (and `--in auto` for Claude) waits up to 20 seconds for a new conversation's Remote Control link
|
|
62
|
+
instead of failing in the first seconds after the conversation starts.
|
|
63
|
+
|
|
64
|
+
## 0.14.5
|
|
65
|
+
|
|
66
|
+
- An agent terminal no longer keeps its workspace awake. `shard ./` and `shard agents open` attach passively: while the agent waits, its workspace parks and suspends under the open terminal. Once it has gone to sleep, the terminal shows `asleep. Type to wake it.`, and your next keystroke wakes it and reaches the agent. A dropped connection reattaches without waking the workspace.
|
|
67
|
+
- Requires `@shardflux/sdk` 0.20.0: `shard ws tunnel` and the device bridge carry a reset on either side as a reset (`ECONNRESET`), never as a clean end of stream.
|
|
68
|
+
|
|
3
69
|
## 0.14.4
|
|
4
70
|
|
|
5
71
|
- `shard <path> --codex` and `--claude` choose the agent like `shard <path> codex` and `claude`. An unknown option or app on `shard <path>` names the right form, e.g. `shard ./ codex`.
|
package/README.md
CHANGED
|
@@ -24,6 +24,14 @@ For usage, account, billing, or private support, email [shardflux@heliosone.fi](
|
|
|
24
24
|
Report vulnerabilities through [private security reporting](https://github.com/shardfluxdev/community/security/advisories/new).
|
|
25
25
|
See the [support guide](https://github.com/shardfluxdev/community/blob/main/SUPPORT.md) for all reporting options.
|
|
26
26
|
|
|
27
|
+
## Command center (0.15.0+)
|
|
28
|
+
|
|
29
|
+
Run `shard` in a terminal to see live conversations. Press **Enter** to open the selected one, **/** to search,
|
|
30
|
+
**D** to expand or collapse finished conversations, and **?** for targets, logs, fork, park and Git actions.
|
|
31
|
+
Press **n** to start a conversation: describe the task, confirm a local project folder, then choose Claude or Codex.
|
|
32
|
+
Both the task and project are required, and the task becomes the first user-authored message. **Esc** cancels;
|
|
33
|
+
**q** returns to your shell. The command center calls the same `shard agents new --path ... --prompt ...` path.
|
|
34
|
+
|
|
27
35
|
## Reverse tunnels (0.11.0+)
|
|
28
36
|
|
|
29
37
|
Run a foreground TCP tunnel from a workspace to a service on this machine:
|
|
@@ -33,7 +41,7 @@ shard ws tunnel customer-42/main --remote 8081 --to localhost:8081
|
|
|
33
41
|
```
|
|
34
42
|
|
|
35
43
|
Commands in the workspace can then call `http://127.0.0.1:8081`. The target is dialed on the machine running
|
|
36
|
-
`shard`. Ctrl-C closes the sockets and guest process. `--bind 0.0.0.0` selects all guest interfaces; loopback is the
|
|
44
|
+
`shard`. Half-close and resets pass through: a reset on either side reaches the other as `ECONNRESET` (0.14.5+). Ctrl-C closes the sockets and guest process. `--bind 0.0.0.0` selects all guest interfaces; loopback is the
|
|
37
45
|
default. An open tunnel keeps the workspace awake, and persistent workspaces preserve it across explicit suspend/resume.
|
|
38
46
|
|
|
39
47
|
The tunnel selects a streaming transport automatically when `pty` permission is granted. Choose `transport: 'pty'` or `transport: 'exec'` in the SDK, or `--transport pty|exec` in the CLI, to select it explicitly. The handle reports `transport`.
|
|
@@ -67,7 +75,10 @@ time `shard` runs, with launch at login enabled; `shard menubar stop` turns it o
|
|
|
67
75
|
|
|
68
76
|
T3 Code uses one **Shardflux** provider; choose a Claude Code or Codex model to start that agent in the cloud.
|
|
69
77
|
Model choices come from your cloud agent's session and use the same coding tools and settings as its terminal.
|
|
70
|
-
The provider is registered automatically when T3 Code is installed.
|
|
78
|
+
The provider is registered automatically when T3 Code is installed. Choose **Sign in** on its card in T3's provider
|
|
79
|
+
settings: T3 runs `shard acp --login` in its own terminal, which creates or signs in to your account (with this Mac's
|
|
80
|
+
Codex login, or your email) and connects Claude Code and Codex, skipping every step that is already done (0.15.0+).
|
|
81
|
+
With T3 Code 0.0.45, `shard agents new --in t3` adds the project as an SSH environment instead.
|
|
71
82
|
|
|
72
83
|
Git fetch and push use the coding guest’s platform transport when available, preserving Git’s binary packets (0.14.0+). Older guests retain their existing transport.
|
|
73
84
|
|
|
@@ -79,7 +90,7 @@ shard conversations try <conversation>
|
|
|
79
90
|
|
|
80
91
|
Your branch opens in `.shard/worktrees/<name>`, syncs both ways, and its dev servers appear at localhost.
|
|
81
92
|
Ctrl-C stops local sync. Your main checkout stays available for your own work.
|
|
82
|
-
Device tools ask
|
|
93
|
+
Device tools ask for approval on your Mac 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
|
|
83
94
|
`~/.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+).
|
|
84
95
|
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.
|
|
85
96
|
|
|
@@ -136,14 +147,19 @@ npm test 2>&1 | tail -n 60 | shard feedback --category bug --workspace acme/demo
|
|
|
136
147
|
`shard` is built to be driven by an agent: no browser, no prompts when standard input is not a
|
|
137
148
|
terminal, `--json` on every command, secrets only on standard input, and documented exit codes.
|
|
138
149
|
|
|
139
|
-
1. **Account (0.9.0+):**
|
|
150
|
+
1. **Account (0.9.0+):** show your person the [Terms of Service](https://shardflux.dev/terms) and the
|
|
151
|
+
[Privacy Policy](https://shardflux.dev/privacy); once they agree, `shard signup --accept-terms --json` (0.15.0+:
|
|
152
|
+
without `--accept-terms` or a terminal to ask on, it stops before sending anything with `terms_not_accepted`, exit 2,
|
|
153
|
+
and the two links in `details`). With the Codex CLI signed in with ChatGPT on the
|
|
140
154
|
machine it signs up (or signs in) verified by that login; otherwise pass `--email <email>` and the
|
|
141
155
|
account works at once on the Free plan. It then runs `shard setup`: an organization, a project and
|
|
142
156
|
an API key with every workspace tool, saved in the credentials file. With two-factor authentication
|
|
143
157
|
it exits 4 with `mfa_required`: ask the person for the code, then `shard auth mfa --code <code>`.
|
|
144
158
|
2. **Verify the email** (an `--email` signup, to start the trial): `shard verify --json` emails a
|
|
145
159
|
6-digit code; ask the person for it, then `shard verify <code>`. Nothing is signed out.
|
|
146
|
-
3. **Set up again any time:** `shard setup --json` is idempotent.
|
|
160
|
+
3. **Set up again any time:** `shard setup --json` is idempotent. `shard setup --inspect --json --apps claude,codex`
|
|
161
|
+
(0.15.0+) checks every connection live (your account, Claude, Codex) and says what to do for any that is not ready;
|
|
162
|
+
`--connect` also finishes the Codex connection.
|
|
147
163
|
4. **Work:** `shard exec <key> --template default -- <cmd>` creates the workspace on first use and runs the command
|
|
148
164
|
(0.10.0+); later commands reuse it. `shard ws open <key> --template <slug> --json`, `shard files ...` use the saved
|
|
149
165
|
key too.
|
|
@@ -164,10 +180,11 @@ What a person does: entering a card in Stripe Checkout, the authenticator app's
|
|
|
164
180
|
## Accounts and sign-in (0.5.0+)
|
|
165
181
|
|
|
166
182
|
```sh
|
|
167
|
-
shard signup [--email you@example.com] [--name "Ada Lovelace"] [--no-codex] [--no-setup] # 0.9.0+
|
|
183
|
+
shard signup [--email you@example.com] [--name "Ada Lovelace"] [--no-codex] [--no-setup] [--accept-terms] # 0.9.0+
|
|
168
184
|
shard verify [<code>] # 0.9.0+: emails a 6-digit code, then confirms it
|
|
169
|
-
shard auth login --
|
|
170
|
-
|
|
185
|
+
shard auth login --browser [--no-open] # 0.15.0+: approve this computer on the sign-in page
|
|
186
|
+
shard auth login --codex [--accept-terms] # 0.9.0+: sign in with the Codex login on this machine
|
|
187
|
+
printf '%s\n' "$PASSWORD" | shard auth register --email you@example.com [--name "Ada Lovelace"] [--accept-terms]
|
|
171
188
|
shard auth verify-email '<link from the email>' # or just the token in it
|
|
172
189
|
shard auth resend-verification # signed in, email not verified yet
|
|
173
190
|
printf '%s\n' "$PASSWORD" | shard auth login --email you@example.com [--code 123456 | --recovery-code X]
|
|
@@ -193,6 +210,24 @@ shard auth totp recovery-codes
|
|
|
193
210
|
already uses that ChatGPT identity or email is signed in instead. Without a Codex login, `--email`
|
|
194
211
|
creates an account that works at once on the Free plan; `shard verify` starts the trial.
|
|
195
212
|
`--no-codex` uses `--email` even when Codex is signed in; `--no-setup` skips `shard setup`.
|
|
213
|
+
- **`shard auth login --browser`** (0.15.0+) opens the sign-in page (`https://app.shardflux.dev/cli?code=XXXX-XXXX`)
|
|
214
|
+
in the default browser and prints the link and the code. Sign in or create your account on the page, check that it
|
|
215
|
+
shows the same code, and approve: `shard` saves the session like a password sign-in, and two-factor accounts use
|
|
216
|
+
their code on the page. `--no-open` only prints the link; `--client-name` sets the name the page shows (default
|
|
217
|
+
`shard CLI on <hostname>`). The code expires after 10 minutes. On a terminal, `shard auth login` without `--email`
|
|
218
|
+
or `--codex`, and `shard signup` without a Codex login or `--email`, sign in this way. With `--json` it prints one
|
|
219
|
+
JSON object per line: `{"event":"open","verification_uri_complete":"…","user_code":"ABCD-EFGH","expires_in":600,
|
|
220
|
+
"terms_url":"https://shardflux.dev/terms","privacy_url":"https://shardflux.dev/privacy"}`, then
|
|
221
|
+
`{"event":"signed_in","email":"…"}`, or `{"event":"error","code":"access_denied","message":"…"}` (also
|
|
222
|
+
`expired_token`, exit 4). Before the browser opens it prints the terms notice: by continuing you agree to the
|
|
223
|
+
Terms of Service and acknowledge the Privacy Policy.
|
|
224
|
+
- **The terms (0.15.0+):** the commands that can create an account (`signup`, `auth register`,
|
|
225
|
+
`auth login --codex`) need your agreement to the [Terms of Service](https://shardflux.dev/terms) and the
|
|
226
|
+
[Privacy Policy](https://shardflux.dev/privacy): `--accept-terms` (or `SHARDFLUX_ACCEPT_TERMS=1`), or "Continue?
|
|
227
|
+
[Y/n]" on a terminal. Without either they stop before sending anything: `terms_not_accepted`, exit 2, with
|
|
228
|
+
`terms_url`, `privacy_url` and `flag` in `details`. The account records the agreement. Signing in only shows the
|
|
229
|
+
line (`terms_url` and `privacy_url` in `--json`); so does `auth login --browser`, which needs no flag: an account
|
|
230
|
+
created on the sign-in page agrees there.
|
|
196
231
|
- **Passwords never come from arguments** (`--password` is refused). On a terminal `shard` asks without
|
|
197
232
|
echo (a new password twice); otherwise it reads standard input, one line per secret: `auth password
|
|
198
233
|
change` reads the current password, then the new one. Emailed links and tokens are single-use
|
|
@@ -980,7 +1015,7 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
|
|
|
980
1015
|
- Account commands (0.5.0+) print the API's objects too: lists are `{data, next_cursor}` (with
|
|
981
1016
|
`default_organization_id`, `default_project_id` or `saved_api_key_id` where a default applies);
|
|
982
1017
|
`auth login`/`auth mfa` print `{status, user, session: {expires_at, credentials_file}, organizations,
|
|
983
|
-
next}` (never the token); `auth status` prints `{api_url, credentials_file, session, api_key, defaults}`;
|
|
1018
|
+
next}` (never the token; `auth login --browser` prints NDJSON events instead, see above); `auth status` prints `{api_url, credentials_file, session, api_key, defaults}`;
|
|
984
1019
|
`setup` prints `{organization, project, api_key: {id, key_id, name, project_id, tool_permissions},
|
|
985
1020
|
created: {organization, project, api_key}, credentials_file, next}`; `api-keys create` prints
|
|
986
1021
|
`{api_key, secret, saved: false}` (or `{api_key, saved: true, credentials_file}` with `--save`);
|
|
@@ -1009,7 +1044,7 @@ printf %s "$NEW_KEY" | shard secrets rotate OPENAI_API_KEY
|
|
|
1009
1044
|
| 1 | error: an API refusal such as `conflict`, `quota_exceeded` or `workspace_not_running`, or a network or protocol failure |
|
|
1010
1045
|
| 2 | usage error: bad arguments, or input the API rejected as `validation_failed` |
|
|
1011
1046
|
| 3 | not found: workspace, operation or route |
|
|
1012
|
-
| 4 | authentication or authorization: missing, malformed, revoked or insufficient key or session; `step_up_required` (run `shard auth step-up`), `mfa_required` (a sign-in waiting for its code), `email_unverified`, `invalid_credentials` |
|
|
1047
|
+
| 4 | authentication or authorization: missing, malformed, revoked or insufficient key or session; `step_up_required` (run `shard auth step-up`), `mfa_required` (a sign-in waiting for its code), `email_unverified`, `invalid_credentials`; `access_denied` or `expired_token` from `auth login --browser` (0.15.0+) |
|
|
1013
1048
|
| 5 | timeout: waiting gave up (`--timeout`, or `--wake-timeout` while waking a workspace); the operation continues server side (`shard operations wait <id>`), up to a queued start's 15-minute deadline. `executions get` (0.5.0+): the execution is still queued or running. `ws exec-read` (0.8.0+): the background command still runs. `billing upgrade --wait` (0.5.0+): nobody paid in time (the checkout stays open) |
|
|
1014
1049
|
| 6 | the awaited operation ended `failed` or `canceled` (also a resume or open that failed while waking a workspace). `retryable=true` in the error means a retry can succeed (`capacity_unavailable`). Also a file-first execution that ended `failed` or `lost` (0.5.0+): nothing was published. `exec` (0.5.1+): the command could not start (`exec_failed_to_start`). `billing upgrade --wait` (0.5.0+): the checkout expired or was canceled (`checkout_ended`) |
|
|
1015
1050
|
| 130 | interrupted (Ctrl-C). Waits stop at once, and exec sends a cancel to the session. A file-first execution runs to completion, and the error names the `shard executions get` command for its result. |
|
|
@@ -1148,7 +1183,7 @@ Template services accept `initialized_cpu` with `compatibility`, `reset`, and `t
|
|
|
1148
1183
|
|
|
1149
1184
|
### Waiting agent reachability (0.13.2+)
|
|
1150
1185
|
|
|
1151
|
-
|
|
1186
|
+
A message from claude.ai or the Claude app wakes a waiting Claude conversation at any time. Reply-traffic wakes count as parked time; agent work counts as awake time. The reachability duration is deprecated and ignored (0.15.0+).
|
|
1152
1187
|
|
|
1153
1188
|
|
|
1154
1189
|
## Cloud agents (0.13.2+)
|
|
@@ -1180,6 +1215,8 @@ Imported secrets: stored encrypted with AWS KMS; only this project's workspaces
|
|
|
1180
1215
|
Vault fills: sent end to end from your vault to the workspace; Shardflux only relays encrypted bytes.
|
|
1181
1216
|
Remembered credentials: stored encrypted with AWS KMS and filled straight into the page; agents never see it, and every use is logged.
|
|
1182
1217
|
|
|
1218
|
+
CLI 0.15.0 adds `credentials approve <request> --from keychain --item <name> --scope ask|project|machine`. The default still asks each time; `--always` retains its project-only behavior. Machine permission is shared across projects in the current account/profile, with project choices taking precedence. Add `--remember` to save only requested fields for unattended sign-in: `ask` saves for the conversation, `project` for the project template, and `machine` for the machine template. Remove a saved copy with `credentials forget <workspace> <site> --scope conversation|project|machine`. Without `--scope`, remember/forget retain the legacy project storage. Conversation and machine copies require the scoped-credentials API.
|
|
1219
|
+
|
|
1183
1220
|
[Start a cloud agent](https://docs.shardflux.dev/guides/cloud-agents) ·
|
|
1184
1221
|
[Machine and template](https://docs.shardflux.dev/guides/machine-and-template) ·
|
|
1185
1222
|
[Credentials](https://docs.shardflux.dev/guides/credentials)
|
|
@@ -1208,3 +1245,28 @@ Open that project connection to start conversations and return to their saved th
|
|
|
1208
1245
|
Conversation JSON includes `join_mode` and the worktree used by reviewers and workers.
|
|
1209
1246
|
|
|
1210
1247
|
`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.
|
|
1248
|
+
|
|
1249
|
+
### Menu bar conversation shortcut (0.15.0+)
|
|
1250
|
+
|
|
1251
|
+
The macOS menu bar's **New conversation** action (menu bar 0.3.0+) opens the same launcher as **Option-Shift-Space**. It requires a task and project, remembers Claude/Codex, and invokes the commands below. Configure the hotkey in the macOS Settings window (⌘,), with conflict detection and a fallback.
|
|
1252
|
+
|
|
1253
|
+
```sh
|
|
1254
|
+
shard agents new --app claude --path ./ --prompt "Review the billing changes" --json
|
|
1255
|
+
shard agents new --app codex --path ./ --prompt-file task.txt --json
|
|
1256
|
+
cat task.txt | shard agents new --app codex --path ./ --prompt-file - --json
|
|
1257
|
+
shard agents new --app claude --path ./ --prompt "What is wrong on this page?" --image page.png --json
|
|
1258
|
+
```
|
|
1259
|
+
|
|
1260
|
+
`--prompt` or `--prompt-file` submits one initial user-authored message to the native agent. Codex Cloud accepts it into the durable conversation inbox with one idempotency key; `initial_prompt.submitted` confirms that acceptance, not a completed provider turn. Text is limited to 256 KiB and may include file, branch and link context. These options apply to fresh conversations and cannot be combined with resume or named/like legacy agent flows. Omit them to start interactively as before. JSON includes `initial_prompt.submitted: true` after native input is accepted. If delivery cannot be confirmed, `initial_prompt_failed` includes `details.workspace_key` and `details.submitted`; open that conversation before creating another.
|
|
1261
|
+
|
|
1262
|
+
`--image <file|->` (0.15.0+, repeatable) attaches up to five PNG, JPEG, GIF or WebP images of up to 10 MiB each to that first message. They are written into the conversation VM under `/home/user/.shardflux/attachments/` and named in the message: Claude Code attaches them as images and Codex opens them with its image viewer. `--image -` reads one image, or PNG images back to back, from standard input; use a file for `--prompt-file` when images come from standard input.
|
|
1263
|
+
|
|
1264
|
+
Use `shard agents open <workspace> --in auto --json` to resolve the remembered app destination. The result includes a `url` or `app_bundle_id` when that app route is available; the caller opens it. Explicit `--in terminal` retains terminal attachment. The menu uses this resolver for new and existing conversations, without provider URLs in Swift.
|
|
1265
|
+
|
|
1266
|
+
## Codex Cloud (0.15.0+, unreleased)
|
|
1267
|
+
|
|
1268
|
+
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.
|
|
1269
|
+
|
|
1270
|
+
`shard setup` uses your existing Codex ChatGPT login to register one executor for the account. Its small connector workspace stays resident and is billed as ordinary workspace memory/CPU usage, including while conversations wait. Use `--no-codex` to skip setup, `shard agents codex status` to inspect it, or `shard agents codex disable` to remove the executor and release its residency. Conversation VMs wake for tools and follow the normal adaptive idle policy afterwards.
|
|
1271
|
+
|
|
1272
|
+
Explicit `agents new --in terminal`, `--in t3`, and `--like` launches keep their existing VM routes. Local Git is prepared and validated before Cloud enrollment. This branch's Cloud feature is awaiting release. Existing local-mode conversations keep their original route. Cloud uses OpenAI's model/configuration capabilities; local app-server plugins and settings are not automatically transferred.
|
package/dist/account-args.js
CHANGED
|
@@ -4,6 +4,13 @@ const ORG = {
|
|
|
4
4
|
const PROJECT = {
|
|
5
5
|
project: { type: 'string', value: '<project>', description: 'Project: id or slug. Default: the saved one (shard projects use), else the organization’s only project.' },
|
|
6
6
|
};
|
|
7
|
+
/** Commands that can create an account (0.15.0+): the agreement to the terms, without a prompt. */
|
|
8
|
+
const ACCEPT_TERMS = {
|
|
9
|
+
'accept-terms': {
|
|
10
|
+
type: 'boolean',
|
|
11
|
+
description: 'Agree to the Terms of Service (https://shardflux.dev/terms) and acknowledge the Privacy Policy (https://shardflux.dev/privacy) without the "Continue? [Y/n]" prompt (0.15.0+; also SHARDFLUX_ACCEPT_TERMS=1). Without a terminal, creating an account needs it: otherwise the command stops before it sends anything (terms_not_accepted, exit 2).',
|
|
12
|
+
},
|
|
13
|
+
};
|
|
7
14
|
const SECOND_FACTOR = {
|
|
8
15
|
code: { type: 'string', value: '<totp>', description: 'Current 6-digit code of your authenticator app (two-factor accounts).' },
|
|
9
16
|
'recovery-code': { type: 'string', value: '<code>', description: 'A one-time recovery code instead of --code.' },
|
|
@@ -52,9 +59,10 @@ export const ACCOUNT_COMMANDS = [
|
|
|
52
59
|
codex: { type: 'boolean', description: 'Use the Codex login on this machine (default; --no-codex signs up with --email).' },
|
|
53
60
|
setup: { type: 'boolean', description: 'Run shard setup afterwards (default; --no-setup skips it).' },
|
|
54
61
|
tool: { type: 'string', multiple: true, value: '<tool>', description: 'Workspace tool the saved API key may use (repeatable; exec, files, pty, process, git, browser). Default: all.' },
|
|
62
|
+
...ACCEPT_TERMS,
|
|
55
63
|
...SECOND_FACTOR,
|
|
56
64
|
},
|
|
57
|
-
examples: ['shard signup', 'shard signup --email you@example.com', 'shard signup --no-codex --email you@example.com --name "Ada Lovelace"'],
|
|
65
|
+
examples: ['shard signup', 'shard signup --accept-terms --json', 'shard signup --email you@example.com', 'shard signup --no-codex --email you@example.com --name "Ada Lovelace"'],
|
|
58
66
|
},
|
|
59
67
|
{
|
|
60
68
|
path: ['verify'],
|
|
@@ -73,6 +81,7 @@ export const ACCOUNT_COMMANDS = [
|
|
|
73
81
|
options: {
|
|
74
82
|
email: { type: 'string', value: '<email>', description: 'Your email address (required).' },
|
|
75
83
|
name: { type: 'string', value: '<display name>', description: 'Your name as others see it (default: none; setup names the organization after it).' },
|
|
84
|
+
...ACCEPT_TERMS,
|
|
76
85
|
},
|
|
77
86
|
examples: [`printf '%s\\n' "$PASSWORD" | shard auth register --email you@example.com --name "Ada Lovelace"`, 'shard auth register --email you@example.com # prompts for the password'],
|
|
78
87
|
},
|
|
@@ -87,15 +96,19 @@ export const ACCOUNT_COMMANDS = [
|
|
|
87
96
|
{ path: ['auth', 'resend-verification'], summary: 'Email a new verification link (signed in, email not verified yet)', positionals: [], options: {} },
|
|
88
97
|
{
|
|
89
98
|
path: ['auth', 'login'],
|
|
90
|
-
summary: 'Sign in: saves a session for this API in the credentials file (the
|
|
91
|
-
description: '
|
|
99
|
+
summary: 'Sign in: saves a session for this API in the credentials file (in the browser, or with a password from standard input or a prompt)',
|
|
100
|
+
description: '--browser (0.15.0+) opens the sign-in page with a code: sign in or create your account there, check that the page shows the same code, and approve; the session is saved here (the page link is printed too, and --no-open only prints it). On a terminal, "shard auth login" without --email or --codex signs in with the browser. --json prints one JSON object per line: {"event":"open",...} with the link and the code, then {"event":"signed_in","email":...} or {"event":"error","code":...,"message":...}. With a password, two-factor accounts pass --code (or --recovery-code), or type the code at the prompt; without either the pending sign-in is saved and "shard auth mfa --code <code>" completes it within 10 minutes (exit 4 until then). Prints who you are, your organizations and the next step. A session lasts 30 days of use (90 at most). SHARDFLUX_SESSION_TOKEN, when set, is used instead of the saved session.',
|
|
92
101
|
positionals: [],
|
|
93
102
|
options: {
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
browser: { type: 'boolean', description: 'Sign in in the browser: open the sign-in page, approve this computer there (0.15.0+). An account created there agrees to the terms on the sign-up page, so --browser needs no --accept-terms.' },
|
|
104
|
+
open: { type: 'boolean', description: 'With --browser: open the page in the default browser (default; --no-open only prints the link).' },
|
|
105
|
+
'client-name': { type: 'string', value: '<name>', description: 'With --browser: the name the sign-in page shows for this computer (default "shard CLI on <hostname>").' },
|
|
106
|
+
email: { type: 'string', value: '<email>', description: 'Your email address, to sign in with a password.' },
|
|
107
|
+
codex: { type: 'boolean', description: 'Sign in with the Codex login on this machine instead of a password (0.9.0+; creates the account when there is none, so it needs --accept-terms like shard signup).' },
|
|
108
|
+
...ACCEPT_TERMS,
|
|
96
109
|
...SECOND_FACTOR,
|
|
97
110
|
},
|
|
98
|
-
examples: [`printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com`, `printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com --code 123456`, 'shard auth login --codex'],
|
|
111
|
+
examples: ['shard auth login --browser', 'shard auth login --browser --no-open --json', `printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com`, `printf '%s\\n' "$PASSWORD" | shard auth login --email you@example.com --code 123456`, 'shard auth login --codex'],
|
|
99
112
|
},
|
|
100
113
|
{
|
|
101
114
|
path: ['auth', 'mfa'],
|
|
@@ -187,13 +200,21 @@ export const ACCOUNT_COMMANDS = [
|
|
|
187
200
|
options: {
|
|
188
201
|
...ORG,
|
|
189
202
|
...PROJECT,
|
|
203
|
+
'prepare-sample': { type: 'boolean', description: 'Copy the Daylight sample app into a new local Git project, "Daylight (sample)", for the first-run demo; prints its path.' },
|
|
204
|
+
activity: { type: 'boolean', description: 'With --inspect: the current commands, listening ports, sudo installs and browser window of --workspace, without waking it.' },
|
|
205
|
+
workspace: { type: 'string', value: '<key|conv_id>', description: 'The workspace or conversation to inspect for live onboarding activity.' },
|
|
206
|
+
inspect: { type: 'boolean', description: 'Report the onboarding steps and verify every connection live (account, Claude, Codex) without changing anything.' },
|
|
207
|
+
apps: { type: 'string', multiple: true, value: '<claude,codex>', description: 'The apps to verify (default: the ones found on this Mac). Repeatable or comma separated.' },
|
|
208
|
+
connect: { type: 'boolean', description: 'With --inspect: also copy this Mac’s Codex login and enable the account’s Codex remote device when Codex is picked.' },
|
|
209
|
+
'retry-codex': { type: 'boolean', description: 'With --inspect --connect: start a Codex remote device that failed or is stuck starting again, once.' },
|
|
190
210
|
'org-name': { type: 'string', value: '<name>', description: 'Name of the organization to create when you have none.' },
|
|
191
211
|
'project-name': { type: 'string', value: '<name>', description: 'Name of the project to create when the organization has none (default Default).' },
|
|
192
212
|
'key-name': { type: 'string', value: '<name>', description: 'Name of the API key (default "shard-cli <hostname>").' },
|
|
193
213
|
tool: { type: 'string', multiple: true, value: '<tool>', description: 'Workspace tool the key may use (repeatable; exec, files, pty, process, git, browser). Default: all.' },
|
|
194
214
|
'new-key': { type: 'boolean', description: 'Create and save a new key even when the saved one works (the old key stays valid until revoked).' },
|
|
215
|
+
codex: { type: 'boolean', description: 'Set up the account Codex Cloud executor with this machine’s Codex login (default when signed in); --no-codex skips it.' },
|
|
195
216
|
},
|
|
196
|
-
examples: ['shard setup', 'shard setup --org acme --project web --tool exec --tool files'],
|
|
217
|
+
examples: ['shard setup', 'shard setup --json --inspect --apps claude,codex', 'shard setup --org acme --project web --tool exec --tool files'],
|
|
197
218
|
},
|
|
198
219
|
{
|
|
199
220
|
path: ['env'],
|
package/dist/account-commands.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { prepareOnboardingSample } from "./onboarding-sample.js";
|
|
2
|
+
import { onboardingReport, onboardingText } from "./onboarding.js";
|
|
1
3
|
/**
|
|
2
4
|
* The account plane (0.5.0; docs/progress/account-cli.md §5): everything a person does in the web app, from a
|
|
3
5
|
* terminal. Sign-in and the account calls go through the SDK's ShardfluxAccount with the user session (ctx.account());
|
|
@@ -13,11 +15,14 @@
|
|
|
13
15
|
* setup), else the only one there is; with several the usage error lists them.
|
|
14
16
|
*/
|
|
15
17
|
import { chmod, writeFile } from 'node:fs/promises';
|
|
18
|
+
import { readAgentCredentials, syncAgentCredentials } from "./agent-credentials.js";
|
|
16
19
|
import { API_KEY_TOOL_PERMISSIONS, CheckoutTimeoutError, Shardflux, ShardfluxAccount, ShardfluxApiError, codexIdentityProof, fetchBillingCatalog, parseEmailToken } from '@shardflux/sdk';
|
|
17
20
|
import { CLI_VERSION, UUID, parseDuration, parsePositiveInt } from "./args.js";
|
|
18
21
|
import { openUrl } from "./browser.js";
|
|
22
|
+
import { defaultClientName, startDeviceAuthorization, waitForDeviceSession } from "./browser-login.js";
|
|
23
|
+
import { legalFields, requireTermsAcceptance, showLegalNotice } from "./legal.js";
|
|
19
24
|
import { fileExists, oneOf, out, parseSlugVersion, principal, readInput, resolveWorkspace, str, strs, waitTimeout } from "./commands.js";
|
|
20
|
-
import { AuthConfigError, CheckoutEndedError, InterruptedError, MfaPendingError, NotFoundError, UsageError } from "./errors.js";
|
|
25
|
+
import { AuthConfigError, CheckoutEndedError, InterruptedError, MfaPendingError, NotFoundError, UsageError, describeError } from "./errors.js";
|
|
21
26
|
import { apiKeyTable, attachmentTable, auditTable, authSessionTable, billingStatusText, dataExportText, egressVersionTable, invitationTable, invoiceTable, isGrantPlan, kv, memberTable, money, orgTable, overagePolicyText, planTable, projectEgressText, projectTable, table, time, toolsText, volumeDetail, volumeTable, workspaceEgressText, WORKSPACE_HEADERS, workspaceRow, } from "./format.js";
|
|
22
27
|
export const DEFAULT_API_URL = 'https://api.shardflux.dev';
|
|
23
28
|
const TOTP = /^\d{6}$/;
|
|
@@ -283,7 +288,7 @@ async function signedInReport(ctx, account, user, session, extra = {}) {
|
|
|
283
288
|
['organizations', memberships === null ? '-' : memberships.length === 0 ? 'none yet' : memberships.map((m) => `${m.organization_name} (${m.organization_slug}, ${m.role})`).join(', ')],
|
|
284
289
|
]) +
|
|
285
290
|
`next: ${next}\n`;
|
|
286
|
-
out(ctx, human, { status: 'authenticated', user, session: { expires_at: session.expires_at, credentials_file: ctx.store.file }, organizations: memberships, next, ...extra });
|
|
291
|
+
out(ctx, human, { status: 'authenticated', user, session: { expires_at: session.expires_at, credentials_file: ctx.store.file }, organizations: memberships, next, ...legalFields(), ...extra });
|
|
287
292
|
return 0;
|
|
288
293
|
}
|
|
289
294
|
/** Best effort: ends a replaced session server side so it does not linger for 30 days. */
|
|
@@ -644,6 +649,14 @@ async function runSetup(ctx, values, account) {
|
|
|
644
649
|
permissions = k.tool_permissions;
|
|
645
650
|
}
|
|
646
651
|
const tag = (made) => (made ? 'created' : 'existing');
|
|
652
|
+
let codex;
|
|
653
|
+
const credentialHome = ctx.io.env.HOME ?? ctx.io.env.USERPROFILE;
|
|
654
|
+
if (values.codex !== false && permissions.includes('exec') && credentialHome && (await readAgentCredentials({ home: credentialHome, redact: value => ctx.addSecret(value) })).some(v => v.app === 'codex')) {
|
|
655
|
+
const cloud = new Shardflux({ apiKey: key.token, baseUrl: ctx.apiUrl });
|
|
656
|
+
await syncAgentCredentials(cloud, { home: credentialHome, redact: value => ctx.addSecret(value) });
|
|
657
|
+
await cloud.codex.enable({ signal: ctx.signal });
|
|
658
|
+
codex = { ...await cloud.codex.waitConnector({ timeoutMs: ctx.wakeTimeoutMs, signal: ctx.signal }) };
|
|
659
|
+
}
|
|
647
660
|
const envNote = ctx.io.env.SHARDFLUX_API_KEY?.trim() ? 'note: SHARDFLUX_API_KEY is set in this environment and wins over the saved key; unset it to use the saved key.\n' : '';
|
|
648
661
|
return {
|
|
649
662
|
text: (keep ? 'Already set up; the saved API key works.\n' : 'Set up.\n') +
|
|
@@ -653,28 +666,112 @@ async function runSetup(ctx, values, account) {
|
|
|
653
666
|
['api key', `"${key.name}" ${key.key_id} (tools: ${toolsText(permissions)}) ${keep ? 'saved earlier' : 'created'}, saved in ${ctx.store.file}`],
|
|
654
667
|
]) +
|
|
655
668
|
envNote +
|
|
669
|
+
(codex ? 'Codex Cloud is ready. One account executor; no pairing. Its residency is billed as workspace usage.\n' : '') +
|
|
656
670
|
`next: ${NEXT_WORKSPACE}\n`,
|
|
657
671
|
data: {
|
|
658
672
|
organization: org,
|
|
659
673
|
project,
|
|
660
674
|
api_key: { id: key.id, key_id: key.key_id, name: key.name, project_id: key.project_id, tool_permissions: permissions },
|
|
661
675
|
created,
|
|
676
|
+
...(codex ? { codex } : {}),
|
|
662
677
|
credentials_file: ctx.store.file,
|
|
663
678
|
next: NEXT_WORKSPACE,
|
|
664
679
|
},
|
|
665
680
|
};
|
|
666
681
|
}
|
|
682
|
+
// ---- browser sign-in (0.15.0+) -----------------------------------------------------------------------------------
|
|
683
|
+
/** One NDJSON line of `auth login --browser --json` (the Mac app reads them). */
|
|
684
|
+
function event(ctx, value) {
|
|
685
|
+
ctx.io.stdout.write(`${JSON.stringify(value)}\n`);
|
|
686
|
+
}
|
|
687
|
+
/**
|
|
688
|
+
* The browser sign-in (browser-login.ts): starts the request, opens or prints the page and the code, waits for the
|
|
689
|
+
* approval and saves the session exactly like `auth login` does. With --json the start is the `open` event; a failure
|
|
690
|
+
* becomes the `error` event (and is thrown on, for the exit code and stderr).
|
|
691
|
+
*/
|
|
692
|
+
async function browserSignIn(ctx, values, noticeShown = false) {
|
|
693
|
+
try {
|
|
694
|
+
const opts = ctx.accountOptions();
|
|
695
|
+
const clientName = str(values['client-name'])?.trim() || defaultClientName(ctx.hostname());
|
|
696
|
+
const d = await startDeviceAuthorization(opts, clientName, ctx.signal);
|
|
697
|
+
// The terms notice comes before the browser opens: a line here, the URLs in the event for an app to show. No
|
|
698
|
+
// --accept-terms: an account created in the browser agrees to the terms on the sign-up page, which records it.
|
|
699
|
+
if (ctx.json) {
|
|
700
|
+
event(ctx, { event: 'open', verification_uri_complete: d.verification_uri_complete, user_code: d.user_code, expires_in: d.expires_in, ...legalFields() });
|
|
701
|
+
}
|
|
702
|
+
else if (!noticeShown)
|
|
703
|
+
showLegalNotice(ctx);
|
|
704
|
+
const opened = values.open !== false && openUrl(d.verification_uri_complete, ctx.io.env);
|
|
705
|
+
const minutes = Math.max(1, Math.round(d.expires_in / 60));
|
|
706
|
+
if (!ctx.json) {
|
|
707
|
+
ctx.io.stderr.write(`${opened ? 'Opening your browser to sign in' : 'Open this page to sign in'}: ${d.verification_uri_complete}\n` +
|
|
708
|
+
`Your code: ${d.user_code} (approve only if the page shows the same code)\n` +
|
|
709
|
+
`Waiting for the approval in the browser (the code expires in ${minutes} minutes; Ctrl-C stops)...\n`);
|
|
710
|
+
}
|
|
711
|
+
const sleep = opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
|
|
712
|
+
const s = await waitForDeviceSession(opts, d, { signal: ctx.signal, sleep });
|
|
713
|
+
ctx.addSecret(s.session_token);
|
|
714
|
+
const replaced = ctx.store.profile(ctx.apiUrl).session?.token ?? null;
|
|
715
|
+
const saved = { token: s.session_token, expires_at: s.session_expires_at || null, user: { id: s.user.id, email: s.user.email }, state: 'active' };
|
|
716
|
+
const { switched } = ctx.saveSession(saved, s.user.email);
|
|
717
|
+
if (replaced && replaced !== saved.token)
|
|
718
|
+
await endQuietly(ctx, replaced);
|
|
719
|
+
if (switched && !ctx.json)
|
|
720
|
+
ctx.io.stderr.write(SWITCHED_NOTE);
|
|
721
|
+
const account = new ShardfluxAccount({ ...ctx.accountOptions(), sessionToken: s.session_token });
|
|
722
|
+
return { account, user: s.user, saved };
|
|
723
|
+
}
|
|
724
|
+
catch (err) {
|
|
725
|
+
if (ctx.json) {
|
|
726
|
+
const info = describeError(err);
|
|
727
|
+
const interrupted = ctx.signal.aborted;
|
|
728
|
+
event(ctx, { event: 'error', code: interrupted ? 'interrupted' : info.code, message: interrupted ? 'Interrupted.' : info.message });
|
|
729
|
+
}
|
|
730
|
+
throw err;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
/** `shard signup` on a terminal without a Codex login or --email (0.15.0+): the browser sign-in, then setup like signup. */
|
|
734
|
+
async function browserSignup(ctx, values) {
|
|
735
|
+
// signup has shown the notice (and asked) already.
|
|
736
|
+
const { account, user } = await browserSignIn(ctx, values, true);
|
|
737
|
+
let text = `Signed in as ${userLine(user)} in the browser.\nsession: saved in ${ctx.store.file}\n`;
|
|
738
|
+
const data = { status: 'authenticated', user, method: 'browser', credentials_file: ctx.store.file };
|
|
739
|
+
if (values.setup !== false) {
|
|
740
|
+
const setup = await runSetup(ctx, values, account);
|
|
741
|
+
text += setup.text;
|
|
742
|
+
data.setup = setup.data;
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
text += 'next: shard setup\n';
|
|
746
|
+
data.next = 'shard setup';
|
|
747
|
+
}
|
|
748
|
+
out(ctx, text, data);
|
|
749
|
+
return 0;
|
|
750
|
+
}
|
|
751
|
+
/** auth login: --browser, or on a terminal without --email/--codex. */
|
|
752
|
+
function wantsBrowser(ctx, values) {
|
|
753
|
+
const email = str(values.email) !== undefined;
|
|
754
|
+
if (values.browser === true) {
|
|
755
|
+
if (email || values.codex === true)
|
|
756
|
+
throw new UsageError('use one of --browser, --email or --codex');
|
|
757
|
+
return true;
|
|
758
|
+
}
|
|
759
|
+
if (values.browser === false || email || values.codex === true)
|
|
760
|
+
return false;
|
|
761
|
+
return ctx.input.interactive && !ctx.json;
|
|
762
|
+
}
|
|
667
763
|
// ---- handlers --------------------------------------------------------------------------------------------------
|
|
668
764
|
export const ACCOUNT_HANDLERS = {
|
|
669
765
|
async 'auth register'(ctx, values) {
|
|
670
766
|
const email = emailArg(need(values, 'email', '--email you@example.com'), '--email');
|
|
671
767
|
const name = str(values.name)?.trim();
|
|
768
|
+
const terms = await requireTermsAcceptance(ctx, values);
|
|
672
769
|
const password = await ctx.input.newPassword(`Choose a password for ${email}: `, 'The password', pipe(`shard auth register --email ${email}`));
|
|
673
|
-
const r = await ShardfluxAccount.register({ ...ctx.accountOptions(), email, password, ...(name ? { displayName: name } : {}) });
|
|
770
|
+
const r = await ShardfluxAccount.register({ ...ctx.accountOptions(), email, password, ...(name ? { displayName: name } : {}), ...terms });
|
|
674
771
|
const next = [`shard auth verify-email '<link from the email>'`, `${pipe(`shard auth login --email ${email}`)}`];
|
|
675
772
|
out(ctx, `Registration received for ${email}.\n` +
|
|
676
773
|
`Check that inbox for a verification link (valid 24 hours). If the address already has an account, no new one is created: sign in instead.\n` +
|
|
677
|
-
`next:\n 1. open the link, or: ${next[0]}\n 2. ${next[1]}\n`, { status: r.status, email, next_steps: next });
|
|
774
|
+
`next:\n 1. open the link, or: ${next[0]}\n 2. ${next[1]}\n`, { status: r.status, email, next_steps: next, ...legalFields() });
|
|
678
775
|
return 0;
|
|
679
776
|
},
|
|
680
777
|
async 'auth verify-email'(ctx, _values, [link]) {
|
|
@@ -711,10 +808,26 @@ export const ACCOUNT_HANDLERS = {
|
|
|
711
808
|
return 0;
|
|
712
809
|
},
|
|
713
810
|
async 'auth login'(ctx, values) {
|
|
811
|
+
if (wantsBrowser(ctx, values)) {
|
|
812
|
+
if (factorFlags(values) !== null)
|
|
813
|
+
throw new UsageError('--code and --recovery-code go with --email; in the browser the sign-in page asks for your second factor');
|
|
814
|
+
const { account, user, saved } = await browserSignIn(ctx, values);
|
|
815
|
+
if (ctx.json) {
|
|
816
|
+
event(ctx, { event: 'signed_in', email: user.email });
|
|
817
|
+
return 0;
|
|
818
|
+
}
|
|
819
|
+
return signedInReport(ctx, account, user, saved);
|
|
820
|
+
}
|
|
821
|
+
if (values.open !== undefined || str(values['client-name']) !== undefined)
|
|
822
|
+
throw new UsageError('--no-open and --client-name go with --browser', 'shard auth login --browser --no-open');
|
|
714
823
|
if (values.codex === true)
|
|
715
824
|
return ACCOUNT_HANDLERS.signup(ctx, { ...values, setup: false, codex: true, email: undefined }, []);
|
|
716
|
-
const
|
|
825
|
+
const given = str(values.email)?.trim();
|
|
826
|
+
if (!given)
|
|
827
|
+
throw new UsageError('--email is required to sign in with a password, e.g. --email you@example.com', 'or sign in with your browser: shard auth login --browser (or with your Codex login: shard auth login --codex)');
|
|
828
|
+
const email = emailArg(given, '--email');
|
|
717
829
|
let factor = factorFlags(values);
|
|
830
|
+
showLegalNotice(ctx);
|
|
718
831
|
const password = await ctx.input.secret(`Password for ${email}: `, 'The password', pipe(`shard auth login --email ${email}`));
|
|
719
832
|
let latest = null;
|
|
720
833
|
const { account, result } = await ShardfluxAccount.login({
|
|
@@ -971,8 +1084,18 @@ export const ACCOUNT_HANDLERS = {
|
|
|
971
1084
|
return 0;
|
|
972
1085
|
},
|
|
973
1086
|
async setup(ctx, values) {
|
|
1087
|
+
if (values['prepare-sample'] === true) {
|
|
1088
|
+
const path = await prepareOnboardingSample(ctx.store.dir);
|
|
1089
|
+
out(ctx, `Daylight is ready at ${path}\n`, { sample_path: path });
|
|
1090
|
+
return 0;
|
|
1091
|
+
}
|
|
1092
|
+
if (values.inspect === true || (ctx.json && ctx.sessionSource() === null)) {
|
|
1093
|
+
const onboarding = await onboardingReport(ctx, values);
|
|
1094
|
+
out(ctx, onboardingText(onboarding), { onboarding });
|
|
1095
|
+
return 0;
|
|
1096
|
+
}
|
|
974
1097
|
const r = await runSetup(ctx, values, ctx.account());
|
|
975
|
-
out(ctx, r.text, r.data);
|
|
1098
|
+
out(ctx, r.text, ctx.json ? { ...r.data, onboarding: await onboardingReport(ctx, values) } : r.data);
|
|
976
1099
|
return 0;
|
|
977
1100
|
},
|
|
978
1101
|
async signup(ctx, values) {
|
|
@@ -980,6 +1103,10 @@ export const ACCOUNT_HANDLERS = {
|
|
|
980
1103
|
const name = str(values.name)?.trim();
|
|
981
1104
|
let codexNote = null;
|
|
982
1105
|
let idToken = null;
|
|
1106
|
+
if (values.codex === false && email === undefined)
|
|
1107
|
+
throw new UsageError('--no-codex needs an email address.', 'shard signup --no-codex --email you@example.com');
|
|
1108
|
+
// Before the Codex login is read (its refresh is a network call) and before the API: the agreement to the terms.
|
|
1109
|
+
const terms = await requireTermsAcceptance(ctx, values);
|
|
983
1110
|
if (values.codex !== false) {
|
|
984
1111
|
const proof = await codexIdentityProof({ env: ctx.io.env, clientName: 'shard', clientVersion: CLI_VERSION });
|
|
985
1112
|
if (proof.ok) {
|
|
@@ -990,17 +1117,19 @@ export const ACCOUNT_HANDLERS = {
|
|
|
990
1117
|
}
|
|
991
1118
|
}
|
|
992
1119
|
else if (email === undefined) {
|
|
993
|
-
|
|
1120
|
+
if (!ctx.input.interactive || ctx.json)
|
|
1121
|
+
throw new UsageError(`No Codex login to sign up with: ${proof.message}`, 'shard signup --email you@example.com, or in the browser: shard auth login --browser');
|
|
1122
|
+
// On a terminal (0.15.0+): sign up or sign in on the web page instead, then set up as below.
|
|
1123
|
+
ctx.io.stderr.write(`No Codex login on this computer (${proof.message}); continuing in the browser.\n`);
|
|
1124
|
+
return browserSignup(ctx, values);
|
|
994
1125
|
}
|
|
995
1126
|
}
|
|
996
|
-
else if (email === undefined) {
|
|
997
|
-
throw new UsageError('--no-codex needs an email address.', 'shard signup --no-codex --email you@example.com');
|
|
998
|
-
}
|
|
999
1127
|
let latest = null;
|
|
1000
1128
|
const { account, result } = await ShardfluxAccount.signup({
|
|
1001
1129
|
...ctx.accountOptions(),
|
|
1002
1130
|
...(idToken !== null ? { codexIdToken: idToken } : { email: email }),
|
|
1003
1131
|
...(name ? { displayName: name } : {}),
|
|
1132
|
+
...terms,
|
|
1004
1133
|
onSessionToken: (s) => {
|
|
1005
1134
|
ctx.addSecret(s.token);
|
|
1006
1135
|
latest = s;
|
|
@@ -1040,7 +1169,7 @@ export const ACCOUNT_HANDLERS = {
|
|
|
1040
1169
|
? 'email not verified yet: workspaces run on the Free plan now; verify to start the trial: shard verify'
|
|
1041
1170
|
: 'email not verified yet: verify it to run workspaces: shard verify';
|
|
1042
1171
|
let text = `${result.created ? 'Signed up' : 'Signed in'} as ${userLine(user)} (${how}).\nsession: saved in ${ctx.store.file}\n`;
|
|
1043
|
-
const data = { status: 'authenticated', created: result.created, access, user, method: idToken !== null ? 'codex' : 'email', credentials_file: ctx.store.file };
|
|
1172
|
+
const data = { status: 'authenticated', created: result.created, access, user, method: idToken !== null ? 'codex' : 'email', credentials_file: ctx.store.file, ...legalFields() };
|
|
1044
1173
|
if (access !== 'verification_required' && values.setup !== false) {
|
|
1045
1174
|
const setup = await runSetup(ctx, values, account);
|
|
1046
1175
|
text += setup.text;
|
package/dist/acp-catalog.js
CHANGED
|
@@ -2,8 +2,8 @@ import { objectValue } from "./acp-rpc.js";
|
|
|
2
2
|
// T3's installation probe works offline, without a VM. These are harness
|
|
3
3
|
// labels only. Real sessions always replace them with guest metadata.
|
|
4
4
|
export const ACP_PROBE_CATALOG = {
|
|
5
|
-
claude: { models: { currentModelId: 'default', availableModels: [{ modelId: 'default', name: '
|
|
6
|
-
codex: { models: { currentModelId: 'default', availableModels: [{ modelId: 'default', name: '
|
|
5
|
+
claude: { models: { currentModelId: 'default', availableModels: [{ modelId: 'default', name: 'Default' }] } },
|
|
6
|
+
codex: { models: { currentModelId: 'default', availableModels: [{ modelId: 'default', name: 'Default' }] } },
|
|
7
7
|
};
|
|
8
8
|
export function nativeModelOptions(session) {
|
|
9
9
|
const configs = Array.isArray(session.configOptions) ? session.configOptions.map(objectValue) : [];
|