Profile-based auth & config (config group, Dataverse provider, MCP contract)#12
Merged
Profile-based auth & config (config group, Dataverse provider, MCP contract)#12
Conversation
Milestone 1 (config-core) of the profile/connection/credential plan. New project src/TALXIS.CLI.Config: - Model: Profile, Connection, Credential, SecretRef (vault:// URI), ProviderKind, CredentialKind, CloudInstance, GlobalConfig, WorkspaceConfig. - Abstractions: IConfigurationResolver, IProfileStore, IConnectionStore, ICredentialStore, IGlobalConfigStore, IConnectionProvider, ICredentialVault, IWorkspaceDiscovery, IHeadlessDetector. - Storage: file-backed JSON stores with atomic write (temp + File.Replace), camelCase properties, kebab-case string enums, ignore-null on write, forward-compat [JsonExtensionData] on Connection. - Resolution: 5-layer ConfigurationResolver (flag > TXC_PROFILE > workspace file > global active > throws), cwd-walk WorkspaceDiscovery, IEnvironmentReader for test injection. - Headless: HeadlessDetector (TXC_NON_INTERACTIVE, CI/GITHUB_ACTIONS/TF_BUILD, stdin+stdout both redirected). - DI: AddTxcConfigCore() and static TxcServices service locator (DotMake CliCommand instances cannot take constructor dependencies). Also: - Delete unreachable src/TALXIS.CLI.Workspace/ConfigCliCommand.cs stub; the replacement lives under 'txc config profile validate' (future milestone). - Add Config test suite (42 tests): SecretRef parse/format, ProfileStore / ConnectionStore / CredentialStore / GlobalConfigStore roundtrips, WorkspaceDiscovery upward walk, PrecedenceTests (all 5 layers plus missing- reference errors), HeadlessDetectorTests. Build: clean. Tests: 199/199 pass. Vault registration (ICredentialVault) and MSAL cache wiring are deferred to milestone 2 (config-vault). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ret)
Implements milestone 2 (config-vault): an ICredentialVault backed by
Microsoft.Identity.Client.Extensions.Msal, the same library pac CLI
uses. Secrets are stored as a single JSON dictionary keyed by
"{credentialId}::{slot}" per cache file, encrypted by the OS
(DPAPI / Keychain / libsecret).
- Add NuGet refs: Microsoft.Identity.Client 4.83.3 and
Microsoft.Identity.Client.Extensions.Msal 4.74.0.
- Vault/VaultOptions: locked filenames (txc.secrets.v1.dat plus reserved
txc.msal.tokens.v1.dat), Keychain service "com.talxis.txc", Linux
keyring schema/label/attrs. Honors TXC_PLAINTEXT_FALLBACK on Linux
and TXC_TOKEN_CACHE_MODE=file on macOS as explicit opt-ins.
- Vault/MsalCacheHelperFactory: builds StorageCreationProperties,
calls VerifyPersistence, fail-fast on Win/mac, plaintext-fallback
path on Linux via WithUnprotectedFile plus chmod 600 and a warning.
- Vault/MsalBackedCredentialVault: Get/Set/Delete over the JSON blob
with a single SemaphoreSlim; CrossPlatLock in MsalCacheHelper handles
cross-process. Registered as a DI singleton so the helper is built
once per process (minimizes macOS Keychain prompts).
- Vault/VaultUnavailableException: canonical remedy message.
- 21 new tests against the plaintext path (doesn't touch host
Keychain/DPAPI/libsecret); full suite 220/220 green.
- Manual smoke on macOS: round-trip Set/Get/Delete succeeded; Keychain
entry (svce=com.talxis.txc, acct=secrets) verified via
`security find-generic-password`.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…elper Completes milestone 3 (config-headless): standardized fail-fast error for interactive-only credential kinds attempted in headless / CI contexts. Detector itself was already present from milestone 1. - Headless/HeadlessAuthRequiredException: carries the attempted CredentialKind + headless reason. Deterministic message lists all permitted headless kinds (client-secret, client-certificate, managed-identity, workload-identity-federation, azure-cli, pat) and the exact env vars / profile command needed to re-run non-interactively. - Abstractions/IHeadlessDetector: adds EnsureKindAllowed extension that throws HeadlessAuthRequiredException when headless + kind is interactive-browser or device-code. - 7 new tests; full suite 227/227 green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…SAL factory) Completes milestone 4 (provider-dataverse). New project 'TALXIS.CLI.Config.Providers.Dataverse' wires the Dataverse IConnectionProvider with all pac 2.6.3 parity constants pinned in one place. - Authority/DataverseCloudMap: CloudInstance -> authority host (Public/Gcc, GccHigh/Dod, China) + host-suffix inference from EnvironmentUrl. Mirrors bolt.authentication.AuthorityInfo. - Authority/AuthorityChallengeResolver: unauthenticated WhoAmI probe, parses authorization_uri from WWW-Authenticate Bearer challenge (pac IAuthorityResolver behaviour). - Scopes/DataverseScope: builds the mandatory '//.default' double-slash scope for Dataverse audiences. - Msal/DataverseMsalClientFactory: pinned public client id 9cee029c-6210-4654-90bb-17e6e9d36617 + http://localhost redirect; confidential builder supports secret, certificate (sendX5C:true), and client-assertion callback for GitHub OIDC / ADO federation. validateAuthority:false throughout. - Msal/DataverseTokenCacheBinder: singleton MsalCacheHelper around the reserved txc.msal.tokens.v1.dat file (separate from generic secret vault by design — see keychain-prompt-research.md). - DataverseConnectionProvider: IConnectionProvider impl with structural ValidateAsync (url + kind + authority buildability). Token acquisition and WhoAmI land when auth commands come online. - DependencyInjection/AddTxcDataverseProvider(). InternalsVisibleTo to Providers.Dataverse on TALXIS.CLI.Config so the shared MsalCacheHelperFactory can be reused. 35 new tests; full suite 262/262 green. Legacy DataverseAuthTokenProvider retained for now (retired in refactor-dataverse-commands, milestone 12). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add FederatedAssertionCallbacks with three WIF flavours for Dataverse
confidential clients:
* ADO pipelines: reads TXC_ADO_ID_TOKEN_REQUEST_URL/TOKEN
(pac-compatible PAC_ADO_* env vars also honored, matching
microsoft/powerplatform-build-tools#884 and #906). POSTs with
System.AccessToken bearer and parses oidcToken from the response.
* GitHub Actions: reads ACTIONS_ID_TOKEN_REQUEST_URL/TOKEN, appends
audience=api://AzureADTokenExchange query param.
* Workload-identity file: AZURE_FEDERATED_TOKEN_FILE content.
AutoSelect() picks the first configured source in that priority. All
return raw JWTs that feed MSAL WithClientAssertion.
6 unit tests (HttpMessageHandler stub, tmp file for file-based),
268/268 green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Create TALXIS.CLI.Config.Commands project with the 'config' and
'config auth' command groups plus three no-MSAL verbs:
* auth list - JSON dump of all credentials (no secrets)
* auth show <alias> - single credential as JSON; exit 2 if missing
* auth delete <alias> - removes entry + vault secret; leaves
dependent profiles orphaned with a warning (pac-auth-clear parity)
The command tree is defined but not yet wired into TxcCliCommand.Children
(deferred to the wire-toplevel milestone per CONTRIBUTING's 'invisible
scaffolding' convention). Commands are tested directly by constructing
the handler classes and driving TxcServices with an in-memory fake
vault.
Login / add-service-principal / add-federated verbs land in follow-up
commits.
6 tests; 274/274 green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Interactive browser sign-in via MSAL (Dataverse provider). Persists an InteractiveBrowser credential whose refresh token lives in the shared MSAL cache; no secret enters the txc credential vault. - IInteractiveLoginService abstraction + DataverseInteractiveLoginService (scopes: openid profile offline_access, UserTokenCache bound via DataverseTokenCacheBinder so silent acquires work later). - AuthLoginCliCommand with --tenant / --alias / --cloud. Fails fast in headless contexts via IHeadlessDetector.EnsureKindAllowed. - Default alias = lowercased UPN, collision-resolved via tenant short name then numeric suffix. - Tests cover happy path, explicit overrides, collision resolution, and headless refusal. CommandTestHost gains fakes for IHeadlessDetector and IInteractiveLoginService. - TxcServicesSerial collection serialises command tests that touch the process-global TxcServices. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Register a client-secret service principal. The secret value is read from --secret-from-env, then piped stdin, then an interactive masked TTY prompt — and only a SecretRef handle is persisted in the credential store (secret itself goes to the OS credential vault). - AuthAddServicePrincipalCliCommand with --alias, --tenant, --application-id (required) plus --cloud, --description, --secret-from-env (optional). Aliased as 'add-sp'. - ReadSecret helper with a StdinOverride test seam. - Tests cover env-var happy path, missing env var, piped stdin, and headless-allowed behaviour (ClientSecret is a permitted headless kind). - Wired into AuthCliCommand.Children. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Dataverse-only in v1: --provider rejects non-dataverse with exit 1. create validates http(s) URL, trims trailing slash, validates GUID org id. delete fails exit 3 when referenced by a profile; --force-orphan-profiles opts into orphan-with-warning (pac-auth-clear parity). show/delete exit 2 when missing. 13 new tests; 302/302 green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…elete Refs-only create (--auth + --connection reference existing primitives; inline-create shortcuts deferred per scope). First profile is auto-promoted to the global active pointer; subsequent creates do not touch it. show without <name> reports the active profile and inline expands connection+credential so scripts don't need three round-trips. delete clears the active pointer when the deleted profile was active; --cascade removes auth+connection only if no other profile uses them (vault secret purged best-effort). 20 new tests; 322/322 green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
pin writes <cwd>/.txc/workspace.json with {defaultProfile}. With no
<name> it pins the global active profile; with <name> it pins the
specified profile (must exist). The workspace pin sits below --profile
and TXC_PROFILE in the precedence chain but beats the global active
pointer.
unpin removes the workspace file and cleans up an empty .txc
directory. Idempotent when no pin exists.
8 new tests exercise cwd isolation, idempotency, sibling-file
preservation, and exit-2 branches. 330/330 green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds ValidationMode (Structural | Live) to IConnectionProvider so the profile validate command can explicitly request a live authenticated round-trip. Default behavior: live check. Pass --skip-live for structural-only (fast, offline-safe). Dataverse provider now delegates live-mode to an injectable IDataverseLiveChecker. Default registration is a placeholder that fails with a precise remedy message until the real WhoAmI HTTP path lands in milestone refactor-dataverse-commands. Tests inject a FakeConnectionProvider via a new CommandTestHost seam, asserting exit 0/1/2 across missing profile, missing connection, provider throw, named-override, structural+live modes, and JSON shape. TxcServices gains GetAll<T>() for provider lookup by kind. 8 new tests; 338/338 green. Completes milestone 8 (cmd-profile): create/list/show/update/select/pin/unpin/validate/delete. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implements milestone 9 (cmd-setting): narrow whitelist-driven verbs over
${TXC_CONFIG_DIR}/config.json for tool-wide preferences.
- SettingRegistry: one descriptor per whitelisted key (log.level,
log.format, telemetry.enabled) with per-key value validation.
- set: unknown keys and invalid values exit 2 with remedy.
- get: prints current value (default if unset); exit 2 for unknown key.
- list: JSON dump of all known keys with current values + allowed set.
- ConfigCliCommand now exposes the setting group.
Tests: 18 new (356 total).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Updates CONTRIBUTING.md for milestone 10 (contributing-md): - Top-level groups go from four to five; config joins workspace, environment, data, docs as a first-class group. - New 'config sub-nouns' section documents the auth/connection/profile/ setting split and why the separation is deliberate. - Alias table adds config=c and config profile=p; documents the --profile=-p short-alias exception (the only flag-level short in the CLI, justified by the frequency of profile-switching). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Implements milestone 11 (wire-toplevel): - TxcCliCommand.Children now includes ConfigCliCommand so 'txc config' is invocable and MCP tools/list auto-discovers config_auth_*, config_connection_*, config_profile_*, config_setting_* tools. - ConfigCliCommand grows alias 'c' (per CONTRIBUTING taxonomy). - Program.Main bootstraps TxcServices with AddTxcConfigCore() and AddTxcDataverseProvider() before DotMake dispatch; the PackageDeployer subprocess branch keeps its own init path. - TALXIS.CLI.csproj picks up ProjectReferences for Config, Config.Commands, Config.Providers.Dataverse. Also fixes a latent bug that made every config leaf command unreachable via the CLI: DotMake v2.6.7 does not auto-bind CancellationToken parameters on handler methods, so 'public async Task<int> RunAsync(CancellationToken ct = default)' was generating a handler that couldn't bind and DotMake silently fell back to showing help. All 21 config commands now use 'RunAsync()' and pass CancellationToken.None downstream. Tests (which call RunAsync() directly) unaffected: 356/356 green. End-to-end smoke (verified): txc config setting set log.level debug -> exit 0, value written txc config setting get log.level -> prints 'debug' txc c p list -> prints '[]' txc c setting set bogus x -> exit 2, structured error Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- ProfiledCliCommand: abstract CliCommand base with --profile (-p) + --verbose - IDataverseAccessTokenService / DataverseAccessTokenService: MSAL-driven token acquisition for InteractiveBrowser, ClientSecret, ClientCertificate, and WorkloadIdentityFederation credential kinds - IDataverseConnectionFactory / DataverseConnectionFactory: build a ServiceClient-backed DataverseConnection from a ResolvedProfileContext - DataverseLiveChecker: real WhoAmI-based implementation that replaces the NotYetImplementedDataverseLiveChecker stub - DataverseConnection.FromServiceClient public factory on TALXIS.CLI.Dataverse - DI wiring: Config.Providers.Dataverse registers the new services; the stub live checker is removed Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…erviceClientFactory surface - Move DataverseCommandBridge from Environment/Platforms/Dataverse to Config.Providers.Dataverse/Runtime so Data project can reuse it without circular refs. Public static, exposes ConnectAsync/ResolveAsync/ BuildConnectionStringAsync (SP-only, pending package-deployer-subprocess milestone). - Refactor 8 Dataverse-touching leaf commands to ProfiledCliCommand base: Solution List/Import/Uninstall, Deployment List/Show, Package Import/ Uninstall, Data Package Import. Removes --connection-string, --environment, --device-code, --verbose-plumbed-as-auth from each. - Package Import + Data Package Import synthesize SP connection strings via BuildConnectionStringAsync; other credential kinds throw NotSupportedException with pointer to package-deployer-subprocess. - Delete ServiceClientFactory legacy surface (Connect, ResolveConnectionString, ResolveEnvironmentUrl, Create) + four DATAVERSE_*/TXC_DATAVERSE_* env var constants. ServiceClientFactory.cs now holds only the DataverseConnection wrapper + FromServiceClient factory used by IDataverseConnectionFactory. All 356 unit + 24 integration tests remain green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…subprocess
Milestone 13 (package-deployer-subprocess). The coordinator JSON previously
carried a fully-materialised Dataverse connection string (including
ClientSecret) in plaintext on disk at
/tmp/txc/package-deployer-process/<guid>/request.json. Replaced with an
identity-neutral transport shape:
PackageDeployerRequest {
PackagePath, ProfileId, ConfigDirectory?, Settings?, LogFile?,
LogConsole, Verbose, TemporaryArtifactsDirectory?, ParentProcessId,
NuGetPackageName?, NuGetPackageVersion?
}
The child:
1. Sets TXC_CONFIG_DIR from request.ConfigDirectory so vault + profile
files resolve identically to the parent.
2. Calls TxcServicesBootstrap.EnsureInitialized() — new shared helper
extracted from Program.InitializeConfigServices so both the main CLI
pipeline and the subprocess wire the same DI graph.
3. Re-resolves the SP connection string in-memory via
DataverseCommandBridge.BuildConnectionStringAsync(profileId, ct).
4. Hands it to PackageDeployerRunner.RunAsync(request, connectionString, ct)
— new signature; the runner no longer reads ConnectionString /
EnvironmentUrl / DeviceCode off the request.
Parent-side (PackageImportCliCommand) now just forwards the --profile
value as ProfileId; no in-process SP resolution. Additionally, the
request.json file is chmod 0600'd on Unix; Windows relies on per-user
%TEMP% ACLs.
All 356 unit + 24 integration tests remain green.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Milestone 14 (cmt-import-symmetry). CmtImportRequest drops ConnectionString / EnvironmentUrl / DeviceCode. CmtImportRunner.RunAsync now takes (request, connectionString, ct) mirroring PackageDeployerRunner.RunAsync. Parallel-import clone loop uses the same in-memory connectionString parameter instead of re-reading it off the request. Dead DataverseInteractiveAuthHook plumbing removed. DataPackageImportCliCommand still resolves the SP connection string via DataverseCommandBridge.BuildConnectionStringAsync (in-process — no subprocess to bridge) and forwards it to the runner. 356 unit + 24 integration tests remain green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…er sink Milestone 15 (redaction-hardening). Patterns added: - Bearer <token> (case-insensitive) - Authorization: <value> (full line value) - Bare JWTs (header.payload.signature) - Extended connection-string secret keys: ApplicationSecret, ApplicationPassword, AccessToken, RefreshToken, IdToken, SasToken, ApiKey, Api_Key - Extended query-param secret keys: access_token, refresh_token, id_token JsonStderrLogger now runs both the composed exception message and string values in the structured-data dictionary through Redact before emitting the JSON line to stderr. This is the belt-and-braces layer that catches accidental 'logger.LogError(ex, ...)' leaks where the Dataverse / Microsoft.Identity SDKs put a connection string or token into the exception Message or StackTrace. Added 6 new LogRedactionFilter tests covering the new patterns. Total test count: 356 -> 362, all green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Milestone 16 (tests). Exercises the full 'txc config' lifecycle end-to-end through the real CLI subprocess with an isolated TXC_CONFIG_DIR: connection create -> auth add-service-principal (via --secret-from-env) -> profile create -> profile list -> profile select -> profile show -> delete cascade. Config unit test coverage (resolution, storage, vault, headless, Dataverse provider, commands) was already extensive under tests/TALXIS.CLI.Tests/Config -- no gaps identified there. The integration gap was the absence of a cross-process test that proves the actual CLI binary wires the command tree, MSAL vault, and file-backed stores together correctly under TXC_CONFIG_DIR isolation. This test plugs that gap without requiring a live Dataverse environment (it stops short of 'profile validate', which would dial out). CliRunner gains an optional 'env' overrides dictionary on RunAsync / RunRawAsync so tests can inject TXC_CONFIG_DIR / TXC_NON_INTERACTIVE / TXC_PLAINTEXT_FALLBACK / TXC_TOKEN_CACHE_MODE without touching the developer's real ~/.txc. Integration test count: 24 -> 26. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tract Milestone 17 (mcp-auth-contract). CliSubprocessRunner now unconditionally sets TXC_NON_INTERACTIVE=1 on every CLI subprocess it spawns. This is the single hard guarantee that MCP tool handlers cannot invoke interactive MSAL (browser / device code) or console prompts (masked secret read). The stdio transport reserves stdout for JSON-RPC frames, so any interactive flow would either hang the session or corrupt the transport. Headless detection in the CLI then rejects interactive credential kinds and fails fast with a clear remedy string instead. src/TALXIS.CLI.MCP/README.md gains an 'Auth contract (stdio)' section documenting: - the non-interactive invariant and its rationale, - the three prerequisite steps (login / add-sp -> connection create -> profile create + select), - per-call 'profile' override shape (forwarded as --profile), - env allow-list consumed from mcpServers.<name>.env (TXC_PROFILE, TXC_CONFIG_DIR, AZURE_*, ACTIONS_ID_TOKEN_*, TXC_ADO_ID_TOKEN_*), - log redaction guarantees (Bearer / Authorization / JWT / conn-string secret keys / URL query-param secrets), - forbidden patterns for future HTTP transport (token passthrough, tokens-in-URIs, missing PKCE, missing audience check). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Milestone 18 (mcp-profile-argument). No production code changes needed:
the MCP adapter reflects [CliOption] properties via
BindingFlags.Public | BindingFlags.Instance, which already includes
inherited properties. So every command deriving from ProfiledCliCommand
(all eight Dataverse-touching leaves) automatically surfaces 'profile'
(and 'verbose') in its MCP tool input schema, and BuildCliArgs forwards
them as --profile / --verbose.
Added CliCommandAdapterProfileArgumentTests pinning this contract:
1. BuildInputSchema on a ProfiledCliCommand-derived command exposes
'profile' as an optional string property (not in 'required').
2. BuildCliArgs forwards { profile: 'customer-a-dev' } as
'--profile customer-a-dev' to the CLI subprocess.
This prevents a future refactor from silently breaking per-session
profile-switching for MCP clients. The (sessionId, profile) credential-
resolution shape from the plan is effectively (constant, profile)
today under stdio; when HTTP transport lands, only sessionId needs to
become dynamic -- the profile axis is already in place.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Milestone 19 (mcp-http-readiness). docs/mcp-http-auth-notes.md records the invariants + forbidden patterns that will apply when the HTTP/SSE MCP transport eventually ships, so today's stdio implementation does not paint future work into a corner. Covers: resource-server-only role (Entra is the AS), RFC 9728 protected- resource metadata, RFC 8707 audience binding, JWT validation rules + WWW-Authenticate challenge, Mcp-Session-Id + MCP-Protocol-Version, per-session (sessionId, profile) credential resolution, and the six hard-forbidden patterns (token passthrough, tokens-in-URIs, non-HTTPS redirects, missing PKCE, missing audience check, caching federation tokens). Also documents transport-security defaults (127.0.0.1 bind, Origin validation, HSTS, CORS off). No production code changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Milestone 20 (docs). README had stale --environment URL flags on every example — these flags no longer exist on leaf commands. Replace the smoke-test-style snippets with the profile-based flow documented in CONTRIBUTING and the MCP README. New sections: - 'Identity, Connections & Profiles' — explains the three primitives, the resolution order (--profile > TXC_PROFILE > .txc/workspace.json > global pointer), and where secrets live (OS vault, not config files). - Interactive workflow: auth login -> connection create -> profile create -> profile select, plus pin/unpin for per-repo defaults and profile validate for end-to-end sanity check. - Headless / CI workflow: TXC_CONFIG_DIR isolation, TXC_NON_INTERACTIVE, add-service-principal with --secret-from-env (never --secret inline), and a note about WIF auto-detection via env vars. - Updated example commands to drop --environment everywhere and show the per-call '-p <profile>' override once. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR introduces a profile-based auth/config system for txc, splitting endpoint metadata (Connections), identities (Credentials), and their binding (Profiles), and refactors Dataverse-touching commands to resolve runtime connections via the new resolver/vault pipeline (including MCP headless behavior and log redaction).
Changes:
- Adds new
TALXIS.CLI.Config*projects (core model/stores/resolution/headless/vault + Dataverse provider +txc configcommand tree). - Refactors Dataverse commands and subprocess contracts to remove secrets from request DTOs and re-resolve credentials via profile/vault.
- Hardens logging redaction and MCP subprocess behavior (forced headless + documented auth contract), plus extensive unit/integration tests.
Reviewed changes
Copilot reviewed 147 out of 147 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/TALXIS.CLI.Tests/TALXIS.CLI.Tests.csproj | Adds references so tests can cover new config/provider/command projects. |
| tests/TALXIS.CLI.Tests/MCP/CliCommandAdapterProfileArgumentTests.cs | Verifies MCP schema/arg forwarding exposes per-call profile. |
| tests/TALXIS.CLI.Tests/Logging/LogRedactionFilterTests.cs | Expands coverage for bearer/header/JWT/connection-string/query redaction. |
| tests/TALXIS.CLI.Tests/Config/Vault/VaultUnavailableExceptionTests.cs | Adds tests for canonical vault-unavailable remedy messaging. |
| tests/TALXIS.CLI.Tests/Config/Vault/VaultOptionsTests.cs | Tests env-var-driven vault plaintext fallback selection. |
| tests/TALXIS.CLI.Tests/Config/TempConfigDir.cs | Test helper for isolated TXC_CONFIG_DIR layouts. |
| tests/TALXIS.CLI.Tests/Config/Storage/SecretRefTests.cs | Tests SecretRef URI formatting/parsing/validation. |
| tests/TALXIS.CLI.Tests/Config/Storage/ProfileStoreRoundtripTests.cs | Roundtrip/case-insensitivity tests for profile store. |
| tests/TALXIS.CLI.Tests/Config/Storage/GlobalConfigStoreRoundtripTests.cs | Roundtrip tests for global active-profile pointer. |
| tests/TALXIS.CLI.Tests/Config/Storage/CredentialStoreRoundtripTests.cs | Ensures SecretRef serializes as URI and nulls are omitted. |
| tests/TALXIS.CLI.Tests/Config/Storage/ConnectionStoreRoundtripTests.cs | Roundtrip tests incl. forward-compat extension-data retention. |
| tests/TALXIS.CLI.Tests/Config/Resolution/WorkspaceDiscoveryTests.cs | Tests .txc/workspace.json upward discovery semantics. |
| tests/TALXIS.CLI.Tests/Config/Providers/Dataverse/FederatedAssertionCallbacksTests.cs | Tests ADO/GitHub/file federated assertion acquisition behavior. |
| tests/TALXIS.CLI.Tests/Config/Providers/Dataverse/DataverseScopeTests.cs | Verifies Dataverse //.default scope construction. |
| tests/TALXIS.CLI.Tests/Config/Providers/Dataverse/DataverseMsalClientFactoryTests.cs | Tests MSAL client creation/authority selection rules. |
| tests/TALXIS.CLI.Tests/Config/Providers/Dataverse/DataverseConnectionProviderTests.cs | Validates provider kind + supported credential kinds + structural checks. |
| tests/TALXIS.CLI.Tests/Config/Providers/Dataverse/DataverseCloudMapTests.cs | Tests sovereign-cloud inference/mapping. |
| tests/TALXIS.CLI.Tests/Config/Providers/Dataverse/AuthorityChallengeResolverTests.cs | Tests parsing/behavior for WWW-Authenticate authority challenge resolution. |
| tests/TALXIS.CLI.Tests/Config/Headless/HeadlessDetectorTests.cs | Tests headless detection (redirect/env/CI signals). |
| tests/TALXIS.CLI.Tests/Config/Headless/HeadlessAuthRequiredExceptionTests.cs | Tests permitted kinds + deterministic failure messaging. |
| tests/TALXIS.CLI.Tests/Config/Commands/TxcServicesSerialCollection.cs | Ensures tests touching process-global service locator run serially. |
| tests/TALXIS.CLI.Tests/Config/Commands/Auth/AuthCrudCommandsTests.cs | Tests CRUD behaviors for txc config auth list/show/delete. |
| tests/TALXIS.CLI.Tests/Config/Commands/Auth/AuthAddServicePrincipalCommandTests.cs | Tests SP credential creation via env-var/pipe + vault persistence. |
| tests/TALXIS.CLI.IntegrationTests/CliRunner.cs | Adds env override support for cross-process integration tests. |
| src/TALXIS.CLI/TxcCliCommand.cs | Wires config command group into top-level CLI taxonomy. |
| src/TALXIS.CLI/TALXIS.CLI.csproj | Adds references to config/provider/command projects. |
| src/TALXIS.CLI/Program.cs | Bootstraps config DI (service locator) once per process. |
| src/TALXIS.CLI.XrmTools/PackageDeployerRunner.cs | Removes interactive auth; runner now requires an explicit connection string input. |
| src/TALXIS.CLI.XrmTools/PackageDeployerRequest.cs | Replaces secret-bearing fields with profile/config-dir pointers for subprocess contract. |
| src/TALXIS.CLI.XrmTools/CmtImportRunner.cs | Removes interactive auth; runner now requires explicit connection string input. |
| src/TALXIS.CLI.XrmTools/CmtImportRequest.cs | Removes secret-bearing fields from request DTO; moves connection string to runner param. |
| src/TALXIS.CLI.Workspace/ConfigCliCommand.cs | Deletes legacy workspace config placeholder group. |
| src/TALXIS.CLI.MCP/README.md | Documents stdio auth contract, per-call profile override, env allow-list, and redaction. |
| src/TALXIS.CLI.MCP/CliSubprocessRunner.cs | Forces TXC_NON_INTERACTIVE=1 for MCP-spawned subprocesses. |
| src/TALXIS.CLI.Logging/LogRedactionFilter.cs | Expands redaction patterns (bearer/header/JWT/connection-string/query-param secrets). |
| src/TALXIS.CLI.Logging/JsonStderrLogger.cs | Applies redaction at stderr sink (message + string state values). |
| src/TALXIS.CLI.Environment/TALXIS.CLI.Environment.csproj | Adds references to config/provider/command projects for env commands. |
| src/TALXIS.CLI.Environment/Solution/SolutionUninstallCliCommand.cs | Converts to ProfiledCliCommand and resolves Dataverse via profile bridge. |
| src/TALXIS.CLI.Environment/Solution/SolutionListCliCommand.cs | Converts to ProfiledCliCommand and resolves Dataverse via profile bridge. |
| src/TALXIS.CLI.Environment/Solution/SolutionImportCliCommand.cs | Converts to ProfiledCliCommand and resolves Dataverse via profile bridge. |
| src/TALXIS.CLI.Environment/Platforms/Dataverse/TxcServicesBootstrap.cs | Adds shared DI bootstrap for main process and subprocess helpers. |
| src/TALXIS.CLI.Environment/Platforms/Dataverse/PackageDeployerSubprocess.cs | Re-resolves connection string in child via profile/vault and scopes TXC_CONFIG_DIR. |
| src/TALXIS.CLI.Environment/Package/PackageUninstallCliCommand.cs | Converts to ProfiledCliCommand and resolves Dataverse via profile bridge. |
| src/TALXIS.CLI.Environment/Package/PackageImportCliCommand.cs | Uses profile-based subprocess contract for package deployer invocation. |
| src/TALXIS.CLI.Environment/Deployment/DeploymentShowCliCommand.cs | Converts to ProfiledCliCommand and resolves Dataverse via profile bridge. |
| src/TALXIS.CLI.Environment/Deployment/DeploymentListCliCommand.cs | Converts to ProfiledCliCommand and resolves Dataverse via profile bridge. |
| src/TALXIS.CLI.Data/TALXIS.CLI.Data.csproj | Adds references to config/provider/command projects. |
| src/TALXIS.CLI.Data/DataPackageImportCliCommand.cs | Builds Dataverse connection string from profile and passes it to CMT runner. |
| src/TALXIS.CLI.Config/Vault/VaultUnavailableException.cs | Adds deterministic vault failure exception with user-facing remedy. |
| src/TALXIS.CLI.Config/Vault/MsalCacheHelperFactory.cs | Centralizes MSAL cache helper creation and plaintext fallback policy. |
| src/TALXIS.CLI.Config/TALXIS.CLI.Config.csproj | New config core project (stores/resolution/headless/vault primitives). |
| src/TALXIS.CLI.Config/Storage/TxcJsonOptions.cs | Introduces canonical JSON serialization options for config/system output. |
| src/TALXIS.CLI.Config/Storage/SecretRefJsonConverter.cs | Serializes SecretRef as canonical URI string. |
| src/TALXIS.CLI.Config/Storage/ProfileStore.cs | File-backed profile store with atomic writes + in-process lock. |
| src/TALXIS.CLI.Config/Storage/JsonFile.cs | Shared atomic JSON read/write helpers. |
| src/TALXIS.CLI.Config/Storage/GlobalConfigStore.cs | File-backed global config store (active pointer, prefs). |
| src/TALXIS.CLI.Config/Storage/CredentialStore.cs | File-backed credential store (non-secret fields + secret refs). |
| src/TALXIS.CLI.Config/Storage/ConnectionStore.cs | File-backed connection store with extension-data retention. |
| src/TALXIS.CLI.Config/Storage/ConfigPaths.cs | Resolves config root via TXC_CONFIG_DIR or default ~/.txc. |
| src/TALXIS.CLI.Config/Resolution/WorkspaceDiscovery.cs | Locates .txc/workspace.json by upward directory walk. |
| src/TALXIS.CLI.Config/Resolution/IEnvironmentReader.cs | Testable abstraction over environment and current directory. |
| src/TALXIS.CLI.Config/Resolution/ConfigurationResolver.cs | Implements profile resolution precedence and loads profile/conn/cred triples. |
| src/TALXIS.CLI.Config/Model/WorkspaceConfig.cs | Models workspace default profile pointer file shape. |
| src/TALXIS.CLI.Config/Model/SecretRef.cs | Defines vault URI handles for secrets. |
| src/TALXIS.CLI.Config/Model/ResolvedProfileContext.cs | Represents resolved profile context + source for diagnostics. |
| src/TALXIS.CLI.Config/Model/ProviderKind.cs | Defines provider enum for connections. |
| src/TALXIS.CLI.Config/Model/ProfileCollection.cs | Defines JSON collection wrappers for stores. |
| src/TALXIS.CLI.Config/Model/Profile.cs | Defines Profile record (connectionRef + credentialRef). |
| src/TALXIS.CLI.Config/Model/GlobalConfig.cs | Defines global settings shape (active profile, log, telemetry). |
| src/TALXIS.CLI.Config/Model/CredentialKind.cs | Defines canonical credential kinds. |
| src/TALXIS.CLI.Config/Model/Credential.cs | Defines credential metadata + optional secret ref. |
| src/TALXIS.CLI.Config/Model/Connection.cs | Defines connection metadata + forward-compat extension data. |
| src/TALXIS.CLI.Config/Model/CloudInstance.cs | Defines sovereign cloud enum. |
| src/TALXIS.CLI.Config/Headless/HeadlessDetector.cs | Implements headless/non-interactive detection rules. |
| src/TALXIS.CLI.Config/Headless/HeadlessAuthRequiredException.cs | Adds deterministic exception for forbidden interactive auth in headless mode. |
| src/TALXIS.CLI.Config/DependencyInjection/TxcServices.cs | Adds process-global service locator to bridge command framework DI limitations. |
| src/TALXIS.CLI.Config/DependencyInjection/ConfigServiceCollectionExtensions.cs | Registers config core services (stores/resolver/headless/vault). |
| src/TALXIS.CLI.Config/Abstractions/IWorkspaceDiscovery.cs | Adds workspace discovery abstraction. |
| src/TALXIS.CLI.Config/Abstractions/IStores.cs | Adds store interfaces for profiles/connections/credentials/global config. |
| src/TALXIS.CLI.Config/Abstractions/IInteractiveLoginService.cs | Defines interactive login service boundary. |
| src/TALXIS.CLI.Config/Abstractions/IHeadlessDetector.cs | Defines headless detector + fail-fast extension method. |
| src/TALXIS.CLI.Config/Abstractions/ICredentialVault.cs | Defines OS-backed credential vault interface. |
| src/TALXIS.CLI.Config/Abstractions/IConnectionProvider.cs | Defines provider interface + validation modes. |
| src/TALXIS.CLI.Config/Abstractions/IConfigurationResolver.cs | Defines resolver interface + canonical resolution precedence doc. |
| src/TALXIS.CLI.Config.Providers.Dataverse/TALXIS.CLI.Config.Providers.Dataverse.csproj | New Dataverse provider project. |
| src/TALXIS.CLI.Config.Providers.Dataverse/Scopes/DataverseScope.cs | Builds Dataverse MSAL scope string (//.default). |
| src/TALXIS.CLI.Config.Providers.Dataverse/Runtime/IDataverseConnectionFactory.cs | Defines runtime Dataverse connection factory interface. |
| src/TALXIS.CLI.Config.Providers.Dataverse/Runtime/IDataverseAccessTokenService.cs | Defines access-token acquisition service interface. |
| src/TALXIS.CLI.Config.Providers.Dataverse/Runtime/DataverseConnectionFactory.cs | Builds ServiceClient using token callback from access-token service. |
| src/TALXIS.CLI.Config.Providers.Dataverse/Runtime/DataverseCommandBridge.cs | Bridges CLI commands to resolver + provider runtime (and connection-string transitional path). |
| src/TALXIS.CLI.Config.Providers.Dataverse/Msal/DataverseTokenCacheBinder.cs | Provides process-wide MSAL token cache helper binding. |
| src/TALXIS.CLI.Config.Providers.Dataverse/Msal/DataverseInteractiveLoginService.cs | Implements interactive browser login that primes MSAL cache. |
| src/TALXIS.CLI.Config.Providers.Dataverse/IDataverseLiveChecker.cs | Adds seam for WhoAmI/live validation. |
| src/TALXIS.CLI.Config.Providers.Dataverse/DependencyInjection/DataverseProviderServiceCollectionExtensions.cs | Registers Dataverse provider, token cache binder, runtime services. |
| src/TALXIS.CLI.Config.Providers.Dataverse/DataverseConnectionProvider.cs | Structural/live validation for Dataverse connections/credentials. |
| src/TALXIS.CLI.Config.Providers.Dataverse/Authority/DataverseCloudMap.cs | Maps sovereign clouds and infers cloud from environment URL host suffix. |
| src/TALXIS.CLI.Config.Providers.Dataverse/Authority/AuthorityChallengeResolver.cs | Resolves authority from Dataverse WWW-Authenticate challenge. |
| src/TALXIS.CLI.Config.Commands/TALXIS.CLI.Config.Commands.csproj | New config command project wiring. |
| src/TALXIS.CLI.Config.Commands/Setting/SettingSetCliCommand.cs | Implements txc config setting set. |
| src/TALXIS.CLI.Config.Commands/Setting/SettingRegistry.cs | Central registry of allowed setting keys and validation logic. |
| src/TALXIS.CLI.Config.Commands/Setting/SettingListCliCommand.cs | Implements txc config setting list. |
| src/TALXIS.CLI.Config.Commands/Setting/SettingGetCliCommand.cs | Implements txc config setting get. |
| src/TALXIS.CLI.Config.Commands/Setting/SettingCliCommand.cs | Adds txc config setting group. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileValidateCliCommand.cs | Implements structural/live txc config profile validate. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileUpdateCliCommand.cs | Implements txc config profile update. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileUnpinCliCommand.cs | Implements workspace unpin behavior. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileShowCliCommand.cs | Implements txc config profile show with expanded refs. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileSelectCliCommand.cs | Implements txc config profile select. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfilePinCliCommand.cs | Implements workspace pin behavior. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileListCliCommand.cs | Implements txc config profile list. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileDeleteCliCommand.cs | Implements txc config profile delete with optional cascade. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileCreateCliCommand.cs | Implements txc config profile create. |
| src/TALXIS.CLI.Config.Commands/Profile/ProfileCliCommand.cs | Adds txc config profile group. |
| src/TALXIS.CLI.Config.Commands/Connection/ConnectionShowCliCommand.cs | Implements txc config connection show. |
| src/TALXIS.CLI.Config.Commands/Connection/ConnectionListCliCommand.cs | Implements txc config connection list. |
| src/TALXIS.CLI.Config.Commands/Connection/ConnectionDeleteCliCommand.cs | Implements txc config connection delete with orphaning policy. |
| src/TALXIS.CLI.Config.Commands/Connection/ConnectionCreateCliCommand.cs | Implements txc config connection create. |
| src/TALXIS.CLI.Config.Commands/Connection/ConnectionCliCommand.cs | Adds txc config connection group. |
| src/TALXIS.CLI.Config.Commands/ConfigCliCommand.cs | Adds top-level txc config group (alias c). |
| src/TALXIS.CLI.Config.Commands/Auth/AuthShowCliCommand.cs | Implements txc config auth show. |
| src/TALXIS.CLI.Config.Commands/Auth/AuthListCliCommand.cs | Implements txc config auth list. |
| src/TALXIS.CLI.Config.Commands/Auth/AuthDeleteCliCommand.cs | Implements txc config auth delete. |
| src/TALXIS.CLI.Config.Commands/Auth/AuthCliCommand.cs | Adds txc config auth group. |
| src/TALXIS.CLI.Config.Commands/Abstractions/ProfiledCliCommand.cs | Adds shared --profile/-p and --verbose options for profile-bound commands. |
| CONTRIBUTING.md | Updates taxonomy/aliases conventions to include config group and -p exception. |
…doc fixes Seven findings from copilot-pull-request-reviewer; all valid. * Consistent JSON shape across every 'txc config' command. Five command sites were constructing 'new JsonSerializerOptions' from scratch, which dropped the kebab-case enum converter registered in TxcJsonOptions.Default. Result: 'provider', 'cloud', and 'kind' serialized as integers instead of the canonical string form used everywhere else. Reviewer flagged three; audit found two more (auth login, auth add-service-principal). All five now clone TxcJsonOptions.Default while keeping the human-readable pretty-print + null-omit behavior they want. * DataverseCommandBridge XML doc no longer overclaims support. BuildConnectionStringAsync actually rejects ClientCertificate (it needs cert-file plumbing that doesn't exist yet). Doc now says ClientSecret only (v1) and points at the follow-up milestone. * PackageImportCliCommand had an empty 'fail-fast' if block - all comments, no behavior. Empty profile IS permitted (resolver falls through to TXC_PROFILE / active-profile pointer), so there's nothing to fail on. Deleted. * ProfilePinCliCommand.WriteWorkspaceConfigAsync now honors its 'ct' parameter instead of passing CancellationToken.None to SerializeAsync / FlushAsync. * HeadlessAuthRequiredException remedy text no longer points at 'txc config auth create --kind client-secret', which doesn't exist. Rewrote to reference 'txc config auth add-service-principal --alias ... --secret-from-env ...' (the real command) and kept the env-var fallback list for WIF/SPN. Test baseline green: 364 unit + 26 integration + 5 skipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Member
Author
|
All 7 review comments addressed in 19ea619. Fixes
Test baseline green: 364 unit + 26 integration + 5 skipped. |
Introduces McpIgnoreAttribute (in TALXIS.CLI.Shared) as a marker that suppresses a command — and its entire descendant sub-tree — from the MCP tool registry. No new project references needed; TALXIS.CLI.Shared is already referenced by every command assembly. CliCommandLookupService.EnumerateRecursive now returns early when [McpIgnore] is detected, so a single attribute on a parent group hides all its children too. DocsCliCommand removed from TxcCliCommand.Children — hidden from both the CLI help tree and the MCP registry. Commands marked [McpIgnore] (9): - TransformServerStartCliCommand — blocks a local HTTP server process - AuthLoginCliCommand — interactive browser flow (headless fails fast) - AuthDeleteCliCommand — destructive credential maintenance - ConnectionDeleteCliCommand — destructive connection maintenance - ProfileDeleteCliCommand — destructive profile maintenance - ProfileUpdateCliCommand — profile maintenance, not agent task - ProfileValidateCliCommand — redundant: create + show cover the checks - ProfilePinCliCommand — workspace-local; agents use profile select - ProfileUnpinCliCommand — same MCP tool count: ~34 → ~25. Tests: - New CliCommandLookupServiceTests covering ignored leaves, ignored sub-trees, and the real command tree. - McpServerProtocolTests updated: replaced docs assertion (docs removed) with workspace_explain (stable short-lived tool). Build: 0 errors. Tests: 368 unit + 26 integration + 5 skipped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Fixes 'Credential kind InteractiveBrowser cannot currently be used with subprocess-based commands' for both 'package import' and 'data package import'. Also moves the CMT data-import runner out-of-process for the same legacy-patched-Xrm.Tooling-assembly isolation reason as Package Deployer. Auth (DataverseCommandBridge): - New PrimeTokenAsync(profile) for parent-side cache priming. - New BuildTokenProviderAsync returning (envUrl, Func<string,Task<string>>), honouring the resource URI requested by the SDK (canonicalized/redirected org URL) rather than hardcoding Connection.EnvironmentUrl. - IDataverseAccessTokenService.AcquireForResourceAsync overload; existing AcquireAsync delegates to it. - BuildConnectionStringAsync now generically rejects non-ClientSecret kinds with a pointer to the token-provider path. Runners: - PackageDeployerRunner.RunAsync(envUrl, tokenProvider, ct) overload using the per-instance CrmServiceClient(Uri, Func, ...) ctor (no static AuthOverrideHook race). - CmtImportRunner.RunAsync(envUrl, tokenProvider, ct) overload doing the same for the primary client and every clone. Subprocess isolation: - Replaced PackageDeployerSubprocess with shared LegacyAssemblyHostSubprocess coordinator. Generic RunJobAsync<TRequest,TResult>; dispatches __txc_internal_package_deployer, __txc_internal_cmt_import, and the existing cleanup helper command. - New CmtImportJob IPC envelope (request + profile + config dir + parent pid) so CmtImportRequest stays a leaf record. - DataPackageImportCliCommand now primes the cache and submits via RunCmtImportAsync; drops in-process CmtImportRunner usage and the connection-string path. - PackageImportCliCommand calls PrimeTokenAsync before spawning and uses the new shared coordinator. Workspace + Data: qualified ambiguous Environment.* references with System.Environment.* now that TALXIS.CLI.Data transitively pulls in the TALXIS.CLI.Environment namespace. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Delete DataverseAuthTokenProvider (legacy ~/.txc/auth MSAL cache). All Dataverse token acquisition now flows through IDataverseAccessTokenService backed by DataverseMsalClientFactory and the per-profile cache under TXC_CONFIG_DIR. - Delete DataverseInteractiveAuthHook (IOverrideAuthHookWrapper adapter). CrmServiceClient is now constructed via the per-instance Func<string,Task<string>> ctor returned by DataverseCommandBridge.BuildTokenProviderAsync — no static auth-hook state. - Simplify DataverseConnection: drop the always-null TokenProvider property and the wrapping ServiceClientFactory file. The class is now a single-responsibility ServiceClient wrapper. - Drop two redundant integration-test methods that exercised DataverseAuthTokenProvider statics; equivalent coverage already lives in DataverseScopeTests + DataverseCloudMapTests. - Tidy IDataverseConnectionFactory doc comment that referenced the deleted ServiceClientFactory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Collapse the three-step dev onboarding (auth login -> connection create ->
profile create --auth --connection) into a single command:
txc c p create --url https://contoso.crm4.dynamics.com/
Provider is inferred from the URL host (Dataverse commercial / gov / DoD /
China), the default name is derived from the first DNS label, and the
interactive sign-in + credential upsert + connection upsert + profile
binding happen in one orchestrated step. The primitive commands stay
for advanced and service-principal flows.
- Extract shared helpers: CredentialAliasResolver, ConnectionUpsertService,
ProviderUrlResolver.
- Introduce IConnectionProviderBootstrapper with DataverseConnectionProviderBootstrapper.
- Rewrite ProfileCreateCliCommand with dual-mode (OneLiner / Explicit) option
classification, --name replaces positional; auto-activation preserved.
- Headless guard honors HeadlessAuthRequiredException in --url mode.
- Register new services in core + Dataverse DI extensions.
- Tests: ProviderUrlResolverTests (inference + name derivation) and
ProfileCreateOneLinerTests (happy path, name collision, unknown host,
mixed-mode rejection, headless failure, explicit --name override).
- README: document quickstart vs advanced flows.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Both auth login and the Dataverse one-liner bootstrapper were doing the same dance: headless guard -> login -> alias resolve -> build Credential with identical fields -> upsert. Extract it into InteractiveCredentialBootstrapper so there is exactly one code path that creates interactive-browser credentials. Also remove the back-compat thin wrappers on AuthLoginCliCommand (ResolveDefaultAliasAsync, ExtractTenantShortName) - pre-release, so tests call CredentialAliasResolver directly now. All 392 tests pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Resolves conflict with master where .DS_Store was deleted (e1d92b6). Fix malformed gitignore entry 'temp.DS_Store' which ignored nothing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…iles # Conflicts: # tests/TALXIS.CLI.IntegrationTests/CliRunner.cs
Relocates TxcServicesBootstrap from TALXIS.CLI.Environment/Platforms/Dataverse/ (a feature project) to TALXIS.CLI.Config.Providers.Dataverse/DependencyInjection/ (the platform adapter that actually owns the Dataverse DI graph). Host and the PackageDeployer/CMT subprocess now import it from the platform namespace. Also drops the unused Config.Commands -> Config.Providers.Dataverse ProjectReference (no 'using' statements referenced it in source). This is the first dependency edge cleanup toward the Features / Core / Platform layering described in plan.md. Part of the repo architecture reorg (Phase 1). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Introduce ISolutionInventoryService + InstalledSolutionRecord in TALXIS.CLI.Config so feature commands no longer import Dataverse SDK or Config.Providers.Dataverse.Runtime directly. The Dataverse-bound reader moves into the provider project as the service implementation. SolutionListCliCommand now resolves the service via TxcServices and stays focused on argument parsing and output formatting. First of seven commands in the per-command-abstractions refactor. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Relocate 14 Dataverse-specific files (readers, subprocess host, importers, history, schema, NuGet installer) from the Environment feature project into the Dataverse platform adapter. Namespace updated from TALXIS.CLI.Environment.Platforms.Dataverse to TALXIS.CLI.Config.Providers.Dataverse.Platforms. Consumer using directives rewritten across Environment/Data commands, Program.cs, and the unit + integration test suites. XrmTools ProjectReference moved into the provider project; TryDeleteDirectory exposed publicly so PackageImport can finish cleaning up its temp directory during the transitional period (will be encapsulated by the upcoming IPackageImportService abstraction). Eliminates the circular-reference obstacle blocking per-command abstractions. 392/392 unit tests green. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DataPackageImportCliCommand and SolutionUninstallCliCommand now resolve their platform work through service-locator abstractions in TALXIS.CLI.Config. MSAL and Dataverse SDK types no longer leak into these feature commands. Lifts DTOs (DataPackageImportResult, SolutionUninstallStatus, SolutionUninstallOutcome) into Core. PackageUninstallCliCommand using directives updated for the relocated DTOs pending its own service lift. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
SolutionImportCliCommand now resolves solution-import work through ISolutionImportService; DTOs (SolutionInfo, SolutionImportPath, SolutionImportOptions, SolutionImportResult) lifted into Core. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Lifts PackageHistoryRecord, SolutionHistoryRecord, DeploymentHistorySnapshot, and DeploymentRelativeTimeParser into Core; adds DataverseDeploymentHistoryService implementation. DeploymentListCliCommand is now a thin shell over the service. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds DeploymentDetailResult / DeploymentRunKind / AsyncOperationSummary to Core. DataverseDeploymentDetailService encapsulates all platform lookups (package / solution / async-op / latest) behind a single provider-agnostic interface; DeploymentShowCliCommand is now a thin renderer. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DataversePackageImportService encapsulates token priming, Package Deployer subprocess, and temp-working-directory cleanup. PackageImportCliCommand becomes a thin shell handling only NuGet restore, arg validation, and result rendering. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
DataversePackageUninstallService owns package-config reading, reverse-order computation, connection, history-record lifecycle, and per-solution uninstall. PackageUninstallCliCommand becomes a thin shell. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move NuGetPackageInstallerService/Options/Result from Config.Providers.Dataverse into Config.Platforms.Packaging (NuGet restore is provider-agnostic). Drop ProjectReferences from Environment and Data to Dataverse, XrmTools, and Config.Providers.Dataverse. Feature projects now compose only against Core and Logging; provider wiring remains host-side in DI. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Workspace never imported any TALXIS.CLI.Data type; the edge was dead. Add direct Logging + Shared references (previously transitive via Data). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All consumers live inside TALXIS.CLI.Config.Providers.Dataverse now. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- TALXIS.CLI.Config.Commands -> TALXIS.CLI.Features.Config - TALXIS.CLI.Data -> TALXIS.CLI.Features.Data - TALXIS.CLI.Environment -> TALXIS.CLI.Features.Environment - TALXIS.CLI.Workspace -> TALXIS.CLI.Features.Workspace - TALXIS.CLI.Docs -> TALXIS.CLI.Features.Docs - TALXIS.CLI.XrmTools.XrmShim -> TALXIS.CLI.Platform.XrmShim - TALXIS.CLI.XrmTools -> TALXIS.CLI.Platform.Xrm Project names now reflect role (Hosts / Features / Platform). XrmShim retains Microsoft.Xrm.Tooling.Connector AssemblyName for legacy-assembly shim behavior. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…Dataverse Consolidates the Dataverse domain project and the Config.Providers.Dataverse provider project into a single TALXIS.CLI.Platform.Dataverse project. Platform.Xrm's dependency on the old domain project is dropped (unused in code), breaking the new circular reference. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Consolidates TALXIS.CLI.Shared (OutputWriter, McpIgnoreAttribute) into the Config project, then renames Config -> TALXIS.CLI.Core to reflect its role as the shared core/abstractions layer for the CLI. Completes Phase 2 of the repository architecture reorganization: projects now follow the Hosts / Features.* / Platform.* / Core / Logging taxonomy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replaces the old 'Platforms/<Name>/ inside the owning project' guidance with the new Platform.* project model. Adds an explicit Project layout section documenting the architectural tiers (Hosts / Features / Core / Platform / Cross-cutting) and the dependency rules between them. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- DataverseConnectionFactory: token callback now honors the resource parameter passed by Xrm Tooling ServiceClient (it may canonicalize or redirect the org URL), falling back to AcquireAsync only when the callback provides no/invalid resource. - Core.csproj: align Microsoft.Identity.Client.Extensions.Msal to 4.83.3 to match Platform.Dataverse and avoid NuGet version downgrade. - TxcServices.Initialize: guard against double-initialization. Throws if called a second time with a different provider; still allows tests to replace the provider by calling Reset() first. - Profile-argument schema test: use TryGetProperty for 'required' so the test tolerates JSON Schemas that legitimately omit the array when no members are required. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…taverse It was the last concrete Dataverse implementation living in Core. Core now only holds the IConnectionProviderBootstrapper abstraction and shared bootstrapping helpers (ConnectionUpsertService, InteractiveCredentialBootstrapper). The Dataverse-specific orchestration lives in Platform.Dataverse where every other Dataverse impl sits, keeping the dependency graph strictly inward-pointing. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TomProkop
added a commit
that referenced
this pull request
Apr 23, 2026
First release includes profile-based auth & config system (PR #12). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TomProkop
added a commit
that referenced
this pull request
Apr 23, 2026
First release includes profile-based auth & config system (PR #12). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
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.
Summary
Introduces a profile-based authentication and configuration system that decouples service endpoints (Connections), identities (Credentials), and their named pairing (Profiles). Closes out the full 20-milestone plan tracked on
tp/config-auth-profiles.Goals driving the design:
--profileflag >TXC_PROFILEenv ><repo>/.txc/workspace.json> global active pointer.TXC_NON_INTERACTIVE=1.TXC_CONFIG_DIRfor total isolation on CI runners.TXC_ADO_ID_TOKEN_REQUEST_*, env-var SPN).Command surface
Every leaf command that touches a live environment takes
--profile <name>(short-p). The old--environment <url>/--connection-stringflags are gone — the only authoring surface for endpoints isconfig connection create.Key changes
TALXIS.CLI.Config— core primitives (Credential/Connection/Profile records), file-backed stores, resolver with env/workspace/global layering, MSAL-backed vault (DPAPI / Keychain / libsecret).TALXIS.CLI.Config.Providers.Dataverse— authority map, scope, MSAL factory, pac-parity federated assertion callbacks (GitHub OIDC + ADO WIF + env SPN).TALXIS.CLI.Config.Commands— full command tree underconfig(20 leaf commands).ProfiledCliCommandand resolveServiceClientthrough the runtime connection factory. Secrets are stripped from subprocess request DTOs and re-resolved in the worker via the same vault path.LogRedactionFilter(Bearer, Authorization header, bare JWT, connection-string keys, URL query-param secrets) applied at theJsonStderrLoggersink so no log frame escapes the process unredacted.TXC_NON_INTERACTIVE=1forced on every subprocess; per-callprofileargument auto-exposed on everyProfiledCliCommand-derived tool via the input-schema reflection path; auth contract documented insrc/TALXIS.CLI.MCP/README.md.docs/mcp-http-auth-notes.md(forward-compat design for the eventual HTTP transport: resource-server-only, RFC 9728 + RFC 8707, 6 forbidden patterns);README.mdrewritten with interactive and CI walkthroughs;CONTRIBUTING.mddocuments the five-group taxonomy withconfigsub-nouns.Tests
ProfileEndToEndTests— full cross-process lifecycle (connection create → add-service-principal via--secret-from-env→ profile create/list/select/show → delete cascade → missing-profile fail-fast) under isolatedTXC_CONFIG_DIR.Out of scope / deferred
CONTRIBUTING.md.docs/mcp-http-auth-notes.md; no code.Reviewer notes
CONTRIBUTING.mdis the source of truth for taxonomy/verbs/selector conventions — please call out any command or flag that drifts from it.