CLI Active Profile
Essential knowledge
Intended Audience:
Technical User
Author:
Marco Heuer
Changed on:
30 Apr 2026
Overview
Learn the technical mechanics of the Active Profile feature and how it streamlines CLI workflows. This article details the shift from rigid "Commander-enforced" validation to a flexible "Defaults-first" resolution, allowing implementing partners to work faster without sacrificing accuracy.For the business, this context-aware CLI reduces the learning curve and prevents manual entry errors. Note: While flags are no longer required at the command level (`required: false`), they remain mandatory for execution; the CLI will exit if no context is found.Key points
- The Shift: Validation has moved from the Commander parsing stage to the Configuration Utility stage.
- Precedence: CLI flags always override the Active Profile. If you explicitly pass
`--profile`, the active context is ignored for that command. - Seamless Integration: The
`loadConfigs`utility now calls`resolveDefaults`first to populate missing options before handing them off to existing validation logic. - Unchanged Security: Standard validation functions like
`loadProfile()`and`checkConfigForAccountProfile()`are still in place; they simply receive their data from a default source if flags are missing. - Developer Impact: Task definitions must have
`required: true`removed from their options to allow the Active Profile logic to trigger. - Error States: If an Active Profile points to a nonexistent profile, the system will catch it during the
`loadProfile`phase and exit with a`process.exit(1)`.
The Question
How does the Active Profile feature work with existing tasks that have`required: true` for profile/retailer options?Current Flow (Before Active Profile)
`1. User runs: fluent module list --profile apsear2dev --retailer ATEST2`2. Commander parses CLI args
- Validates required options exist
- Builds allOpts object: { profile: 'apsear2dev', retailer: 'ATEST2' }
3. Action handler calls configurationUtil.loadConfigs(config, allOpts, modulePath)
4. loadConfigs checks: if (allOpts.profile)
- Calls loadProfile(allOpts.profile)
- loadProfile validates profile exists, calls process.exit(1) if not
- Calls loadRetailer(allOpts.profile, allOpts.retailer)
- loadRetailer validates retailer exists, calls process.exit(1) if not
5. Task's run() function executes with populated configKey point: Validation happens in TWO places:
- Commander level:
`required: true`ensures flags are provided - Configuration level:
`loadProfile()`and`loadRetailer()`validate they exist
New Flow (with Active Profile)
Key changes:- Remove
`required: true`from task option definitions - Add Active Profile resolution BEFORE existing validation
- Existing validation in
`loadProfile()`and`loadRetailer()`is UNCHANGED
Error Handling Examples
Scenario 1: No Active Profile set, no flags
`$ fluent module list``# Flow:
# 1. allOpts.profile = undefined
# 2. Active Profile doesn't exist
# 3. allOpts.profile still undefined after resolution
# 4. loadConfigs checks: if (allOpts.profile) - FALSE
# 5. config.account is undefined
# 6. Task calls checkConfigForAccountProfile(config)
# 7. Error: "Profile must be set. Use --profile flag or fluent profile use"
`Scenario 2: Active Profile exists, profile invalid
`$ fluent profile use nonexistent_profile`$ fluent module list
`# Flow:
# 1. allOpts.profile = undefined
# 2. Active Profile: { activeProfile: 'nonexistent_profile' }
# 3. allOpts.profile = 'nonexistent_profile' (from Active Profile)
# 4. loadConfigs calls loadProfile('nonexistent_profile')
# 5. loadProfile checks if profile exists
# 6. Error: "Profile 'nonexistent_profile' does not exist. Use 'profile' command..."
# 7. process.exit(1)
`Scenario 3: Active Profile exists, retailer required but missing
`$ fluent profile use apsear2dev # No retailer set
`$ fluent setup payment-service-provider`# Flow:
# 1. allOpts.profile = 'apsear2dev', allOpts.retailer = undefined
# 2. loadConfigs loads profile successfully
# 3. allOpts.retailer still undefined
# 4. config.retailer = {} (empty)
# 5. Task calls checkConfigForRetailerProfile(config)
# 6. Error: "Retailer must be set. Use --retailer flag or fluent profile use <profile> [-r|--retailer] <retailer>"
# 7. Throws error
`Scenario 4: CLI flags override Active Profile
`$ fluent profile use apsear2dev --retailer ATEST2`$ fluent module list --profile production --retailer PRODRETAILER
`# Flow:
# 1. allOpts.profile = 'production', allOpts.retailer = 'PRODRETAILER' (from CLI)
# 2. resolveDefaults sees CLI values exist
# 3. Skip Active Profile (CLI takes precedence)
# 4. Use 'production' and 'PRODRETAILER'
`Task Option Changes
Before
`// packages/cli/src/tasks/module/moduleList.ts
export const taskdef: fluentCliTypes.Taskdef = {`command
`: 'module',`subcommand
`: 'list',`options
`: [`{
flags
`: '-p, --profile <profile>',`description
`: 'The Profile to use.',`required
`: true // ← Commander enforces this
` }],
run
`: async function (config, args, opts) {`checkConfigForAccountProfile(config);
`// ← Also validates
` `// ...
` }};
After
`// packages/cli/src/tasks/module/moduleList.ts
export const taskdef: fluentCliTypes.Taskdef = {`command
`: 'module',`subcommand
`: 'list',`options
`: [`{
flags
`: '-p, --profile <profile>',`description
`: 'The Profile to use. Defaults to active profile if set.',`required
`: false // ← Allow Commander to skip
` }],
run
`: async function (config, args, opts) {`checkConfigForAccountProfile(config);
`// ← This STILL validates!
` `// ...
` }};
Validation Utilities (Unchanged)
The existing validation utilities still work:`// packages/cli/src/utils/validationSchemaUtil.ts
export const checkConfigForAccountProfile = (config: fluentCliTypes.Config): void => {``if (!config.account) {``throw new Error('Account profile not configured. Use --profile flag or set active profile with: fluent profile use <profile>');`}
};
`export const checkConfigForRetailerProfile = (config: fluentCliTypes.Config): void => {`checkConfigForAccountProfile(config);
`if (!config.retailer || !config.retailer.id) {``throw new Error('Retailer not configured. Use --retailer flag or set active profile with: fluent profile use <profile> [-r|--retailer] <retailer>');`}
};
These throw errors if
`config.account` or `config.retailer` are missing, which happens when: - No active profile is set - Active profile doesn't have the needed value - loadProfile/loadRetailer failed to populate configConfiguration Flow Diagram

Implementation in configurationUtil.ts
`import activeContextUtil from './activeContextUtil.js';``const loadConfigs = (config: fluentCliTypes.Config, allOpts: OptionValues, modulePath: string | null) => {`log.debug('loadConfigs, allOpts: %s', allOpts);
`// NEW: Inject active profile if CLI flags not provided
` `const defaults = activeContextUtil.resolveDefaults(allOpts);``if (!allOpts.profile && defaults.profile) {`allOpts.profile
`= defaults.profile;`log.debug('Using active profile: %s', defaults.profile);
}
`if (!allOpts.retailer && defaults.retailer) {`allOpts.retailer
`= defaults.retailer;`log.debug('Using active retailer: %s', defaults.retailer);
}
`// EXISTING CODE (unchanged)
` `if (allOpts.profile) {`config.name
`= allOpts.profile;``const profileConfig = loadProfile(allOpts.profile); // ← Validates & exits if bad
` config.account `= _.merge(profileConfig);``const retailerConfig = allOpts.retailer && !Array.isArray(allOpts.retailer)``? loadRetailer(allOpts.profile, allOpts.retailer) // ← Validates & exits if bad
` `: {};`config.retailer
`= _.merge(retailerConfig);`}
`const moduleConfig = modulePath ? loadModule(modulePath, allOpts.retailer, allOpts.config) : {};`config.module
`= _.merge(moduleConfig);`log.debug('loadConfigs: %o', config);
`return config;`};
Summary (Answer and Result)
The answer: Active Profile integrates seamlessly because:- ✅ We remove
`required: true`from Commander option definitions - ✅ Active Profile populates
`allOpts`BEFORE existing validation - ✅ Existing validation in
`loadProfile()`and`loadRetailer()`is UNCHANGED - ✅ Existing validation in
`checkConfigForAccountProfile()`is UNCHANGED - ✅ Tasks see no difference - they get
`config.account`and`config.retailer`either way
