Skip to content

Commit eaac4cb

Browse files
riglardcd-release-please[bot]finalerock44github-actions[bot]dependabot[bot]
authored
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>
1 parent a1348a2 commit eaac4cb

55 files changed

Lines changed: 2561 additions & 776 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/workflows/claude-code-review.yml‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,26 @@ jobs:
7676
# The plugin also stops early on PRs it judges trivial. The prompt above
7777
# tells Claude to post a one-line note in that case, so a deliberate skip
7878
# leaves a comment and only a real failure trips this check.
79+
#
80+
# Nor is a PR that edits THIS file: the action refuses to run unless the
81+
# workflow is identical to the default branch's ("Workflow validation
82+
# failed"), a deliberate guard against a PR rewriting its own reviewer. It
83+
# exits green having posted nothing, so pass with a notice instead.
7984
- name: Check a review comment was posted
8085
env:
8186
GH_TOKEN: ${{ github.token }}
8287
REPO: ${{ github.repository }}
8388
PR: ${{ github.event.pull_request.number }}
89+
SELF: ${{ github.workflow_ref }}
8490
run: |
91+
# workflow_ref is "owner/repo/.github/workflows/<file>@<ref>".
92+
self_path="${SELF#"$REPO"/}"
93+
self_path="${self_path%@*}"
94+
if gh api "repos/$REPO/pulls/$PR/files" --paginate --jq '.[].filename' | grep -qxF "$self_path"; then
95+
echo "::notice::This PR edits $self_path, so the review action skipped it by design (it only runs the default branch's version of its own workflow). No review on this PR."
96+
exit 0
97+
fi
98+
8599
# Prints the time of every claude[bot] comment, inline comment and review.
86100
claude_posts() {
87101
gh api "repos/$REPO/issues/$PR/comments" --paginate \

‎.github/workflows/cli-ci.yml‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,11 @@ jobs:
113113
#
114114
# It has to be on a NORMAL commit, not the merge commit — release-please's
115115
# commit splitting is unreliable on merges.
116+
#
117+
# Release-please's own release PR also targets `production` but is not a
118+
# promotion: it never carries a pin, so the check would always fail on it.
116119
promotion-pin:
117-
if: github.event_name == 'pull_request' && github.base_ref == 'production'
120+
if: github.event_name == 'pull_request' && github.base_ref == 'production' && !startsWith(github.head_ref, 'release-please--')
118121
runs-on: ubuntu-latest
119122
steps:
120123
- uses: actions/checkout@v7

‎.gitleaks.toml‎

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,32 @@
11
# Gitleaks configuration — extends the default ruleset.
22
#
33
# The only allowlisted secrets are PUBLIC key material committed in
4-
# src/config/environments.ts:
5-
# - the two Supabase anon keys — anon-role JWTs, designed to be embedded in
6-
# client code and gated by RLS (see the doc comment in that file).
7-
# - the two KEK public keys (prod + dev) for client-side binary envelope
8-
# encryption — base64 of the raw 32-byte X25519 PUBLIC half. They can only
9-
# *encrypt*; the private halves live solely on the API, never in this repo.
10-
# Both are intentionally not secret; gitleaks flags them on entropy alone
4+
# src/config/environments.ts: the two KEK public keys (prod + dev) for
5+
# client-side binary envelope encryption — base64 of the raw 32-byte X25519
6+
# PUBLIC half. They can only *encrypt*; the private halves live solely on the
7+
# API, never in this repo. gitleaks flags them on entropy alone
118
# (generic-api-key), not because it recognizes them as credentials.
129
#
10+
# The two legacy Supabase anon keys (prod + dev) are also listed, but only
11+
# because they remain in git history and CI scans the full history. The source
12+
# no longer contains them: the CLI fetches its publishable key from the API's
13+
# GET /cli-config at runtime (dcd#1112). Do not add a Supabase key back.
14+
#
1315
# They are allowlisted by EXACT VALUE, deliberately not by file path or by the
14-
# whole `jwt` / `generic-api-key` rule: a Supabase service_role key is also a
15-
# JWT, and a KEK *private* key is the same shape as its public half, so a
16-
# path/rule allowlist would let a genuinely sensitive key pasted into the same
17-
# file slip through. Matching exact values keeps that detection intact.
16+
# whole `generic-api-key` rule: a KEK *private* key is the same shape as its
17+
# public half, so a path/rule allowlist would let a genuinely sensitive key
18+
# pasted into the same file slip through. Matching exact values keeps that
19+
# detection intact.
1820
#
1921
# When a KEK is rotated, replace the corresponding value below — do not simply
2022
# append, or the retired key stops being distinguishable from a live secret.
2123
[extend]
2224
useDefault = true
2325

2426
[allowlist]
25-
description = "Public Supabase anon keys and KEK public keys (safe to commit)"
27+
description = "KEK public keys, and retired Supabase anon keys left in history (safe)"
2628
regexes = [
29+
# retired legacy anon keys (prod, dev), history only
2730
'''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InBneWRucGhiaW1ldGluc2dma2JvIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDc1OTQzNDYsImV4cCI6MjAyMzE3MDM0Nn0\.hAYOMFxxwX1exkQkY9xyQJGC_GhGnyogkj2N-kBkMI8''',
2831
'''eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9\.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxibXNvd2VodGp3bnFsdXJwZW1iIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MDkyMTg0ODcsImV4cCI6MjAyNDc5NDQ4N30\.zeLTMAuZ_WwYvGdeP0kdvL_Zrs-RQee5APPyxmWq7qQ''',
2932
# prod kekPublicKey v1

‎.husky/pre-commit‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#!/usr/bin/env sh
22
# Scan staged changes for secrets before they ever enter git history.
33
# Mirrors the gitleaks CI gate (.github/workflows/cli-ci.yml) and shares the
4-
# same allowlist (.gitleaks.toml), so the public Supabase anon keys won't trip.
4+
# same allowlist (.gitleaks.toml), so the public KEK keys won't trip.
55
#
66
# gitleaks is NOT a dependency of this package — contributors install the binary
77
# themselves. If it's missing we skip (exit 0) rather than block: CI is the

‎.release-please-manifest.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "5.7.1"
2+
".": "5.10.0"
33
}

‎CHANGELOG.md‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,62 @@
11
# Changelog
22

3+
## [5.10.0](https://github.com/devicecloud-dev/dcd-cli/compare/v5.9.0...v5.10.0) (2026-10-22)
4+
5+
6+
### Features
7+
8+
* **encrypt:** list --encrypt in --help now encryption is on by defau… ([#250](https://github.com/devicecloud-dev/dcd-cli/issues/250)) ([55a0609](https://github.com/devicecloud-dev/dcd-cli/commit/55a0609abbc686021fe969c523bcaa1efa994658))
9+
* **encrypt:** list --encrypt in --help now encryption is on by default (dcd[#1138](https://github.com/devicecloud-dev/dcd-cli/issues/1138)) ([55a0609](https://github.com/devicecloud-dev/dcd-cli/commit/55a0609abbc686021fe969c523bcaa1efa994658))
10+
* fetch the KEK public key from the API instead of pinning it ([#251](https://github.com/devicecloud-dev/dcd-cli/issues/251)) ([452324b](https://github.com/devicecloud-dev/dcd-cli/commit/452324ba6f6313fe9e4d418c3db655e858f79540))
11+
12+
13+
### Miscellaneous
14+
15+
* pin the 5.10.0 promotion ([d85115c](https://github.com/devicecloud-dev/dcd-cli/commit/d85115c1efc1da80b7eaeb9dacca49dd0d37257b))
16+
17+
## [5.9.0](https://github.com/devicecloud-dev/dcd-cli/compare/v5.8.0...v5.9.0) (2026-10-22)
18+
19+
20+
### Features
21+
22+
* **auth:** fetch the Supabase key from the API instead of compiling it in ([#245](https://github.com/devicecloud-dev/dcd-cli/issues/245)) ([8cedb4e](https://github.com/devicecloud-dev/dcd-cli/commit/8cedb4e2f133f4300b669f877896e65c61c38820))
23+
* **login:** reuse the last org and add --org ([#243](https://github.com/devicecloud-dev/dcd-cli/issues/243)) ([5f710d5](https://github.com/devicecloud-dev/dcd-cli/commit/5f710d55d024c6869d529c606f261530ac9013ad))
24+
25+
26+
### Dependencies
27+
28+
* bump the minor-and-patch group across 1 directory with 5 updates ([#238](https://github.com/devicecloud-dev/dcd-cli/issues/238)) ([5fffd7a](https://github.com/devicecloud-dev/dcd-cli/commit/5fffd7ae00e2e6749271ae859c6cf6c47d98678f))
29+
30+
31+
### Miscellaneous
32+
33+
* pin the 5.9.0 promotion ([57cf6b1](https://github.com/devicecloud-dev/dcd-cli/commit/57cf6b1fc0cb74ac4fe9f30b6659095d4321fbfc))
34+
35+
## [5.8.0](https://github.com/devicecloud-dev/dcd-cli/compare/v5.7.1...v5.8.0) (2026-10-16)
36+
37+
38+
### Features
39+
40+
* **cloud:** add --priority flag for within-org run ordering ([#222](https://github.com/devicecloud-dev/dcd-cli/issues/222)) ([510d64b](https://github.com/devicecloud-dev/dcd-cli/commit/510d64baa34e3f7f19c5483f693aced20c0f0758))
41+
* **cloud:** add --priority flag for within-org run ordering (dcd[#1581](https://github.com/devicecloud-dev/dcd-cli/issues/1581)) ([510d64b](https://github.com/devicecloud-dev/dcd-cli/commit/510d64baa34e3f7f19c5483f693aced20c0f0758))
42+
43+
44+
### Bug Fixes
45+
46+
* **cloud:** re-send a flow submit whose response was lost ([#226](https://github.com/devicecloud-dev/dcd-cli/issues/226)) ([f4db8f9](https://github.com/devicecloud-dev/dcd-cli/commit/f4db8f9d0b51d358b62153f40c2164abc67cb078))
47+
* **device:** drop Android API 29-31 from the --android-api-level help ([#227](https://github.com/devicecloud-dev/dcd-cli/issues/227)) ([05ea2dc](https://github.com/devicecloud-dev/dcd-cli/commit/05ea2dc292b1c8d2d453a4ec05b3c34577cf2c61))
48+
* **validation:** mirror the API's Pixel 7 / API 36 Android default ([#225](https://github.com/devicecloud-dev/dcd-cli/issues/225)) ([3daeb94](https://github.com/devicecloud-dev/dcd-cli/commit/3daeb94e077a3819f4568c74cc5059bd8ac9ba9b))
49+
50+
51+
### Performance
52+
53+
* **cloud:** poll results with ?view=summary and If-None-Match ([#219](https://github.com/devicecloud-dev/dcd-cli/issues/219)) ([e916c59](https://github.com/devicecloud-dev/dcd-cli/commit/e916c59631ee10aee0c315af016582747afa0b81))
54+
55+
56+
### Dependencies
57+
58+
* bump the minor-and-patch group with 6 updates ([#220](https://github.com/devicecloud-dev/dcd-cli/issues/220)) ([2b88b64](https://github.com/devicecloud-dev/dcd-cli/commit/2b88b6489d77591f3f47b00c980e11d5e6cc2e27))
59+
360
## [5.7.1](https://github.com/devicecloud-dev/dcd-cli/compare/v5.7.0...v5.7.1) (2026-10-07)
461

562

‎CLAUDE.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Top-level `defineCommand` in `src/index.ts` wires eleven subcommands (`cloud`, `
4343

4444
**Auth.** Every command calls `resolveAuth({ apiKeyFlag })` (`src/utils/auth.ts`) once and threads the returned `AuthContext` into gateways/services. `ApiGateway` and `fetchCompatibilityData` spread `auth.headers` into fetch headers — they no longer accept a raw api key. Precedence: `--api-key` flag > `DEVICE_CLOUD_API_KEY` env > stored session from `dcd login`. `resolveAuth` refreshes expiring Supabase sessions via `CliAuthGateway.refresh` and rewrites the config atomically. A session's access token lasts about an hour, so code that can outlive it re-checks with `isAuthExpiring` / `refreshAuth`: the `dcd cloud` poll loop and the report downloads after it, the MCP context, and `dcd_run_cloud_test`'s wait loop. `refreshAuth` keeps the org the command started with and leaves API-key contexts untouched.
4545

46-
**Config store.** `dcd login` writes `$XDG_CONFIG_HOME/dcd/config.json` (fallback `~/.dcd/config.json`, 0600). Shape: `{ version, env, api_url, supabase_url, session: { access_token, refresh_token, expires_at, user_email, user_id }, current_org_id, current_org_name }`. `DCD_CONFIG_DIR` overrides the directory (used by tests). The login command itself (`src/commands/login.ts`) uses PKCE (S256) with a server rendezvous — no loopback server: it mints `state`, `code_verifier`, and `code_challenge`, opens `<frontend>/cli-login?state=...&code_challenge=...`, then polls the dcd API's `POST /cli-login/claim` with `{state, code_verifier}` while the frontend POSTs proof of identity (the browser's access token) to `POST /cli-login/handoff`; the API verifies the token, **mints a dedicated Supabase session for the CLI** (its own refresh-token family — sharing the browser's tokens caused "Invalid Refresh Token: Already Used" whenever either client rotated them), stores it keyed by state, then on claim verifies `sha256(verifier) === challenge` and returns it. After claiming, the CLI fetches `/me/orgs` and prompts for an org (the same picker `dcd switch-org` uses). The frontend lives in `../dcd/frontend/app/features/cli-login/CliLoginScreen.tsx`.
46+
**Config store.** `dcd login` writes `$XDG_CONFIG_HOME/dcd/config.json` (fallback `~/.dcd/config.json`, 0600). Shape: `{ version, env, api_url, supabase_url, session: { access_token, refresh_token, expires_at, user_email, user_id }, current_org_id, current_org_name }`. `DCD_CONFIG_DIR` overrides the directory (used by tests). The login command itself (`src/commands/login.ts`) uses PKCE (S256) with a server rendezvous — no loopback server: it mints `state`, `code_verifier`, and `code_challenge`, opens `<frontend>/cli-login?state=...&code_challenge=...`, then polls the dcd API's `POST /cli-login/claim` with `{state, code_verifier}` while the frontend POSTs proof of identity (the browser's access token) to `POST /cli-login/handoff`; the API verifies the token, **mints a dedicated Supabase session for the CLI** (its own refresh-token family — sharing the browser's tokens caused "Invalid Refresh Token: Already Used" whenever either client rotated them), stores it keyed by state, then on claim verifies `sha256(verifier) === challenge` and returns it. After claiming, the CLI fetches `/me/orgs` and picks the org with `selectOrg` (`src/utils/orgs.ts`): `--org <name|id>` if given, else the only org, else the picker `dcd switch-org` uses, starting on the last login's org. That org only counts when the stored `api_url` matches, because org ids are per environment. Without a TTY (`canPrompt` in `src/utils/ci.ts`) the prompts can't be answered: the "already logged in" confirm takes its default and signs in again, the last login's org is used, and with none the command fails pointing at `--org`. `dcd logout` deletes the whole config, so nothing is remembered after it. The frontend lives in `../dcd/frontend/app/features/cli-login/CliLoginScreen.tsx`.
4747

4848
**Cross-repo auth surface.** The dcd API's `ApiKeyGuard` accepts either `x-app-api-key` (existing) or `Authorization: Bearer <jwt>` + `x-dcd-org: <id>`. For Bearer it verifies the JWT, checks `user_org_profile` membership, and injects the org's api_key back into the request headers so existing `@Headers(APP_API_KEY_HEADER)` controller code keeps working unchanged. `dcd switch-org` calls `GET /me/orgs`, a JWT-only endpoint at `../dcd/api/src/apps/me/me.controller.ts`.
4949

‎package.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
"chai": "^6.2.2",
3131
"eslint": "^10.5.0",
3232
"eslint-config-prettier": "^10.1.8",
33-
"eslint-plugin-unicorn": "^77.0.0",
33+
"eslint-plugin-unicorn": "^78.0.0",
3434
"husky": "^9.1.7",
3535
"mocha": "^12.0.0",
3636
"prettier": "^3.8.4",
@@ -68,7 +68,7 @@
6868
"test:unit": "node scripts/test-runner.mjs --unit",
6969
"typecheck": "tsc --noEmit -p tsconfig.test.json"
7070
},
71-
"version": "5.7.1",
71+
"version": "5.10.0",
7272
"bugs": {
7373
"url": "https://discord.gg/gm3mJwcNw8"
7474
},
@@ -110,7 +110,7 @@
110110
"esbuild@<0.28.1": ">=0.28.1",
111111
"qs@<6.16.0": "6.16.0",
112112
"fast-uri@>=3.0.0 <3.1.8": "3.1.8",
113-
"hono@>=4.0.0 <4.13.7": "4.13.7",
113+
"hono@>=4.0.0 <4.13.11": "4.13.11",
114114
"source-map-js@<1.2.2": "1.2.2",
115115
"micromatch>picomatch": "^2.3.2",
116116
"tinyglobby>picomatch": "^4.0.4"

0 commit comments

Comments
 (0)