refactor(cli): auth/workspace cleanup — record-backed token store#37219
Open
GareArc wants to merge 4 commits into
Open
refactor(cli): auth/workspace cleanup — record-backed token store#37219GareArc wants to merge 4 commits into
GareArc wants to merge 4 commits into
Conversation
d1c293a to
390aaef
Compare
WTA-479. Consolidates the difyctl auth and workspace layer.
Token store:
- Add TokenStore { read/write/remove(host,email) } with two backends:
FileTokenStore (versioned nested YAML, never dot-splits host/email) and
KeychainTokenStore (one keyring Entry per host+email, entry name byte-
identical to legacy for back-compat). Route all credential consumers
through it; delete tokenKey. File-mode users re-login once; keychain
users are unaffected.
- Split backend selection: detectTokenStore probes the keyring at login
(where a write happens anyway) and persists the chosen mode to the
registry; getTokenStore(mode) constructs the recorded backend on read
paths without probing, so reads no longer mutate the keyring. Dispatch
through a single TOKEN_STORE_OPENERS table.
- Guard the login probe cleanup in a finally so a failed probe read never
orphans the probe entry in the keyring.
- Distinguish keyring-unavailable from credential-absent: KeychainTokenStore
.read throws KeyringUnavailable on backend access failure instead of
returning '', so a transient keyring outage surfaces as 'keychain
unreachable' (exit 1) rather than a false 'not logged in' (exit 4).
- Single StorageMode source of truth (STORAGE_MODES) shared by the type
and the zod schema.
Workspace:
- Rewrite 'use workspace' as an interactive live picker (arg -> direct
switch; no-arg+TTY -> list+select; no-arg+no-TTY -> error), persisting
only the active workspace from the switch response.
- Drop the cached available_workspaces field, its readers, and the
implicit-default-workspace fallback. Resolver is now
flag -> env -> ctx.workspace.id -> throw.
- logout no longer blocked when OS keychain is locked: read() throw is caught in both logout/index.ts and logout.ts; local credential cleanup (hosts.yml) always runs regardless of keychain state - FileTokenStore transparently reads pre-upgrade tokens.yml (no version header = legacy format); next write stamps version: 1 - KeychainTokenStore.write() now wraps native keyring errors as KeyringUnavailable, matching the error surface of read() - tests for all three: logout-with-locked-keychain, legacy-file migration, write-error wrapping
…ace validation Rebased onto main which added UUID validation for --workspace flag and DIFY_WORKSPACE_ID env values (#37212). Short fixture IDs ('ws-2', 'ws-9') now fail that validation. Replace them with zero-padded UUIDs (00000000-0000-0000-0000-000000000002 / ...0009) throughout unit tests and the dify-mock fixture scenarios so all 996 tests pass. Also forward-ports the DIFY_E2E_NO_KEYRING bypass from main into detectTokenStore (replacing the old getTokenStore probe entrypoint).
…S strict fix - resolver.ts: validate active-context workspace ID as UUID (same guard already applied to flag/env paths); throws UsageInvalidFlag with hint to run `difyctl use workspace` when stored ID is stale/non-UUID - manager.ts: initialise `got` to '' so strict TS assignment analysis does not flag it as potentially-unassigned after a keyring read throw - use/workspace/use.ts: change error code for "no workspaces available" from UsageMissingArg (exit 2 / usage) to AccessDenied (exit 4); it is a server-state/permissions issue, not a missing argument - test fixtures: update ws-1 → aaaaaaaa-0000-0000-0000-000000000001 across dify-mock scenarios, server, and all command tests so fixture data uses valid UUIDs consistent with the resolver's new validation
4c262eb to
e8b0850
Compare
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #37219 +/- ##
=======================================
Coverage 86.12% 86.13%
=======================================
Files 4808 4809 +1
Lines 236734 236800 +66
Branches 43668 43682 +14
=======================================
+ Hits 203897 203973 +76
+ Misses 28934 28924 -10
Partials 3903 3903
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WTA-479. Consolidates the difyctl auth and workspace layer.
Token store
TokenStore { read/write/remove(host, email) }with two backends:FileTokenStore— versioned nested YAML; host/email are opaque keys, never dot-split. Reads legacy pre-v1 files (no version header) transparently; next write stampsversion: 1.KeychainTokenStore— one keyringEntryper (host, email); entry name byte-identical to the legacy key for back-compat.tokenKeyremoved.detectTokenStoreprobes the keyring at login (where a write happens anyway) and persists the chosen mode to the registry;getTokenStore(mode)constructs the recorded backend on read paths without probing, so reads no longer mutate the keyring. Dispatch flows through a singleTOKEN_STORE_OPENERStable.finallyso a failed probe read never orphans the probe entry in the keyring.KeychainTokenStore.readand.writedistinguish keyring-unavailable from credential-absent: a backend access failure throwsKeyringUnavailable(exit 1, "keychain unreachable") instead of returning''and surfacing as a false "not logged in" (exit 4).read()failure is caught in both call sites; local credential cleanup (hosts.yml) always runs.StorageModesource of truth (STORAGE_MODES) shared by the type and the zod schema.Workspace
use workspaceas an interactive picker: arg → direct switch; no-arg + TTY → list + select; no-arg + no-TTY → error. Persists only the active workspace from the switch response.available_workspacesfield, its readers, and the implicit-default-workspace fallback. Resolver is nowflag → env → ctx.workspace.id → throw.Testing
pnpm type-check,pnpm lint,pnpm testall green (985 passed / 16 skipped).