Repository navigation
chore: promote cli 5.11.0 - #280
Conversation
Bump the transitive ip-address from 10.7.3 to 10.7.4 for GHSA-rxp7-6hxx-x8q5. express-rate-limit's ^10.2.0 range already allows it, so only the lockfile changes. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* ReleaseAs: 5.8.0 * Release-As: 5.8.0 * Release-As: 5.8.0 * chore(production): release 5.8.0 * chore: pin the 5.9.0 promotion Release-As: 5.9.0 * chore(production): release 5.9.0 * chore: pin the 5.10.0 promotion Release-As: 5.10.0 * chore(production): release 5.10.0 --------- Co-authored-by: finalerock44 <ethan@devicecloud.dev> Co-authored-by: finalerock44 <77282157+finalerock44@users.noreply.github.com> Co-authored-by: dcd-release-please[bot] <296541543+dcd-release-please[bot]@users.noreply.github.com> Co-authored-by: Tom Riglar <tom.riglar@moropo.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…EN (#257) Back from workload identity federation to the OAuth token, now an org-level secret. Federation bills the Anthropic API org's purchased credit, which promotional credit doesn't cover for Claude Code, so reviews were failing with "Credit balance is too low". Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
body-parser 2.3.0 -> 2.4.0 (GHSA-27r3-jxmh-p828, high) and fast-uri 3.1.8 -> 3.1.9 (GHSA-w74x-v6f9-j3xg, GHSA-wjr9-j795-jxcr, moderate), both under @modelcontextprotocol/sdk. fast-uri is pinned by an override, so the override moves to 3.1.9. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
install.sh now prints a short banner and groups its output into stages (resolve, download, verify, install) in the same ⏺/⎿ tree the CLI uses, with a separate "next steps" block at the end. Colours are only used on a real terminal, and are off with NO_COLOR or TERM=dumb. It stays POSIX sh and wrapped in main(). The spinner now uses cyan braille frames instead of clack's magenta ones, drops the │ guide bar, and ends with the CLI's own ✓ / ✗ symbols. A new ux.action.fail() marks failures. The CI fallback is unchanged apart from those symbols. Closes #90 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* feat(device): add dcd device start, list, status, stop, install, launch and models (#231) The first stages of the Agent Devices CLI (dcd plan 3.1 and 3.2). - --json prints one envelope, {ok, session, data, error}, on success and on failure. - Exit codes: 0 ok, 1 usage, 2 failed, 3 limit, 4 session ended or not found. - The session id may be left out when exactly one session is open. - start --app uploads a local build first; an https URL goes to the API's install-from-URL. - Progress goes to stderr, with no spinner unless at a terminal. Hidden from dcd --help until the API reaches production. Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(device): add dcd device inspect, screenshot, run, logs and open (#232) * feat(device): add dcd device start, list, status, stop, install, launch and models The first stages of the Agent Devices CLI (dcd plan 3.1 and 3.2). - --json prints one envelope, {ok, session, data, error}, on success and on failure. - Exit codes: 0 ok, 1 usage, 2 failed, 3 limit, 4 session ended or not found. - The session id may be left out when exactly one session is open. - start --app uploads a local build first; an https URL goes to the API's install-from-URL. - Progress goes to stderr, with no spinner unless at a terminal. Hidden from dcd --help until the API reaches production. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device inspect, screenshot, run, logs and open Stage 3.3 of the Agent Devices CLI. - inspect prints the compact screen; screenshot saves the frame to a file and reports its path. - run sends one flow (a file or --yaml) with optional --env KEY=VALUE, and polls a flow that outlasts the API's wait. A failed flow exits 2. - logs prints the app's recent log lines; --follow keeps printing new ones until the session ends. - open opens the monitor at a terminal, and prints its URL otherwise. Running a directory of flows needs an API change first and is refused for now. openBrowser moves out of login.ts so device can share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * fix(device): renew a dcd login token while polling logs --follow, a long run and start --wait reused the token resolved at the start. A dcd login access token lasts about an hour, so a poll past that failed with an auth error. Each poll now calls refreshAuth first; an API key is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(device): add dcd device artifacts (#234) * feat(device): add dcd device start, list, status, stop, install, launch and models The first stages of the Agent Devices CLI (dcd plan 3.1 and 3.2). - --json prints one envelope, {ok, session, data, error}, on success and on failure. - Exit codes: 0 ok, 1 usage, 2 failed, 3 limit, 4 session ended or not found. - The session id may be left out when exactly one session is open. - start --app uploads a local build first; an https URL goes to the API's install-from-URL. - Progress goes to stderr, with no spinner unless at a terminal. Hidden from dcd --help until the API reaches production. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device inspect, screenshot, run, logs and open Stage 3.3 of the Agent Devices CLI. - inspect prints the compact screen; screenshot saves the frame to a file and reports its path. - run sends one flow (a file or --yaml) with optional --env KEY=VALUE, and polls a flow that outlasts the API's wait. A failed flow exits 2. - logs prints the app's recent log lines; --follow keeps printing new ones until the session ends. - open opens the monitor at a terminal, and prints its URL otherwise. Running a directory of flows needs an API change first and is refused for now. openBrowser moves out of login.ts so device can share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device artifacts Stage 5.3 of Agent Devices. Downloads what a session left behind into ./<session>-artifacts (or -o dir): recording.mp4, device.log, and per action actions/NNN-screenshot.jpg and NNN-screen.json, numbered oldest first. Follows every page. --links lists the signed links instead. Files that cannot be fetched are reported, and the command exits 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * fix(device): renew a dcd login token while polling logs --follow, a long run and start --wait reused the token resolved at the start. A dcd login access token lasts about an hour, so a poll past that failed with an auth error. Each poll now calls refreshAuth first; an API key is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(device): run a directory of flows as a bundle (#235) * feat(device): add dcd device start, list, status, stop, install, launch and models The first stages of the Agent Devices CLI (dcd plan 3.1 and 3.2). - --json prints one envelope, {ok, session, data, error}, on success and on failure. - Exit codes: 0 ok, 1 usage, 2 failed, 3 limit, 4 session ended or not found. - The session id may be left out when exactly one session is open. - start --app uploads a local build first; an https URL goes to the API's install-from-URL. - Progress goes to stderr, with no spinner unless at a terminal. Hidden from dcd --help until the API reaches production. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device inspect, screenshot, run, logs and open Stage 3.3 of the Agent Devices CLI. - inspect prints the compact screen; screenshot saves the frame to a file and reports its path. - run sends one flow (a file or --yaml) with optional --env KEY=VALUE, and polls a flow that outlasts the API's wait. A failed flow exits 2. - logs prints the app's recent log lines; --follow keeps printing new ones until the session ends. - open opens the monitor at a terminal, and prints its URL otherwise. Running a directory of flows needs an API change first and is refused for now. openBrowser moves out of login.ts so device can share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device artifacts Stage 5.3 of Agent Devices. Downloads what a session left behind into ./<session>-artifacts (or -o dir): recording.mp4, device.log, and per action actions/NNN-screenshot.jpg and NNN-screen.json, numbered oldest first. Follows every page. --links lists the signed links instead. Files that cannot be fetched are reported, and the command exits 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * fix(device): renew a dcd login token while polling logs --follow, a long run and start --wait reused the token resolved at the start. A dcd login access token lasts about an hour, so a poll past that failed with an auth error. Each poll now calls refreshAuth first; an API key is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): run a directory of flows as a bundle dcd device run <dir> zips the directory (without dot-folders and node_modules), uploads it to both copies through POST /device/bundles/upload-url, registers it with its SHA-256, and runs it by id. --entry picks one flow inside it; without it every flow runs. The zip is encrypted when the org's default or DCD_ENCRYPT says so, as dcd cloud's flow zips are. Needs dcd#1718. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(mcp): add dcd mcp, one stdio server for cloud tests and cloud devices (#237) * feat(device): add dcd device start, list, status, stop, install, launch and models The first stages of the Agent Devices CLI (dcd plan 3.1 and 3.2). - --json prints one envelope, {ok, session, data, error}, on success and on failure. - Exit codes: 0 ok, 1 usage, 2 failed, 3 limit, 4 session ended or not found. - The session id may be left out when exactly one session is open. - start --app uploads a local build first; an https URL goes to the API's install-from-URL. - Progress goes to stderr, with no spinner unless at a terminal. Hidden from dcd --help until the API reaches production. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device inspect, screenshot, run, logs and open Stage 3.3 of the Agent Devices CLI. - inspect prints the compact screen; screenshot saves the frame to a file and reports its path. - run sends one flow (a file or --yaml) with optional --env KEY=VALUE, and polls a flow that outlasts the API's wait. A failed flow exits 2. - logs prints the app's recent log lines; --follow keeps printing new ones until the session ends. - open opens the monitor at a terminal, and prints its URL otherwise. Running a directory of flows needs an API change first and is refused for now. openBrowser moves out of login.ts so device can share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device artifacts Stage 5.3 of Agent Devices. Downloads what a session left behind into ./<session>-artifacts (or -o dir): recording.mp4, device.log, and per action actions/NNN-screenshot.jpg and NNN-screen.json, numbered oldest first. Follows every page. --links lists the signed links instead. Files that cannot be fetched are reported, and the command exits 2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * fix(device): renew a dcd login token while polling logs --follow, a long run and start --wait reused the token resolved at the start. A dcd login access token lasts about an hour, so a poll past that failed with an auth error. Each poll now calls refreshAuth first; an API key is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): run a directory of flows as a bundle dcd device run <dir> zips the directory (without dot-folders and node_modules), uploads it to both copies through POST /device/bundles/upload-url, registers it with its SHA-256, and runs it by id. --entry picks one flow inside it; without it every flow runs. The zip is encrypted when the org's default or DCD_ENCRYPT says so, as dcd cloud's flow zips are. Needs dcd#1718. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(mcp): add dcd mcp, one stdio server for cloud tests and cloud devices Agent Devices 4.4. dcd mcp serves the dcd_* tools (the same in-process server dcd-mcp runs) and the device tools, proxied from the hosted <api>/mcp so they are defined once. It adds what only a local process can do: install_app takes app_path (uploaded first) and run takes path (a flow file sent as yaml, or a directory uploaded as a bundle). Uses the CLI's credentials; the hosted /mcp accepts a dcd login session with dcd#1719. Without the hosted endpoint it serves the dcd_* tools only. Read-only mode keeps only read-only device tools. Hidden until the device API is live; dcd-mcp is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * fix(mcp): refuse state-changing device tools in read-only mode Read-only mode hid them from the tool list, but a call made anyway was still forwarded to the hosted /mcp, which knows nothing of the mode. The bridge now refuses any device tool not annotated readOnlyHint, and the server instructions in read-only mode name only the reading tools. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * fix(mcp): don't cut forwarded calls off at the SDK's 60s default dcd mcp forwarded every call through an MCP Client with no request options, so the SDK's 60s default applied: dcd_run_cloud_test with wait (up to an hour), a large upload, or a long device run failed with 'Request timed out' while the work carried on. Both forwarded calls now pass the caller's cancel signal and a 2h timeout that resets on progress, so the calling client's own timeout is what limits them. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * test(mcp): check the bridge's call timeout is the one applied createBridgeServer takes callTimeoutMs (default 2h), so a test can show a forwarded call outlasting it times out and a faster one doesn't. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(device): time out stalled artifact downloads (#240) * fix(device): install --wait waits for the install itself (#242) install --wait returned as soon as the session was usable, but a session stays ready while a new build installs, so it came back before the app was there. It now also polls the session block's install.status (dcd #1748) until the build is installed or failed. A failed install exits 2 with the reason and says the session is still open; start --app --wait does the same. An API without install status returns as before. Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(device): QA fixes, dev sync, and request-human/human commands (#258) * chore: merge production into dev after 5.8.0 (#236) * ReleaseAs: 5.8.0 * Release-As: 5.8.0 * Release-As: 5.8.0 * chore(production): release 5.8.0 --------- Co-authored-by: finalerock44 <ethan@devicecloud.dev> Co-authored-by: finalerock44 <77282157+finalerock44@users.noreply.github.com> Co-authored-by: dcd-release-please[bot] <296541543+dcd-release-please[bot]@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * chore: bump eslint-plugin-unicorn from 77.0.0 to 78.0.0 (#239) Bumps [eslint-plugin-unicorn](https://github.com/sindresorhus/eslint-plugin-unicorn) from 77.0.0 to 78.0.0. - [Release notes](https://github.com/sindresorhus/eslint-plugin-unicorn/releases) - [Commits](sindresorhus/eslint-plugin-unicorn@v77.0.0...v78.0.0) --- updated-dependencies: - dependency-name: eslint-plugin-unicorn dependency-version: 78.0.0 dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * deps: bump the minor-and-patch group across 1 directory with 5 updates (#238) Bumps the minor-and-patch group with 5 updates in the / directory: | Package | From | To | | --- | --- | --- | | [@supabase/supabase-js](https://github.com/supabase/supabase-js/tree/HEAD/packages/core/supabase-js) | `2.117.3` | `2.118.0` | | [js-yaml](https://github.com/nodeca/js-yaml) | `5.4.3` | `5.5.0` | | [zod](https://github.com/colinhacks/zod) | `4.6.5` | `4.6.7` | | [eslint](https://github.com/eslint/eslint) | `10.12.0` | `10.13.0` | | [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint) | `8.71.1` | `8.71.2` | Updates `@supabase/supabase-js` from 2.117.3 to 2.118.0 - [Release notes](https://github.com/supabase/supabase-js/releases) - [Changelog](https://github.com/supabase/supabase-js/blob/master/packages/core/supabase-js/CHANGELOG.md) - [Commits](https://github.com/supabase/supabase-js/commits/v2.118.0/packages/core/supabase-js) Updates `js-yaml` from 5.4.3 to 5.5.0 - [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md) - [Commits](nodeca/js-yaml@5.4.3...5.5.0) Updates `zod` from 4.6.5 to 4.6.7 - [Release notes](https://github.com/colinhacks/zod/releases) - [Commits](colinhacks/zod@v4.6.5...v4.6.7) Updates `eslint` from 10.12.0 to 10.13.0 - [Release notes](https://github.com/eslint/eslint/releases) - [Commits](eslint/eslint@v10.12.0...v10.13.0) Updates `typescript-eslint` from 8.71.1 to 8.71.2 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.71.2/packages/typescript-eslint) --- updated-dependencies: - dependency-name: "@supabase/supabase-js" dependency-version: 2.118.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: eslint dependency-version: 10.13.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: js-yaml dependency-version: 5.5.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: minor-and-patch - dependency-name: typescript-eslint dependency-version: 8.71.2 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: minor-and-patch - dependency-name: zod dependency-version: 4.6.7 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * feat(login): reuse the last org and add --org (#243) * feat(login): reuse the last org and add --org dcd login no longer hangs where nobody can answer a prompt, such as a script, CI, or a shell an agent runs. - The org picker starts on the org from the last login, so Enter keeps it. - Without a TTY, the last login's org is used and one line says so. With no last org, or one that is gone, login fails with an error that names --org and lists the available orgs. - --org <name|id> picks the org without the picker, and fails if the user isn't a member. - Without a TTY, the "already logged in" confirm takes its default and signs in again. The old session stays until the new login succeeds. - The last org only counts when the last login was to the same API, because org ids differ between environments. - A bare dcd switch-org without a TTY now fails with the org names instead of hanging the same way. Closes #241 * chore(deps): bump hono override to 4.13.11 to clear audit pnpm audit flagged GHSA-5r4p-p66f-jhc7 (moderate) on hono >=4.12.4 <4.13.11, which comes in through @modelcontextprotocol/sdk. In those versions serveStatic decodes the request path twice. The advisory was published on 2026-10-20, after dev's last green run, so the lint-and-test audit step now fails on every branch. Dependabot opened an alert but no fix PR, because the exact-pin override holds hono at 4.13.7. 4.13.11 is the lowest patched version, as with the earlier pins. The audit passes apart from the advisory that was already ignored. Lint, typecheck, unit tests and build pass, and dcd-mcp answers initialize and tools/list over stdio with all five tools. * feat(auth): fetch the Supabase key from the API instead of compiling it in (#245) Every release so far compiled the Supabase anon key in, so the key could not be rotated without breaking pinned CLI versions (dcd#1112). The CLI now fetches its publishable key from the API's public GET /cli-config, once per process, so a rotation is an env change on the API. - The key is kept in memory only. A stale on-disk copy would make a session refresh fail with a 4xx, which drops the user's login. - A failed key fetch during refresh is a retryable error, never a definitive one, so the stored session survives. - Supabase URLs stay pinned (prod uses the cloud.devicecloud.dev custom domain); only the key comes from the API. - Raw Storage/Auth requests send an sb_publishable_ key as `apikey` only, and a legacy JWT key as a Bearer too. - Realtime keeps a synchronous subscribe() and connects once the key arrives; until then the backstop poll covers it. - The anon keys stay in the gitleaks allowlist, relabelled: they remain in history, which CI scans in full. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * ci: skip promotion-pin on release-please PRs (#249) Release-please's release PR targets production but is not a promotion, so the Release-As pin check always failed on it (#230, #247). Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(encrypt): list --encrypt in --help now encryption is on by defau… (#250) feat(encrypt): list --encrypt in --help now encryption is on by default (dcd#1138) The API now sends encryption.defaultOn = true for every org, not only orgs in the client_encryption beta, so the beta-only pieces come out: - --encrypt is listed in `dcd cloud --help` and `dcd upload --help` again, and its description says it is on by default and that --no-encrypt or DCD_ENCRYPT=0 turns it off. The hidden-arg filter (src/utils/help.ts), which only ever hid this flag, is removed; flags go back to citty's ArgsDef. - The notice drops "(beta)": "Encrypting uploads; opt out with --no-encrypt" (and "encrypt: false" from the MCP tool). It still prints only when the server default turned encryption on, and --json still suppresses it. - The KEK error messages, the MCP encrypt input and comments say "the default" or "the server default" instead of "the organization default". - server.json lists DCD_ENCRYPT, so MCP users can find the opt-out. - environments.ts no longer says the KEK is "left null until generated"; it describes how to rotate. resolveEncryption is unchanged: flag, then DCD_ENCRYPT, then the server default, then off. The default stays server-side so the API's CLIENT_ENCRYPTION_DEFAULT=off kill switch keeps working for every CLI. src/types/generated/schema.types.ts still carries the beta wording in one JSDoc description; a full regeneration would pull in unrelated API surface, so it waits for the next one. * feat: fetch the KEK public key from the API instead of pinning it (#251) The CLI pinned one KEK public key per environment, so every KEK rotation needed a CLI release that customers then had to pick up. GET /cli-config now publishes the newest KEK the API holds the private half of (binary_kek_public_key), and the CLI wraps uploads with whatever the API it talks to serves, fetched once per process alongside the Supabase key. DCD_BINARY_KEK_PUBLIC still overrides for an API that publishes nothing. An API that publishes no key keeps the old behaviour (explicit --encrypt fails, env/org-default encryption skips with a notice); an API that cannot be asked at all fails the run rather than silently uploading plaintext. * chore: merge production into dev after 5.9.0 (#248) * ReleaseAs: 5.8.0 * Release-As: 5.8.0 * Release-As: 5.8.0 * chore(production): release 5.8.0 * chore: pin the 5.9.0 promotion Release-As: 5.9.0 * chore(production): release 5.9.0 --------- Co-authored-by: finalerock44 <ethan@devicecloud.dev> Co-authored-by: finalerock44 <77282157+finalerock44@users.noreply.github.com> Co-authored-by: dcd-release-please[bot] <296541543+dcd-release-please[bot]@users.noreply.github.com> Co-authored-by: Tom Riglar <tom.riglar@moropo.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * deps: patch ip-address advisory (#256) Bump the transitive ip-address from 10.7.3 to 10.7.4 for GHSA-rxp7-6hxx-x8q5. express-rate-limit's ^10.2.0 range already allows it, so only the lockfile changes. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * ci: authenticate the Claude action by workload identity federation (#255) Swap the CLAUDE_CODE_OAUTH_TOKEN secret for the action's built-in OIDC exchange, so the workflows need no stored secret. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * chore: merge production into dev after 5.10.0 (#254) * ReleaseAs: 5.8.0 * Release-As: 5.8.0 * Release-As: 5.8.0 * chore(production): release 5.8.0 * chore: pin the 5.9.0 promotion Release-As: 5.9.0 * chore(production): release 5.9.0 * chore: pin the 5.10.0 promotion Release-As: 5.10.0 * chore(production): release 5.10.0 --------- Co-authored-by: finalerock44 <ethan@devicecloud.dev> Co-authored-by: finalerock44 <77282157+finalerock44@users.noreply.github.com> Co-authored-by: dcd-release-please[bot] <296541543+dcd-release-please[bot]@users.noreply.github.com> Co-authored-by: Tom Riglar <tom.riglar@moropo.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * ci: authenticate the Claude action with the org CLAUDE_CODE_OAUTH_TOKEN (#257) Back from workload identity federation to the OAuth token, now an org-level secret. Federation bills the Anthropic API org's purchased credit, which promotional credit doesn't cover for Claude Code, so reviews were failing with "Credit balance is too low". Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(auth): keep the stored session when a refresh fails A refused refresh used to delete the session from config.json. Old builds hit that on every refresh once Supabase turned off legacy keys ("Legacy API keys are disabled"), so the session was wiped even though an upgrade alone would have fixed it. Now only `dcd logout` (or the next `dcd login`) replaces the stored session. The error still tells the user to log in again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB * fix(device): show run results as one line per flow `dcd device run` printed the runner's raw JSON under "Output" on a pass and under "Error" on a failed directory run, env_vars included. It now reads that report (or `result.flows`, when the API sends it) and prints each flow with passed or failed and the reason, plus a short summary. The failure message names the failing flows. --json still carries the API's full result. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB * fix(device): leave session out of --json for list, models and stop --all `dcd device list --json` printed a stray "session": null, as if the command were about one session. Commands that are not about one session now leave the key out. Every other command's envelope is unchanged, and errors still carry session (null when the API sent none). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB * fix(device): infer the platform from --model `dcd device start --model iphone-16` failed with "--platform is required". Without --platform, start now looks the model up in GET /device/models (by id or name) and uses its platform. It is still a usage error when the model is unknown or exists on both platforms. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB * fix(device): warn when the video and log are still uploading Right after `dcd device stop`, `dcd device artifacts` downloaded only the per-action screenshots; the video and device log landed about 45s later, with no word that they were coming. It now warns "The video and device log are still uploading. Run this again in a minute." and puts `pending` and `warning` in --json. It reads the API's new `pending` field. An older API gets a guess instead: an ended session missing either file whose last activity was under three minutes ago. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB * feat(device): add request-human and human, like the MCP tools The MCP server has request_human and get_human_status; the CLI had nothing, though session warnings told CLI users to wait for a person. - `dcd device request-human "<reason>" [id] [--wait]` asks a person for help (POST .../request-human). - `dcd device human [id] [--wait]` shows whether the session waits for a person, who has control, when it ends, and the hand-back note. - --wait checks every 5s until the person hands back or the session ends (exit 4). Ctrl-C stops waiting and leaves the session open. - `dcd device status` shows the hand-back note, and --json carries it. - A command refused with 409 LIVE_HUMAN_IN_CONTROL now says a person has control and to run `dcd device human <id> --wait`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dcd-release-please[bot] <296541543+dcd-release-please[bot]@users.noreply.github.com> Co-authored-by: finalerock44 <ethan@devicecloud.dev> Co-authored-by: finalerock44 <77282157+finalerock44@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(device): say a person has control once in dcd device status (#259) The API's session warnings already say a person has control and how to wait, so status printed the same thing twice. Print our own line only when an older API leaves it out. Claude-Session: https://claude.ai/code/session_016xsPHeqEEpmiNy9YvGFDwB Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * feat(device): list dcd device and dcd mcp in help, and deprecate dcd live (#233) * feat(device): add dcd device start, list, status, stop, install, launch and models The first stages of the Agent Devices CLI (dcd plan 3.1 and 3.2). - --json prints one envelope, {ok, session, data, error}, on success and on failure. - Exit codes: 0 ok, 1 usage, 2 failed, 3 limit, 4 session ended or not found. - The session id may be left out when exactly one session is open. - start --app uploads a local build first; an https URL goes to the API's install-from-URL. - Progress goes to stderr, with no spinner unless at a terminal. Hidden from dcd --help until the API reaches production. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): add dcd device inspect, screenshot, run, logs and open Stage 3.3 of the Agent Devices CLI. - inspect prints the compact screen; screenshot saves the frame to a file and reports its path. - run sends one flow (a file or --yaml) with optional --env KEY=VALUE, and polls a flow that outlasts the API's wait. A failed flow exits 2. - logs prints the app's recent log lines; --follow keeps printing new ones until the session ends. - open opens the monitor at a terminal, and prints its URL otherwise. Running a directory of flows needs an API change first and is refused for now. openBrowser moves out of login.ts so device can share it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(device): list dcd device in help and deprecate dcd live Stage 3.4 of the Agent Devices CLI. dcd device appears in dcd --help. dcd live is hidden and prints a deprecation line on stderr pointing at dcd device; it keeps working for existing scripts. Merge only when the /device/sessions API is in production. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * fix(device): renew a dcd login token while polling logs --follow, a long run and start --wait reused the token resolved at the start. A dcd login access token lasts about an hour, so a poll past that failed with an auth error. Each poll now calls refreshAuth first; an API key is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA * feat(mcp): list dcd mcp in help at launch Unhide dcd mcp alongside dcd device, and update CLAUDE.md to match. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018tQfgpTsA9UhcopKoDezUA --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(device): open URLs without a shell, and keep device calls across a session renewal (#261) openBrowser: Windows used `cmd /c start`, which parses the URL again, so the login URL's `&` split it and ran the rest as a command (CodeQL: uncontrolled command line). It now uses rundll32, and opens only http(s) URLs. dcd mcp: renewing a `dcd login` session closed the hosted connection under calls still running, so a long `run` failed with "Connection closed" while the flow carried on. The bridge now keeps one connection and puts the current auth on each request. Calls that arrive together share one connect. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * fix(device): keep the --json envelope when request-human has no reason (#263) The reason positional was required, so citty refused a missing one before run() and the top-level handler printed { status: 'FAILED', error } instead of { ok, session, data, error }. It is now optional to citty, like every other dcd device positional, and run() rejects it with the usual CliError. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: dcd-release-please[bot] <296541543+dcd-release-please[bot]@users.noreply.github.com> Co-authored-by: finalerock44 <ethan@devicecloud.dev> Co-authored-by: finalerock44 <77282157+finalerock44@users.noreply.github.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…cp (#266) * docs(mcp): say the mcp command is for MCP clients to start Reword the dcd mcp help line so it is clear an MCP client starts it, not a person. Add a README section on dcd mcp and the hosted endpoint, next to the existing dcd-mcp docs. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(device): fit start's next steps to the session state A queued or provisioning session can't be inspected yet, so start now suggests dcd device status first and points at --wait. It suggests install when no app was given, and ends with dcd device --help. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
spawn reports a missing opener (xdg-open on a headless box) as an 'error' event, not a throw, and with no listener it crashed the process after dcd device open had printed "Opened the monitor". openBrowser now waits for 'spawn' or 'error' and returns false, so callers print the URL instead. Also corrects CLAUDE.md and the bridge.ts header: the bridge keeps one connection and re-reads auth on every request; it no longer remakes the connection when a session is renewed. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… binary (#267) dcd mcp install [claude-code|claude-desktop|cursor|vscode] adds the server to an AI tool: through claude mcp add or code --add-mcp, or by merging an entry into the Claude Desktop / Cursor JSON config. Entries start dcd by full path, since desktop apps don't see the shell's PATH. The install scripts point at dcd mcp install and print the full path. The README leads with dcd mcp install, then full-path configs for each client (with VS Code's own format), with npx as the fallback. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
#268) fix(device): drop Pixel 6 Pro and Pixel 7 Pro, and mirror the Pixel 10 default The API removes pixel-6-pro and pixel-7-pro on 26 Oct 2026 and moves the default Android device from pixel-7 to pixel-10 (API 36 unchanged). pixel-7 stays until 9 Nov 2026. - The help-text enum loses both devices; validation already follows the live compatibility data. - The default-device mirror is pixel-10, so a version-only request is checked against the device the API will actually use. A level Pixel 10 doesn't offer (32-35) is refused with the devices that do offer it, which is what the API now answers too. - dcd live's --android-device help lists the live-session devices the API offers (the API already refuses pixel-7 and the Pro devices for live). - Device-matrix and MCP examples use devices that outlive Pixel 7. schema.types.ts is deliberately not regenerated: the API drift would come with it.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…po (#271) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* fix(upload): retry Backblaze uploads on transient failures Direct-to-B2 uploads made exactly one attempt per request. A 503, 408, 429 or dropped connection on any part failed the whole B2 upload, and the Supabase upload behind it was the only thing that caught it. - Retry each B2 request up to 4 times with jittered exponential backoff (1s, 2s, 4s; capped at 8s) on network errors and 408 / 429 / 5xx. - Large-file parts move on to the next upload URL on each retry, as B2 asks after a 503. The URLs are bound to the file, not the part number, so any of them can carry any part. A 401 is retried only there. - The simple (pre-signed S3) upload retries the same URL. - When every upload fails, the error now names the Backblaze failure as well as the Supabase one, instead of hiding it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(upload): trim incident detail from B2 retry comments Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… 2x (#272) * feat(cloud): say which flows use extended runtime, and that they cost 2x Any org can now opt a flow into the 20-minute extended runtime with DEVICECLOUD_OVERRIDE_EXTENDED_RUNTIME: true, and each such flow is billed at 2x. `dcd cloud` already listed the override under "Overrides"; it now adds one line after them, e.g. "2 flows with extended runtime: up to 20 min each, billed at 2×", so the price is clear before the run. It counts the same values the API treats as opted in (true and 'true'). The MCP run_cloud_test tool's waitTimeoutSeconds description now says that such a flow can run for up to 20 minutes, past the 600 s default. * Clarify billing details in extended runtime test Updated comment to clarify billing for extended runtime flows. * Fix comment formatting in countExtendedRuntimeFlows * Update src/services/execution-plan.service.ts
…ed run (#273) `dcd device inspect` now leaves out the on-screen keyboard and System UI by default (the API does this), says when a keyboard is open and how many elements it left out, and `--all` shows them. Elements show checked, selected, focused and disabled. A failed `dcd device run` prints the app's visible text after the failure. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Dependabot groups apply to scheduled version updates unless they say `applies-to: security-updates`, so every advisory opened its own PR. Add a `security` group to each npm entry, so a burst of advisories arrives as one PR. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
`dcd device run <dir>` and the MCP `run` tool's `path` zipped every file in the directory. A samples folder with builds next to its flows sent all of them, so an agent copied the flow, scripts and subflows to a temp folder by hand. A bundle now holds every YAML file (flows, subflows, config), the files the flows use (runScript, addMedia, assertScreenshot and the like, found the way `dcd cloud` finds them), and config's includedPaths. Nothing else. With an entry flow (`--entry`, or the MCP `flow` input), only its files must exist; another flow's missing file doesn't stop it. Without one, every flow's must. A file outside the directory is an error that names it. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…patch group (#276) chore: bump typescript-eslint Bumps the minor-and-patch group with 1 update: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `typescript-eslint` from 8.71.2 to 8.72.0 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.72.0/packages/typescript-eslint) --- updated-dependencies: - dependency-name: typescript-eslint dependency-version: 8.72.0 dependency-type: direct:development update-type: version-update:semver-minor dependency-group: minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…277) * feat(api): send the CLI version and CI identity on every API request x-dcd-cli-version, x-dcd-ci-provider and x-dcd-ci-wrapper-version were only sent on the compatibility call, for notices. resolveAuth now puts them on every auth context, so every gateway sends them. The API records them on each upload and binary, and on its HTTP log line, so it can tell which CLIs and CI wrappers still use an endpoint before it is retired. fetchCompatibilityData loses its client-context argument, since the auth headers carry the same values. MCP calls now send them too. Refs dcd#1622 * Update src/utils/auth.ts * Correct comment formatting in auth.test.ts Fix comment formatting in auth test file.
…s, failed starts say what to check (#278) - dcd_run_cloud_test with dryRun listed every flow in the folder, because it read the metadata of all flows rather than the ones that would run. It now lists the flows after includeTags, excludeTags and excludeFlows, as `dcd cloud --dry-run` does. Real runs already submitted only those flows. - `run` with path set to one flow file sent it as yaml on its own, so a flow that uses other files (runFlow, runScript, addMedia and the like) failed on the device with an invalid file path. Such a file is now uploaded with its folder as a bundle, with flow set to the file. A file that uses no other files is still sent as yaml. - A start_device call that fails without an answer may still have started a session, which is billed while it runs. The error now says so and lists the open sessions started in the last two minutes, so an agent checks list_devices instead of starting a second device. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…sked (#279) `dcd device human` labelled the start of a takeover "asked", though the agent never asked for anything. A request a person picked up stays "asked". Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Release-As: 5.11.0
|
|
||
| /** Run another tool's CLI without a shell. Undefined when it isn't installed. */ | ||
| function runTool(command: string, args: string[]): { ok: boolean; output: string } | undefined { | ||
| const result = spawnSync(command, args, { encoding: 'utf8' }); |
There was a problem hiding this comment.
Bug: dcd mcp install vscode can't find code on Windows, at least under Node.
On Windows, VS Code's command line lives only in …\Microsoft VS Code\bin\code.cmd. Without a shell, Node's spawnSync looks for .com and .exe files on PATH and skips .cmd. So spawnSync('code', …) fails with ENOENT, runTool returns undefined, and the user is told "The code command isn't on PATH" even when code works in their terminal. Recent Node versions also won't spawn a .cmd file unless you pass shell: true (CVE-2024-27980). npm installs of claude (claude.cmd) hit the same problem at line 77.
I haven't checked whether the bun-compiled binary resolves .cmd differently, but anyone who installed dcd with npm on Windows is affected. Windows is a supported platform: there's install.ps1, and clientConfigPath handles win32.
Two possible fixes:
- On
win32, trycode.cmd/claude.cmd. - Spawn through
cmd.exewithshell: true. If you do, quote the JSON argument fromvscodeAddArgscarefully, becausecmdwill reparse it.
Lines 42 to 47 in e83ae64
What & why
Type of change
fix— bug fixfeat— new featureperf— performance improvementrefactor— code change that's neither a fix nor a featuredocs— documentation onlychore/ci/build/test— tooling, no user-facing change!or PR notes aBREAKING CHANGE:)Checklist
pnpm lintpassespnpm typecheckpassespnpm buildpassesCHANGELOG.md(release-please handles this)README.md/STYLE_GUIDE.mdupdated if behaviour or output changedHow to test