Skip to content

Introduce layered YAML config loader, replaces .env Config which remains for backwards compat (but deprecated)#2342

Merged
robgruen merged 34 commits into
mainfrom
dev/robgruen/config
May 14, 2026
Merged

Introduce layered YAML config loader, replaces .env Config which remains for backwards compat (but deprecated)#2342
robgruen merged 34 commits into
mainfrom
dev/robgruen/config

Conversation

@robgruen
Copy link
Copy Markdown
Collaborator

This pull request migrates the configuration system from legacy .env files to a new, more flexible YAML-based approach using config.local.yaml, powered by the @typeagent/config package. It updates documentation, adds sample and default config files, and refactors all example projects to use the new configuration loader. Legacy .env files are now deprecated and will be removed after September 2026.

robgruen added 19 commits May 7, 2026 20:00
Adds a layered YAML configuration loader as the foundation for migrating
TypeAgent off flat .env files. This phase is fully backwards compatible:
the loader populates process.env, so all existing getEnvSetting / EnvVars
consumers in aiclient and elsewhere keep working unchanged.

Package contents:
  - flatten: nested YAML to flat EnvVars-shaped keys (with env: / extra:
    top-level passthrough so existing flat-key conventions like
    AZURE_OPENAI_ENDPOINT_GPT_4_O_EASTUS_PTU work verbatim).
  - schema: lightweight zod validation with file-path-aware errors.
  - loader: async loadConfig() and sync loadConfigSync() with precedence
    .env (legacy) -> config.defaults.yaml -> config.local.yaml ->
    process.env. Existing process.env values are preserved.
  - 31 unit tests covering flattening, merging, precedence, source
    tracking, strict / non-strict modes, and idempotency.

Workspace integration:
  - ts/config.defaults.yaml committed with non-secret universal defaults
    (max_concurrency, max_timeout, max_retryattempts, response_format).
  - ts/.gitignore now excludes config.local.yaml.

Live Azure Key Vault fetch, encrypted on-disk caching, the
typeagent config CLI family (import / push / show / refresh / check /
sync), and the ~40-call-site sweep are added in subsequent phases.

Refs: dev/robgruen/config plan.
Introduces live Key Vault fetch as a new layer in the precedence
chain, sitting between defaults and local YAML per the locked design:

  .env (legacy) -> defaults -> Key Vault -> local -> process.env

New module packages/config/src/keyVault.ts:
  - DEFAULT_SECRET_NAME = 'typeagent-config'
  - makeAzureFetcher(credential): SecretClient-backed fetcher with
    per-vault caching, mirroring the auth pattern used by aiclient
    and tools/scripts/getKeys.mjs. 404 is treated as a soft miss.
  - fetchKeyVaultConfig(options): downloads, validates, parses YAML.
    Honors a 25 KiB byte cap matching the Azure secret-size limit.
    failOnError defaults to false so transient outages let the loader
    fall through to lower layers (offline-friendly).
  - Test isolation guard: refuses live SDK calls under Jest unless
    TYPEAGENT_ALLOW_KEYVAULT_IN_TESTS=1, or a stub fetcher is
    injected. This keeps unit suites hermetic.

Loader integration:
  - loadConfig is now a real async loader (no longer a thin wrapper).
  - When options.keyVault is provided it splices the KV layer into
    the precedence chain at the correct position, then delegates the
    final merge / process.env application to a shared helper.
  - loadConfigSync ignores keyVault (sync API stays file-only).

Tests (21 new, 52 passing total):
  - keyVault.spec.ts: parse, default secret name, missing / empty,
    failOnError on / off, oversize, top-level array rejection,
    schema validation, isolation guard on / off, env opt-in.
  - loaderKeyVault.spec.ts: KV beats defaults, local beats KV, KV
    beats .env, missing secret falls through, source-tracking
    attribution, no-op when keyVault not provided.

Dependencies: @azure/identity ^4.10.0, @azure/keyvault-secrets ^4.9.0
(version-aligned with packages/aiclient).

Refs: dev/robgruen/config plan, Phase 2.
Lands the user-facing CLI for the new config system. Three commands
now ship; push / refresh / sync are deferred to Phase 2.5 because
they need the encrypted backup path.

New modules:
  - src/import.ts: parseDotEnvText / parseDotEnvFile / importDotEnv /
    flatEnvToConfigTree / writeConfigYamlFile.  The importer places
    every flat key into the 'extra:' passthrough bucket, which
    guarantees a byte-identical round trip through flatten().
    Round-trip equivalence is verified before the file is written;
    failure throws.  Structured rewriting (e.g., AZURE_OPENAI_* into
    nested form) lands incrementally on top of this safety net.
  - src/redact.ts: shouldRedact / redactFlat / redactTree.  Pattern
    based on /key|secret|password|token|credential/i, with explicit
    allow-list for the 'identity' managed-identity sentinel and
    empty strings.  Used by 'show' (default) and 'check'.
  - src/cli.ts: pluggable runCli(argv, io) entry point.  CliIO
    interface lets tests capture stdout / stderr without touching
    the real streams.  Subcommands:
      import <path/to/.env> [--out <yaml>]  (default ./config.local.yaml)
      show [--source] [--reveal-secrets]
      check [--vault <name>] [--secret <s>]
  - bin/typeagent-config.mjs: thin shim that invokes runCli.

Package wiring:
  - package.json gains 'bin: typeagent-config' and an './cli' subpath
    export so other packages can call runCli programmatically.
  - 'bin/' added to the published 'files' list.
  - index.ts re-exports the importer, redactor, and CLI surface.

Tests (34 new, 86 passing total):
  - import.spec.ts: dotenv parsing, extra-bucket placement, round
    trip through flatten, end-to-end import file → tree, missing-file
    error.
  - redact.spec.ts: secret-key detection, identity / empty allow
    list, non-string passthrough, deep tree redaction, no mutation.
  - cli.spec.ts: help + unknown commands, import success path with
    YAML output assertions, missing-file error, unknown flag handling
    for each subcommand, check vault-name requirement.

Refs: dev/robgruen/config plan, Phase 2.7 (push / refresh / sync
deferred to Phase 2.5).
Comment thread ts/packages/config/src/keyVault.ts Fixed
Comment thread ts/packages/config/src/keyVault.ts Fixed
Comment thread ts/packages/config/src/keyVault.ts Fixed
Comment thread ts/packages/config/src/keyVault.ts Fixed
@robgruen robgruen had a problem deploying to development-fork May 14, 2026 19:32 — with GitHub Actions Failure
@robgruen robgruen had a problem deploying to development-fork May 14, 2026 19:32 — with GitHub Actions Failure
@robgruen robgruen had a problem deploying to development-fork May 14, 2026 21:44 — with GitHub Actions Failure
@robgruen robgruen had a problem deploying to development-fork May 14, 2026 21:44 — with GitHub Actions Failure
@robgruen robgruen marked this pull request as ready for review May 14, 2026 22:37
@robgruen robgruen added this pull request to the merge queue May 14, 2026
Merged via the queue into main with commit 9ad34de May 14, 2026
24 of 29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants