Skip to content

feat(cli): sign in with xping login --device, and add --no-browser and --workspace (P4) - #249

Merged
xping-admin merged 4 commits into
feat/cli-authfrom
feat/cli-auth-04-device-login
Oct 6, 2026
Merged

xping-admin merged 4 commits into
feat/cli-authfrom
feat/cli-auth-04-device-login

Conversation

@xping-admin

@xping-admin xping-admin commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

Phase 4 of docs/internals/implementation-specs/cli-auth-cli-spec.md (§19).

Contract / spec sections

  • §6 device flow (contract §3.2, §4.5): DeviceFlow — fresh discovery, prompt, optional browser open of verification_uri_complete, polling that waits the interval before every poll, slow_down +5 s, transient errors keep the interval, deadline and expired_token → exit 12, access_denied → 11, invalid_grant / other → 14.
  • §3.2 --device, --no-browser, --workspace; §4.2 xping_workspace_id on the authorization URL; §5.2 --no-browser skips detection.

Spec amendments (first commit)

  • --workspace must be a ULID; otherwise exit 2 before any request (§3.2, §13).
  • --no-browser first line drops "No browser was found…" (§3.2, §5.2).
  • DeviceFlowTests run against FakeCloud with the real OAuthClient, as the loopback flow is tested (§18.1).

Also

  • SignedInRecord and FlowFailures extracted from LoopbackFlow and shared by both flows.
  • FakeCloud ids are now valid ULIDs (they contained U).

Tests

  • Login_Device_Success, _SlowDown, _Denied, _Expired, _ServerExpired, _CancelledWhilePolling, browser/headless/--no-browser, --workspace, interactive precondition (LoginDeviceTests).
  • DeviceFlowTests: poll gaps on the fake clock, slow_down accumulation (5/10/15 s), transient errors keep the interval, deadline without an extra poll, error mapping, start refusal.
  • LoginCommandTests: --no-browser wording, --workspace on the authorize URL, invalid workspace → exit 2.

dotnet build -c Release clean, tests/Xping.Cli.Tests 1979/1979 green, dotnet format --verify-no-changes clean.

Still to do (exit criterion)

  • Manual: xping login --device over SSH (macOS, file store), approved on another device, xping auth status.

🤖 Generated with Claude Code

xping-admin and others added 2 commits October 6, 2026 20:38
…er wording, device flow tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d --workspace (P4)

xping login --device runs the device flow (cli-auth-cli-spec §6): fresh
discovery, the page and code printed, the page with the code filled in
opened when a browser is available, then polling that always waits the
interval first, adds 5 s on slow_down, keeps the interval on network
errors, and ends on approval, denial, expiry or the server's error.

--no-browser prints the loopback link without looking for a browser.
--workspace preselects a workspace on both flows; it must be a ULID,
otherwise it is a parse error before any request.

The record building and the shared failure messages move out of
LoopbackFlow so both flows use them. The fake Cloud's ids are now valid
ULIDs (they contained U, which Crockford base32 excludes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.57282% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/Xping.Cli/Auth/Flows/FlowFailures.cs 92.85% 0 Missing and 2 partials ⚠️
src/Xping.Cli/Auth/Flows/DeviceFlow.cs 98.11% 0 Missing and 1 partial ⚠️
src/Xping.Cli/Commands/Auth/LoginCommand.cs 97.61% 0 Missing and 1 partial ⚠️
src/Xping.Cli/Program.cs 96.55% 0 Missing and 1 partial ⚠️
Files with missing lines Coverage Δ
src/Xping.Cli/Auth/Flows/BrowserChoice.cs 100.00% <100.00%> (ø)
src/Xping.Cli/Auth/Flows/LoopbackFlow.cs 97.26% <100.00%> (ø)
src/Xping.Cli/Auth/Flows/SignedInRecord.cs 100.00% <100.00%> (ø)
src/Xping.Cli/Auth/OAuthClient.cs 97.70% <100.00%> (ø)
...c/Xping.Cli/Hosting/ServiceCollectionExtensions.cs 100.00% <100.00%> (ø)
src/Xping.Cli/Auth/Flows/DeviceFlow.cs 98.11% <98.11%> (ø)
src/Xping.Cli/Commands/Auth/LoginCommand.cs 85.84% <97.61%> (ø)
src/Xping.Cli/Program.cs 93.62% <96.55%> (ø)
src/Xping.Cli/Auth/Flows/FlowFailures.cs 92.85% <92.85%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

xping-admin and others added 2 commits October 6, 2026 21:11
…, 429 is transient, deadline-capped wait

- §2.4: a 429 without an OAuth body is transient while polling.
- §6.1: a user_code with a control character is an incomplete response.
- §6.3: the last wait stops at the deadline; the first unreachable poll
  prints one warning; other poll errors map as §4.8 (invalid_client is
  CloudUnreachable), control characters dropped.
- §3.2: the waiting line never rounds a lifetime to "0 minutes".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…p server text out of the terminal (#249 review)

- invalid_client while polling exits 15 with the --cloud-url hint, and
  invalid_request asks for a bug report, as at the start of the flow.
- A user_code with a control character is refused, and server error codes
  lose their control characters in messages and in --json oauthError.
- A 429 while polling waits for the next interval instead of ending the
  login; the first unreachable poll prints one warning on stderr.
- The last wait stops at the deadline, so expiry is reported on time.
- The waiting line shows seconds when the lifetime is not whole minutes.
- One BrowserChoice decides whether either flow opens a browser;
  LoginOptions has no unused defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xping-admin
xping-admin merged commit 2399e8c into feat/cli-auth Oct 6, 2026
2 checks passed
@xping-admin
xping-admin deleted the feat/cli-auth-04-device-login branch October 6, 2026 19:18
xping-admin added a commit that referenced this pull request Oct 9, 2026
…fidence in xping report (#265)

* feat(cli): add login, logout and auth status shells with global Cloud options (P0) (#243)

* docs(cli): name the auth phase branches feat/cli-auth-NN-<name>

Git cannot hold feat/cli-auth and feat/cli-auth/... at once, so the
sub-branch names in §19 of the CLI auth spec could not be created.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): add login, logout and auth status shells with global Cloud options (P0)

Phase 0 of cli-auth-cli-spec: the global --cloud-url, --api-key and
--verbose options; CliConfiguration (flag > XPING_* > Xping__* >
appsettings) with Cloud URL validation and normalization; the auth exit
codes; Redaction; a verbose stderr logger for Xping.Cli.* only; Ctrl+C
as exit 130; scrubbed reporting of escaping exceptions; a test hook on
Program.Run; and the CloudUrl row in the configuration reference.

login, logout and auth status print "not implemented" and exit 14.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): tighten the auth spec's redaction rules

The JWT pattern in §15.2 matched .NET type names and would mangle
--verbose stack traces; it now requires the eyJ header and payload. The
form-field list gains access_token, id_token and client_secret, and the
same names are scrubbed as JSON members. Phase 8 also removes the
temporary ci.yml trigger for feat/cli-auth.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: run the test pipeline on PRs into feat/cli-auth

Temporary: the CLI auth phases merge into feat/cli-auth by PR, and those
PRs need CI. Removed in the last phase (cli-auth-cli-spec §19, phase 8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): keep Ctrl+C working for report and stop auth errors leaking secrets (P0)

Review fixes for the phase 0 scaffold:

- Ctrl+C is swallowed only while a command that watches the token runs
  (login, logout, auth status), and a second press still ends the
  process. report, where and clear stop at once again, prompt included.
- Redaction also covers access_token, id_token and client_secret, and
  token values in JSON bodies. The JWT rule requires the eyJ header and
  payload, so verbose stack traces keep their type names.
- Cloud URL errors no longer quote the value as given: user info is
  checked first, and a query or fragment is never echoed. Parse errors
  are scrubbed as a backstop.
- XPING__APIKEY and the other all-capitals nested variables are read,
  as the SDK reads them.
- A missing settings directory holds no settings; an unreadable file is
  a CliConfigurationException naming the file.
- The usage hint names the command when a global option comes first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): add Cloud discovery and the OAuth client for xping login (P1) (#244)

* docs(cli): follow the contract's issuer and revocation amendments in the auth spec

Contract OQ-17 makes the discovery issuer the Cloud URL followed by one
slash, as OpenIddict writes it; OQ-19 lets the revocation body be {}.
§2.3 and §14.1 compare against {Cloud URL}/, §2.4 ignores the revoke body.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): pin the discovery URL rules and device-poll retries in the auth spec

Endpoints in the discovery document must meet the Cloud URL's scheme
rule, since codes and refresh tokens are sent there; the gateway URI also
refuses a query. PollDeviceAsync makes no retries of its own: the next
poll is the retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): add Cloud discovery and the OAuth client for xping login (P1)

Phase 1 of cli-auth-cli-spec: DiscoveryClient validates the discovery
document in the §2.3 order (issuer is the Cloud URL plus one slash per
contract OQ-17, gateway URI, contract version, minimum CLI version,
endpoints) and fails closed with CloudUnreachable or
CloudVersionMismatch. Documents are cached for 24 hours under
~/.xping/cache/discovery, owner-only, and revalidated on read; login
bypasses the cache.

OAuthClient covers the code exchange, refresh (no scope), device start
and poll, and revocation (body ignored, OQ-19), with the contract §8.1
retries on an injected TimeProvider. Device polling never retries on its
own. Tokens and device codes are registered with Redaction and kept out
of ToString.

The xping-oauth client follows no redirects, sends no cookies, has a
15 s timeout and no factory logging.

Tests run against FakeCloud, an in-process Portal with PKCE, single-use
codes, refresh rotation with the 30 s reuse leeway, device grants,
revocation, fault injection and a request log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): amend the auth spec for the P1 review fixes

The code exchange does not retry after a network failure, since the code
may already be redeemed. Network reasons are named broadly (connection
failed, TLS error). Temporary files are unique per write, every missing
level under ~/.xping is created 0700, and an existing ~/.xping made by
the SDK local store in a repository home is tightened to 0700.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): harden discovery and the OAuth client after review (P1)

- The code exchange no longer retries after a network failure: the
  server may have redeemed the single-use code, and the retry's
  invalid_grant hid the real failure. 5xx answers are still retried.
- Temporary files are unique per write; two writers of one file in one
  process deleted each other's temporary file and lost a write.
- An existing ~/.xping with group or other bits is tightened to 0700,
  and every missing level is created 0700, not only the last one.
- Device verification pages follow the server URL rule (https, or http
  on localhost/127.0.0.1), shared with discovery as ServerUri.
- SemVer parsing refuses a trailing newline and non-ASCII digits, and
  compares numeric prerelease identifiers of any length.
- Network reasons read "connection failed" and "TLS error", which is
  all HttpRequestError can tell apart.
- PostAsync loses its unused cloudUrl parameter.

Tests: DriveAsync moves the fake clock exactly by each timer the client
starts, so retry gaps are exact on any machine and a stuck task fails
after 30 s instead of hanging. Each fix has a regression test that fails
without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): add the file credential store and the credential resolver (P2) (#245)

* docs(cli): amend the auth spec for the file store and the resolver (P2)

ReadAsync returns the warning with the result, CredentialStores owns the
read order, an unsafe file's entries are never carried into a safe one,
and the resolver takes CliConfiguration and leaves when-to-fall-back to
the authenticated pipeline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): add the file credential store and the credential resolver (P2)

CredentialRecord, ICredentialStore and FileCredentialStore keep sign-ins
in ~/.xping/credentials.json (0600 in a 0700 directory, owner-only ACL on
Windows, atomic replace). Files others can read are refused with chmod
instructions; corrupt entries read as nothing with a warning and are kept.
CredentialStores holds the keychain-then-file read order, and
CredentialResolver picks --api-key, the stored login, then the ambient
key, without a network call.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): amend the auth spec for the P2 review fixes

A failing credential store no longer stops the lookup or the sign-out,
an unreadable store is reported separately from a corrupt entry, and a
delete leaves a file it cannot parse untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): harden the file credential store and the resolver after review (P2)

- A ~/.xping the user cannot search is a CredentialStoreException, not a
  raw UnauthorizedAccessException from the mode check.
- CredentialStores keeps reading after a store fails, so a locked keychain
  cannot hide a file login, and DeleteAllAsync tries every store before
  reporting the failures.
- The resolver reports unreadable stores as StoreFailures, apart from
  corrupt-entry warnings, so auth status can exit 17.
- Signing out leaves an unparseable file untouched instead of deleting
  every Cloud URL's sign-in.
- Reads share the file for delete, so on Windows a reader no longer makes
  the replacing move fail.
- XpingHome.Display checks for an empty profile before GetRelativePath.
- CredentialStoresTests split out; Display tested without the real HOME;
  no dictionary copy, one mode check per delete, no Lazy in the selector.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): sign in with xping login in the browser, and add logout and auth status (P3) (#247)

* docs(cli): amend the auth spec for P3: stderr decides decoration, config key source, flow test seams

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): note that [::1] loopback binding works on Windows only

The managed HttpListener of macOS and Linux rejects bracketed IPv6
prefixes, so the §4.3 fallback to [::1] can only bind under http.sys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): sign in with xping login in the browser, and add logout and auth status (P3)

xping login runs the loopback PKCE flow (cli-auth-cli-spec §4): fresh
discovery, a one-shot listener on 127.0.0.1, the link printed once, the
browser opened unless the machine is headless, a 5-minute wait, the
code exchange, and the record stored in the file store. It refuses to
run without a terminal or with CI set, and every failure has its exit
code and, under --json, its JSON document.

xping logout revokes the refresh token and removes the sign-in and the
caches for the Cloud URL, locally even when revocation fails (§12).
xping auth status reports the credential in use without a request
(§3.4).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): report every auth failure under --json, scrubbed, with the configured Cloud URL (#247 review)

- One AuthCommandRunner for login, logout and auth status: a bad
  XPING_CLOUDURL or settings file now writes the failure document
  (error "configuration", exit 2), the document names the configured
  Cloud URL, and every failure message is scrubbed on both streams.
- Local listener failures report "local_listener", not "oauth_error".
- logout tells "refused" from "could not reach" when revocation fails.
- An invalid_request without a description reads cleanly.
- Browser launchers run with /dev/null streams through a constant sh
  script, so a browser they start cannot write into --json output or
  die on a closed pipe. The launcher's duplicate headless check is gone.
- Spec §3.2 and §5.1 amended; AGENTS.md names the --json output as the
  exception to the IXpingSerializer rule.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): sign in with xping login --device, and add --no-browser and --workspace (P4) (#249)

* docs(cli): amend the auth spec for P4: --workspace format, --no-browser wording, device flow tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): sign in with xping login --device, and add --no-browser and --workspace (P4)

xping login --device runs the device flow (cli-auth-cli-spec §6): fresh
discovery, the page and code printed, the page with the code filled in
opened when a browser is available, then polling that always waits the
interval first, adds 5 s on slow_down, keeps the interval on network
errors, and ends on approval, denial, expiry or the server's error.

--no-browser prints the loopback link without looking for a browser.
--workspace preselects a workspace on both flows; it must be a ULID,
otherwise it is a parse error before any request.

The record building and the shared failure messages move out of
LoopbackFlow so both flows use them. The fake Cloud's ids are now valid
ULIDs (they contained U, which Crockford base32 excludes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): amend the auth spec for the P4 review: poll errors as §4.8, 429 is transient, deadline-capped wait

- §2.4: a 429 without an OAuth body is transient while polling.
- §6.1: a user_code with a control character is an incomplete response.
- §6.3: the last wait stops at the deadline; the first unreachable poll
  prints one warning; other poll errors map as §4.8 (invalid_client is
  CloudUnreachable), control characters dropped.
- §3.2: the waiting line never rounds a lifetime to "0 minutes".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): report device poll errors with the right exit code, and keep server text out of the terminal (#249 review)

- invalid_client while polling exits 15 with the --cloud-url hint, and
  invalid_request asks for a bug report, as at the start of the flow.
- A user_code with a control character is refused, and server error codes
  lose their control characters in messages and in --json oauthError.
- A 429 while polling waits for the next interval instead of ending the
  login; the first unreachable poll prints one warning on stderr.
- The last wait stops at the deadline, so expiry is reported on time.
- The waiting line shows seconds when the lifetime is not whole minutes.
- One BrowserChoice decides whether either flow opens a browser;
  LoginOptions has no unused defaults.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): keep xping login in the OS keychain, with the file as fallback (P5) (#252)

* docs(cli): amend the auth spec for P5: libsecret v functions, probe-aware read order, keychain reasons

- §7.3: libsecret is bound at run time and called through the
  non-variadic storev/lookupv/clearv functions; IKeychainCredentialStore
  adds MaxSecretBytes and Probe; the service name is a constructor
  argument so tests use xping-cli-tests.
- §7.4: Select(cloudUrl); a keychain whose probe failed is left out of
  that process's read order, so no keyring means exit 10, not 14.
- §7.6: "keychain unavailable" for macOS errors other than locked.
- §17.2, §18.2: PR CI excludes Category=CredentialStore; flow tests run
  without a keychain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): keep xping login in the OS keychain, with the file as fallback (P5)

Sign-ins go to Windows Credential Manager, the macOS Keychain or the
Secret Service when a probe of the real entry succeeds, and to
~/.xping/credentials.json otherwise, with the reason printed at login
and listed by auth status. Reads go keychain first, then file, so a
login made over SSH still works in a desktop session.

- WindowsCredentialStore: CredRead/Write/Delete; records over 2560 bytes
  drop the access token, and fail if they still do not fit.
- MacOsKeychainStore: SecItem* with CoreFoundation dictionaries; writes
  update in place so "Always Allow" survives.
- LibSecretStore: libsecret loaded at run time; missing library or
  session bus is a fallback reason.
- KeychainRecordCodec shares the size and corruption rules.
- cli-credential-stores.yml runs the real-keychain tests weekly on
  Windows, macOS and Ubuntu; ci.yml excludes them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: temporarily run the credential store workflow on the P5 branch

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: name the Windows and macOS credential store step after its stores

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: remove the temporary push trigger from the credential store workflow

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): amend the auth spec for the P5 review: logout reaches an unreachable keychain, one keychain read per command

- §7.4: a keychain whose probe failed stays in CredentialStores.Unreachable
  (unless it cannot hold anything); logout tries it and warns, without
  failing, when it cannot. The probe's read is reused by the first read.
- §7.4: failing to clear the old file entry after a keychain write is a
  warning, not a store failure.
- §17.2: "Weekly", not "Nightly"; the fallback step injects its environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): sign out of a locked keychain over SSH, and read the keychain once per command (P5)

- logout also tries a keychain whose probe failed (locked, no session
  bus), and warns that a desktop sign-in may remain when it cannot reach
  it; it no longer says "not signed in" while one is stored there.
  LibSecretStore fails fast without a session bus instead of letting
  libsecret autolaunch one.
- The macOS and libsecret stores hand the probe's read to the first
  ReadAsync (ProbedSecret), so an upgrade shows one access dialog, not two.
- login no longer exits 14 when only the cleanup of the old file entry
  fails; it warns instead.
- FileCredentialStore decodes entries with the shared CredentialRecordCodec
  (renamed from KeychainRecordCodec).
- The no-bus workflow step drops an env -u that the tests never read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): amend the auth spec: native credential stores are excluded from PR coverage

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cli): measure P5 coverage where it can run, and cover the login and logout warning paths

The Windows, macOS and libsecret stores, and the OS switch that builds
them, are excluded from coverage with the reason: the Ubuntu PR runner
cannot execute them, and cli-credential-stores.yml tests them weekly
against the real stores. New tests cover the two command paths Codecov
reported: login warning when the old file entry cannot be removed, and
logout combining a failed revocation with an unchecked keychain.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): refresh Cloud sign-ins and read the DataGateway with them (P6) (#256)

* docs: amend the CLI auth spec for the phase 6 pipeline (P6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): refresh Cloud sign-ins and read the DataGateway with them (P6)

TokenRefresher (proactive, single-flight, cross-process lock, reuse
handling), BearerTokenHandler and ApiKeyHandler, the xping-cloud client
with resilience, CloudApiClient with the status-then-title mapping, and
FakeCloud gateway routes. Nothing calls it from report until P7.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): keep Cloud sign-ins through cancels, failed writes and stray 401s (P6)

Review fixes for #256:
- A refresh runs to the end once the lock is held, and the new pair is
  stored before it is used, so a cancel or attempt timeout cannot leave
  the old refresh token in the store.
- An older stored record no longer replaces a newer one in memory.
- Only a 401 on a request retried after a refresh ends the sign-in, and
  a sign-in stored meanwhile by another login is kept.
- The lock wait (now 5 s) is shorter than the 10 s attempt it runs in;
  the Windows lock file's ACL is set by path and the handle is closed if
  that fails.
- Retries use HttpRetryStrategyOptions and IsTransient; a project list
  without items is an incomplete answer.
- Spec §9.1, §9.4, §9.7 and §10.3 describe the composed pipeline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(cli): check the lock file's ACL and share mode on Windows (P6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): show Xping Cloud confidence in xping report when signed in (P7) (#258)

* docs: amend the cli-auth and report specs for Cloud-enriched reports (P7)

Answer Q-4 (no branch clause), render the Cloud slots from the cloud
objects so JSON metrics and contrast stay local, fix the category and
evidence wording, and cache name matches only for stored logins.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(sdk): record the pinned project id in local sessions (P7)

The CLI reads Xping.ProjectId to bind a local run to its Cloud project
without reproducing the Cloud's key derivation (cli-auth spec §11.2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat(cli): show Xping Cloud confidence in xping report when signed in (P7)

When a credential resolves, report binds the assembly to its Cloud
project (--project, XPING_PROJECTID, the session's pin, then a name
match cached for 24 h) and reads each reported test from the
DataGateway, four at a time inside a 10 s budget. The header gains
"· cloud", rows gain "confidence 0.62 · moderately reliable",
latest-run rows say "confidence 0.94 over 812 runs" and --id adds
cloud pairs. JSON moves to 1.22 with context.cloud and a cloud object
per finding and latest-run failure.

An invalid sign-in falls back to XPING_APIKEY once; network errors,
5xx and version mismatches never fall back. Every failure leaves the
local report and its exit code unchanged, with one stderr hint (the
expired-sign-in hint is printed even off a terminal). Existing report
tests are isolated from the developer's own credentials.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): say when a Cloud project key names no project, and other review fixes (P7)

- A key from --project, XPING_PROJECTID, appsettings or the session pin
  under which nothing is found is checked with GetProjectAsync; a 404
  is now a hint instead of an "ok" report with no data.
- Only each assembly's newest session decides its pin.
- A cached name match is re-listed only when it came from the cache.
- "· cloud" appears only when a row shows a Cloud score.
- No sign-up pitch on a report that read Cloud data.
- appsettings keys report source "config"; reason is the hint text.
- ASCII joins score and category with "-", not the trailer's "|".
- Kebab case comes from JsonNamingPolicy.KebabCaseLower.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): document xping login, logout and auth status (P8) (#263)

* docs: amend the cli-auth spec to match what P0-P7 shipped

Drops state_mismatch (a wrong state is ignored and the CLI keeps waiting),
fixes the auth status store-failure exit code (17), and records the hint
lines, glyphs and exit-code columns the implementation actually has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): document xping login, logout and auth status

Adds the three commands, global options, credential precedence, the full
exit-code table, the macOS keychain prompt and the WSL and devcontainer
notes, and the Cloud hint lines report can print.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: replace the "landing next" notes now that xping login ships

README, the docs home, the NuGet page, local-first, CI setup and
troubleshooting now describe signing in and Cloud data in the report,
and drop the never-built --source flag from the roadmap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* ci: run the test pipeline only for PRs to main

feat/cli-auth was listed so its phase PRs ran CI; phase 8 is the last one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs(cli): close the jq fence that hid half the CLI reference's anchors

A closing fence followed by text on the same line kept the fence open, so
every heading after the population marker lost its id and in-page links broke.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* docs: correct exit codes, key sources and sign-in storage claims from review

Exit 2 for a bad XPING_CLOUDURL is auth-command only, 130 applies to every
command, the nested Xping__ApiKey/Xping__CloudUrl forms are listed, the
file store is named alongside the keychain, and XPING_MODE is marked as
SDK-only. The feat/cli-auth CI trigger stays until the PR to main.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): store the sign-in in the file when Windows Credential Manager refuses it (#264)

* docs: let a refused keychain write fall back to the file in the cli-auth spec

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): store the sign-in in the file when Windows Credential Manager refuses it

A damaged Windows profile passes the read-only probe and then fails every
CredWrite with error 8, so xping login exited 17 after a successful sign-in.
The sign-in now goes to ~/.xping/credentials.json with a warning, a stale
keychain entry is removed so it cannot shadow it, and Windows errors carry
their system text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>

* ci: stop running the test pipeline for PRs to feat/cli-auth

The integration branch is merging to main (cli-auth-cli-spec §19, phase 8).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant