Skip to content

feat: add session-level profile selection and identity diagnostics#1776

Open
luozhixiong01 wants to merge 25 commits into
mainfrom
feat/session-profile-selection
Open

feat: add session-level profile selection and identity diagnostics#1776
luozhixiong01 wants to merge 25 commits into
mainfrom
feat/session-profile-selection

Conversation

@luozhixiong01

@luozhixiong01 luozhixiong01 commented Jul 7, 2026

Copy link
Copy Markdown
Collaborator

Summary

Session-level profile selection for lark-cli. All identity inputs — the --profile flag, the new LARKSUITE_CLI_PROFILE env var, direct app-credential env (LARKSUITE_CLI_APP_ID/LARKSUITE_CLI_APP_SECRET), and the configured default — now resolve through one explainable path with app-id conflict detection and stable, machine-readable error codes. It also sharpens App/profile observability: whoami --json reports the identity a specific invocation actually resolves to, while config show / profile list report the saved default. This is an incremental change on top of the existing --profile flag, whoami, and profile commands.

Changes

  • Add the LARKSUITE_CLI_PROFILE env var. BootstrapInvocationContext falls back to it when --profile is absent, and records whether the active profile came from the flag or the env.
  • Unify credential selection in CredentialProvider.doResolveAccount: precedence --profile > LARKSUITE_CLI_PROFILE > direct app env / configured default. When a profile and direct app-credential env are both present, compare app_id: matching app uses the profile; a mismatch fails instead of silently picking one. Actively-specified identities never fall back on failure. The resolution result is cached as an explainable IdentitySelection (no secret).
  • Add stable error subtypes with machine-readable fields: profile_not_found, no_active_profile, app_credential_incomplete (config, exit 3), profile_app_credential_conflict (validation, exit 2), and profile_secret_invalid. Secrets never appear in diagnostics or error envelopes.
  • Preserve a malformed-config load error instead of masking it as profile_not_found when an explicit profile is requested (only a genuinely absent config keeps the friendly profile_not_found).
  • whoami --json now surfaces credentialSource, explicit, and directCredentialEnv.
  • BREAKING (JSON output): profile list --json renames the active field to default — it reports the saved default profile, not the identity currently in effect. Use whoami --json for the effective identity.
  • Help text: clarify across whoami, auth status, profile, config show, and profile list that whoami --json reports the effective app/profile for an invocation, auth status --json --verify reports OAuth login/token state, and config show / profile list report the saved config (not current usage).
  • Update the lark-shared skill guidance to route profile/tenant/identity requests by intent (current state → whoami; OAuth/token → auth status; per-command → --profile; same-shell script → LARKSUITE_CLI_PROFILE; saved config → config show/profile list; long-term default → profile use).

Test Plan

  • make unit-test, go vet ./..., gofmt -l . (no output), go mod tidy (no change), and golangci-lint run --new-from-rev=origin/main (0 issues).
  • Unit tests cover the full selection matrix across all identity sources, every error subtype with its exit code and fields, the new whoami diagnostic fields, the malformed-config passthrough, and secret non-leakage (a broken-secret path asserts the raw secret never reaches the error envelope).
  • cmd/config and cmd/profile tests updated for the activedefault field rename and the clarified help.
  • Behavioral checks for whoami output and error envelopes across --profile, LARKSUITE_CLI_PROFILE, direct app env, and configured-default sources.

Related Issues

None.

Summary by CodeRabbit

  • New Features
    • whoami --json now includes credential-selection context (credentialSource) plus whether direct app-credential environment values are present/matched.
    • Profile selection and conflict states are now reflected in structured results and typed error details.
    • profile list --json now uses default (replacing legacy active) to indicate the saved default profile.
  • Bug Fixes
    • Improved safety to prevent credential secret values from appearing in error output.
  • Documentation
    • Expanded auth status, profile, whoami, and config show help with clearer guidance; updated shared skill “profile-selection” scenarios and examples.

Add LARKSUITE_CLI_PROFILE env var and make BootstrapInvocationContext
fall back to it when --profile is empty, so downstream credential
resolution sees the correct profile. Also track whether the resolved
profile came from the flag or the env fallback via a new
InvocationContext.ProfileFromFlag field, needed by a later task to
report the correct credential source.
Declares the 5 stable error subtypes (4 config + 1 validation) and the
ConfigError/ValidationError extension fields the profile-selection
credential core (Task 4) will produce, plus builder-chain and wire-pin
tests pinning their shape.
…env provider

Mirror the env-incomplete block-path guard on the success-account path so a
non-env extension provider (e.g. sidecar, Priority 0) that returns an account
wins outright instead of being misreported as a direct-credential env account.
This restores pre-diff behavior for such providers: no profile arbitration, no
spurious profile_app_credential_conflict, and DirectCredentialEnv.Present stays
false when no direct env vars are set. Env matrix states are unchanged.

Add TestSelection_NonEnvExtensionProviderWinsOverProfile as a regression guard.
…cause

Add a case where the underlying account-resolution error itself contains a
secret marker, proving doResolveAccount's drop-the-cause design (§5.1) holds
beyond the existing noop-keychain (empty-error) test, including across the
full errors.Unwrap chain.
whoami reports facts about the effective identity; it should not
proactively push profile-switching guidance at agents. That guidance
lives in `profile --help` / the lark-shared skill, and failure recovery
already lives in error hints. Remove the now-unused Suggestion field
from IdentitySelection and its only setter/consumer.
…ofile_secret_invalid for broken default secret
@luozhixiong01 luozhixiong01 added size/M Single-domain feat or fix with limited business impact feature labels Jul 7, 2026
@github-actions github-actions Bot added size/L Large or sensitive change across domains or core paths and removed size/M Single-domain feat or fix with limited business impact labels Jul 7, 2026
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR adds profile-aware credential selection: bootstrap resolves the active profile from a flag or LARKSUITE_CLI_PROFILE, credential resolution records selection provenance and typed error metadata, whoami exposes that selection in JSON, and related help text/docs are updated.

Changes

Profile-aware credential selection

Layer / File(s) Summary
Profile resolution and wiring
cmd/bootstrap.go, cmd/bootstrap_test.go, internal/cmdutil/factory.go, internal/cmdutil/factory_default.go
BootstrapInvocationContext now resolves Profile from --profile or LARKSUITE_CLI_PROFILE, exposes ProfileFromFlag, and passes that flag into credential provider construction.
Selection source types
internal/credential/identity_selection.go, internal/credential/identity_selection_test.go, internal/envvars/envvars.go
Adds credential source kind constants, DirectCredentialEnv and IdentitySelection structs, Explicit() logic, and the CliProfile env var constant.
Credential arbitration and selection tests
internal/credential/credential_provider.go, internal/credential/credential_provider_selection_test.go
CredentialProvider now caches selection state and arbitrates between extension providers, direct env credentials, and explicit profiles while emitting typed config/validation errors; the selection test suite covers the full decision matrix and secret-leak checks.
Typed selection errors
errs/subtypes.go, errs/types.go, errs/types_test.go, errs/marshal_test.go
Adds config/validation subtypes and new typed-error fields/builders for profile selection conflicts and credential metadata, with JSON marshal and unit coverage.
Whoami credential-source output
cmd/whoami/whoami.go, cmd/whoami/whoami_test.go
whoami reads cached selection state, emits credentialSource, explicit, and directCredentialEnv in JSON, and adds regression coverage for the new fields.
Command help and shared guidance
cmd/auth/status.go, cmd/auth/status_test.go, cmd/profile/profile.go, cmd/profile/list.go, cmd/profile/profile_test.go, cmd/config/show.go, cmd/config/config_test.go, skills/lark-shared/SKILL.md
Updates auth/profile/config help text and tests, plus the shared skill doc’s profile-selection section.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: size/L, feature

Suggested reviewers: liangshuo-1, MaxHuang22

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the main change: session-level profile selection plus identity diagnostics.
Description check ✅ Passed The description follows the required template and covers summary, changes, test plan, and related issues.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/session-profile-selection

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Jul 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.01990% with 8 lines in your changes missing coverage. Please review.
✅ Project coverage is 74.53%. Comparing base (b76dc18) to head (94032da).
⚠️ Report is 10 commits behind head on main.

Files with missing lines Patch % Lines
internal/credential/credential_provider.go 93.89% 6 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1776      +/-   ##
==========================================
+ Coverage   74.43%   74.53%   +0.09%     
==========================================
  Files         854      861       +7     
  Lines       88490    89390     +900     
==========================================
+ Hits        65867    66624     +757     
- Misses      17554    17631      +77     
- Partials     5069     5135      +66     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

PR Quality Summary

CI did not complete successfully. Use the failed check links below to decide whether this PR needs a code change or a rerun.

Failed checks

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown

🚀 PR Preview Install Guide

🧰 CLI update

npm i -g https://pkg.pr.new/larksuite/cli/@larksuite/cli@94032da8623d02b76580a767cf24fa4bd17a03e5

🧩 Skill update

npx skills add larksuite/cli#feat/session-profile-selection -y -g

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
cmd/whoami/whoami_test.go (1)

338-374: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Use cmdutil.TestFactory here. profileSelectionFactory can take the shared factory setup, then override f.Credential with the custom profile-aware provider. That keeps the test aligned with the rest of the suite and avoids duplicating boilerplate in this helper.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@cmd/whoami/whoami_test.go` around lines 338 - 374, `profileSelectionFactory`
is hand-building a `cmdutil.Factory` instead of using the shared test helper.
Switch this helper to start from `cmdutil.TestFactory` and then override the
returned factory’s `Credential` with the custom
`credential.NewCredentialProvider`/`WithProfile` setup, while keeping the
existing config and IO stream tweaks. This keeps the test consistent with the
rest of the suite and removes duplicated factory boilerplate.

Source: Path instructions

internal/credential/credential_provider.go (1)

233-262: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate enrichment/warning logic — extract a helper.

The account-enrichment + warn-and-clear-on-failure block here (Lines 245-254) is duplicated almost verbatim at Lines 320-329 for the env-direct path. Consider extracting a shared helper (e.g. p.enrichAndClearOnFailure(ctx, acct, source)) to avoid future divergence.

♻️ Proposed refactor
+func (p *CredentialProvider) enrichOrClear(ctx context.Context, acct *Account, source credentialSource) {
+	if err := p.enrichUserInfo(ctx, acct, source); err != nil {
+		if p.warnOut != nil {
+			_, _ = fmt.Fprintf(p.warnOut, "warning: unable to verify user identity from credential source %q: %v\n", source.Name(), err)
+		}
+		acct.UserOpenId = ""
+		acct.UserName = ""
+	}
+}

Then replace both call sites (Lines 245-254 and 320-329) with p.enrichOrClear(ctx, internal, source) / p.enrichOrClear(ctx, envAcct, envSource).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/credential/credential_provider.go` around lines 233 - 262, The
account enrichment and warning/clear-on-failure logic in credential_provider.go
is duplicated between the non-env provider path and the env-direct path. Extract
that shared behavior from the credential selection flow in CredentialProvider
into a helper (for example, on p as enrichOrClear or enrichAndClearOnFailure)
that takes ctx, the converted account, and the source, calls enrichUserInfo,
logs the warning through warnOut, and clears UserOpenId/UserName on failure.
Then replace both existing call sites in the provider-selection logic with the
helper so the two paths stay consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@cmd/bootstrap.go`:
- Around line 31-39: The profile bootstrap logic is using globals.Profile != ""
to detect whether --profile was provided, which incorrectly treats an explicit
empty value the same as no flag. Update the bootstrap path in the command setup
to use fs.Changed("profile") to determine whether the user passed the flag, and
only fall back to envvars.CliProfile when the flag was not set. Keep the
InvocationContext construction in sync so ProfileFromFlag reflects the explicit
flag state rather than the resolved value.

In `@internal/credential/credential_provider.go`:
- Around line 264-277: The explicit-profile branch in credential_provider.go is
swallowing the error from core.LoadMultiAppConfig and always turning it into
profile_not_found, which masks real config-loading failures. Update the logic
around p.profile, LoadMultiAppConfig, and the app == nil check so config load
errors are preserved and propagated instead of reported as a missing profile;
only return SubtypeProfileNotFound when the config loads successfully but
FindApp(p.profile) returns nil. Follow the pattern used in the later no-profile
branch by inspecting errs.ProblemOf(loadErr) when applicable and attaching the
original error with .WithCause(loadErr) so errors.Is and errors.Unwrap keep
working.

---

Nitpick comments:
In `@cmd/whoami/whoami_test.go`:
- Around line 338-374: `profileSelectionFactory` is hand-building a
`cmdutil.Factory` instead of using the shared test helper. Switch this helper to
start from `cmdutil.TestFactory` and then override the returned factory’s
`Credential` with the custom `credential.NewCredentialProvider`/`WithProfile`
setup, while keeping the existing config and IO stream tweaks. This keeps the
test consistent with the rest of the suite and removes duplicated factory
boilerplate.

In `@internal/credential/credential_provider.go`:
- Around line 233-262: The account enrichment and warning/clear-on-failure logic
in credential_provider.go is duplicated between the non-env provider path and
the env-direct path. Extract that shared behavior from the credential selection
flow in CredentialProvider into a helper (for example, on p as enrichOrClear or
enrichAndClearOnFailure) that takes ctx, the converted account, and the source,
calls enrichUserInfo, logs the warning through warnOut, and clears
UserOpenId/UserName on failure. Then replace both existing call sites in the
provider-selection logic with the helper so the two paths stay consistent.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e5965ff7-ac70-4f0e-ae37-05d2790a35c5

📥 Commits

Reviewing files that changed from the base of the PR and between f0b6f35 and 251de0f.

📒 Files selected for processing (20)
  • cmd/auth/status.go
  • cmd/auth/status_test.go
  • cmd/bootstrap.go
  • cmd/bootstrap_test.go
  • cmd/profile/profile.go
  • cmd/profile/profile_test.go
  • cmd/whoami/whoami.go
  • cmd/whoami/whoami_test.go
  • errs/marshal_test.go
  • errs/subtypes.go
  • errs/types.go
  • errs/types_test.go
  • internal/cmdutil/factory.go
  • internal/cmdutil/factory_default.go
  • internal/credential/credential_provider.go
  • internal/credential/credential_provider_selection_test.go
  • internal/credential/identity_selection.go
  • internal/credential/identity_selection_test.go
  • internal/envvars/envvars.go
  • skills/lark-shared/SKILL.md

Comment thread cmd/bootstrap.go
Comment thread internal/credential/credential_provider.go
Replace credential-shaped literals in whoami and selection tests with
placeholder values recognized by the public-content quality gate
(test-secret / your-secret / your-password / your-access-token), so the
deterministic public-content scan does not flag test fixtures as generic
credentials. No behavioral change; the fixtures are only compared for
non-leakage and identity arbitration.
Clarify that --profile and LARKSUITE_CLI_PROFILE accept either a profile
name or an app_id, keep the effective-identity vs OAuth-token boundary
(whoami vs auth status --json --verify), and note not to set direct
app-credential env vars unless direct credentials are provided.
When an explicit profile was requested and LoadMultiAppConfig failed, the
error was discarded and every failure reported as profile_not_found,
masking a real config problem (e.g. malformed file) behind a misleading
"run profile list" hint. Propagate the underlying error when it is a
malformed-config failure (errors.Is ErrMalformedConfig) so errors.Is /
errors.Unwrap keep working, mirroring the no-profile branch. An absent
config is not malformed and still yields the friendly profile_not_found.
profile list / config show only report the saved default profile, not the
app/profile a specific invocation actually resolves to (especially under
--profile or LARKSUITE_CLI_PROFILE). Rename the misleading profile list
JSON field active -> default (it is the configured default, not the one in
effect), and point config show / profile list / profile help at
lark-cli whoami --json for the identity actually used now. Restructure the
lark-shared skill profile guidance as an intent -> command table.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
skills/lark-shared/SKILL.md (1)

129-132: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Language inconsistency in new "Profile 选择" section.

The section header is Chinese but the content on Line 131 is entirely English, unlike every other section in this document (auth table, update-check, security rules) which is written in Chinese. Consider translating this content for consistency with the rest of the skill guidance.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@skills/lark-shared/SKILL.md` around lines 129 - 132, The new “Profile 选择”
section is inconsistent because its guidance text is written in English while
the surrounding skill documentation uses Chinese. Update the content in this
section to Chinese for consistency, keeping the same meaning for profile
selection, CLI flags, environment variables, saved config, default profile
handling, and the ambiguity/credential note; use the existing “Profile 选择”
heading and nearby sections as the style reference.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@skills/lark-shared/SKILL.md`:
- Around line 129-132: The new “Profile 选择” section is inconsistent because its
guidance text is written in English while the surrounding skill documentation
uses Chinese. Update the content in this section to Chinese for consistency,
keeping the same meaning for profile selection, CLI flags, environment
variables, saved config, default profile handling, and the ambiguity/credential
note; use the existing “Profile 选择” heading and nearby sections as the style
reference.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4a654e00-7609-4bc0-9a4f-ac3211765632

📥 Commits

Reviewing files that changed from the base of the PR and between 425122e and d44c70f.

📒 Files selected for processing (6)
  • cmd/config/config_test.go
  • cmd/config/show.go
  • cmd/profile/list.go
  • cmd/profile/profile.go
  • cmd/profile/profile_test.go
  • skills/lark-shared/SKILL.md
✅ Files skipped from review due to trivial changes (1)
  • cmd/config/show.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • cmd/profile/profile.go

Match the rest of the skill document, which is in Chinese. Keep command
and env-var tokens in English. Content unchanged.
@luozhixiong01

Copy link
Copy Markdown
Collaborator Author

Language inconsistency in "Profile 选择" section

Addressed in 94032da — the ## Profile 选择 section is now written in Chinese to match the rest of this skill document (command and env-var tokens kept in English). Thanks for the catch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature size/L Large or sensitive change across domains or core paths

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant