collectRoleSecrets function
- ModelRolesConfig rolesConfig,
- SecureKeyCache keys, {
- Map<
String, String> ? env, - List<
CustomProviderEntry> customProviders = const [],
Collects the secrets snapshot for the model-roles resolver: every
provider catalog env name plus its rotation stack (NAME, NAME_2,
NAME_3, ...), plus any base name referenced by an explicit
apiKeyName in the roles config. The platform secure store backs up
base names where the environment has none (env wins; rotation stacks
stay env-only — secure storage holds base names only). customProviders
(the customProviders: section) feeds the registry leg (issue #1552):
a chain entry naming one of these entries resolves keys through the
entry — its pinned keyName first, else the endpoint-scoped slot — so
both names ride the snapshot, scoped to the entries the chains actually
NAME: an unreferenced saved entry changes nothing (existing configs
stay byte-identical — the PR contract).
Implementation
Map<String, String> collectRoleSecrets(
ModelRolesConfig rolesConfig,
SecureKeyCache keys, {
Map<String, String>? env,
List<CustomProviderEntry> customProviders = const [],
}) {
final baseNames = <String>{
for (final spec in providerCatalog.values) ...spec.apiKeyEnvNames,
...roleKeyNames(rolesConfig, customProviders: customProviders),
..._referencedCustomKeyNames(rolesConfig, customProviders),
};
final secrets = <String, String>{};
final environment = env ?? Platform.environment;
for (final base in baseNames) {
final suffix = RegExp('^${RegExp.escape(base)}_\\d+\$');
for (final entry in environment.entries) {
if (entry.key == base || suffix.hasMatch(entry.key)) {
if (entry.value.isNotEmpty) secrets[entry.key] = entry.value;
}
}
if (!secrets.containsKey(base)) {
final stored = keys.read(base);
if (stored != null) secrets[base] = stored;
}
}
return secrets;
}