Skip to contentStep 1: Create the provider catalog in
Step 2: Create
Step 4: Update
Step 5: Update
Part 3: Configuration Management
Configuration is what turns ProtoAgent from a generic CLI into a usable local tool.
In Part 2, the app only knew how to talk to OpenAI through a single environment variable. In this part, you move that setup into a real persisted config flow.
By the end, your result should match protoagent-tutorial-again-part-3.
What you are building in this part
Starting from Part 2, you are adding:
- a persisted config file on disk
- a provider/model catalog
- a merged runtime provider registry backed by
protoagent.jsonc - a standalone
protoagent configureflow - provider-specific API key handling
- app startup that reads config before creating the model client
This is the point where ProtoAgent stops feeling like a demo and starts feeling like a local tool you can actually reopen.
Starting point
Copy your Part 2 project and continue from there.
Your target snapshot is:
protoagent-tutorial-again-part-3
Files to create or change
This stage introduces three important files and updates the CLI/UI wiring:
src/config.tsxsrc/providers.tssrc/cli.tsxsrc/App.tsx
Step 1: Create the provider catalog in src/providers.ts
This file becomes the source of truth for which providers and models the config flow can offer.
The stage snapshot defines:
ModelDetailsModelProviderBUILTIN_PROVIDERSgetAllProviders()
At this stage, the provider list already includes:
- OpenAI
- Google Gemini
- Anthropic Claude
Each model carries:
idnamecontextWindowpricingPerMillionInputpricingPerMillionOutput
The pricing fields are not used yet in this part, but adding them here keeps the provider catalog useful for later stages.
Step 2: Create src/config.tsx
This file does three jobs at once:
- config file persistence
- the standalone configure UI
- API key resolution against the merged provider registry
The stage-3 snapshot keeps config.json intentionally small:
ts
export interface Config {
provider: string;
model: string;
apiKey?: string;
}The richer extensibility layer lives in protoagent.jsonc, which is loaded separately from the persisted selection file.
Also implement helper functions for:
getConfigDirectory()getConfigPath()ensureConfigDirectory()readConfig()writeConfig()
At this stage, those helpers can stay synchronous and simple.
Step 3: Add the configure wizard components
The stage snapshot keeps the config wizard inside src/config.tsx itself.
It defines a multi-step flow with these components:
InitialLoadingResetPromptModelSelectionApiKeyInputConfigResultConfigureComponent
That means the file is doing a lot, but it also makes the stage easier to follow because all config-related logic is in one place.
The flow is:
- check whether a config already exists
- optionally ask whether to reset it
- choose a provider/model pair
- enter the provider-specific API key, or skip it if auth already resolves from env/config
- write the config file and show success
Step 4: Update src/cli.tsx
Now that you have a configure UI, add a real subcommand:
ts
program
.command('configure')
.description('Configure AI model settings')
.action(() => {
render(<ConfigureComponent />);
});The stage snapshot also adds a basic --log-level option even though logging is still primitive.
The default command continues to render App.
Step 5: Update src/App.tsx to load config before creating the client
This is the most important behavioral change in the part.
Instead of constructing the client directly from process.env.OPENAI_API_KEY, the app now:
- calls
readConfig()insideuseEffect() - loads merged
protoagent.jsonc - checks the selected provider in the runtime registry
- resolves auth and base URL
- creates the
OpenAIclient only after the config is valid
Step 6: Fail early when config is missing
If no config exists, show an error in the UI like:
ts
Configuration not found. Please run `protoagent configure`.That might feel a little blunt, but it is the right behavior for this stage.
Current implementation note
The current main app has moved beyond this exact structure:
- config is flatter
- runtime providers are merged from built-ins plus
protoagent.jsonc - API key resolution can fall back to env vars or provider-level config
- setup can happen inline in the main UI
But this stage is still valuable because it introduces the right mental model: config sits between the CLI, provider selection, and client creation.
Verification
Run the standalone configure flow:
bash
npm run dev -- configureComplete the prompts, then start the app normally:
bash
npm run devIf it worked, you should see:
- a config file created under the local ProtoAgent data directory
- the app loading successfully without hardcoded env-only setup
- prompts being sent through the selected provider/model configuration
Resulting snapshot
At the end of this part, your project should match:
protoagent-tutorial-again-part-3
Pitfalls
- splitting the provider/model value incorrectly when reading the selection result
- writing config but never actually reading it on app startup
- mismatching provider IDs between
providers.tsand the app logic - trying to make this stage too elegant instead of matching the staged snapshot clearly
Core takeaway
Good config handling is not only about saving JSON. Even at this stage it becomes the glue between:
- the CLI
- provider selection
- API key resolution
- model selection
- client construction
That glue is what the rest of the runtime will depend on.