multi-forge 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- forge/__init__.py +3 -0
- forge/_extensions/agents/.gitkeep +0 -0
- forge/_extensions/commands/.gitkeep +0 -0
- forge/_extensions/skills/analyze/SKILL.md +87 -0
- forge/_extensions/skills/challenge/SKILL.md +91 -0
- forge/_extensions/skills/consensus/SKILL.md +120 -0
- forge/_extensions/skills/consensus/resources/code_consensus_evaluation.md +94 -0
- forge/_extensions/skills/consensus/resources/consensus_evaluation.md +70 -0
- forge/_extensions/skills/consensus/resources/synthesis.md +101 -0
- forge/_extensions/skills/debate/SKILL.md +116 -0
- forge/_extensions/skills/debate/resources/code_debate_evaluation.md +101 -0
- forge/_extensions/skills/debate/resources/debate_evaluation.md +90 -0
- forge/_extensions/skills/panel/SKILL.md +141 -0
- forge/_extensions/skills/panel/resources/synthesis.md +103 -0
- forge/_extensions/skills/qa/SKILL.md +704 -0
- forge/_extensions/skills/qa/resources/checklist/0-enable.md +78 -0
- forge/_extensions/skills/qa/resources/checklist/1-preflight.md +24 -0
- forge/_extensions/skills/qa/resources/checklist/10-resume.md +143 -0
- forge/_extensions/skills/qa/resources/checklist/11-config.md +150 -0
- forge/_extensions/skills/qa/resources/checklist/12-search.md +58 -0
- forge/_extensions/skills/qa/resources/checklist/13-guard.md +237 -0
- forge/_extensions/skills/qa/resources/checklist/14-workflow.md +305 -0
- forge/_extensions/skills/qa/resources/checklist/15-skills.md +155 -0
- forge/_extensions/skills/qa/resources/checklist/16-handoff.md +224 -0
- forge/_extensions/skills/qa/resources/checklist/17-info.md +50 -0
- forge/_extensions/skills/qa/resources/checklist/18-disable.md +84 -0
- forge/_extensions/skills/qa/resources/checklist/19-uninstall.md +146 -0
- forge/_extensions/skills/qa/resources/checklist/2-extensions.md +188 -0
- forge/_extensions/skills/qa/resources/checklist/20-cleanup.md +36 -0
- forge/_extensions/skills/qa/resources/checklist/3-auth.md +234 -0
- forge/_extensions/skills/qa/resources/checklist/4-proxy.md +481 -0
- forge/_extensions/skills/qa/resources/checklist/5-session.md +541 -0
- forge/_extensions/skills/qa/resources/checklist/6-hooks.md +275 -0
- forge/_extensions/skills/qa/resources/checklist/7-costs.md +309 -0
- forge/_extensions/skills/qa/resources/checklist/8-status-line.md +174 -0
- forge/_extensions/skills/qa/resources/checklist/9-direct-commands.md +146 -0
- forge/_extensions/skills/qa/resources/checklist.md +103 -0
- forge/_extensions/skills/qa/resources/report-template.md +62 -0
- forge/_extensions/skills/qa/scripts/start-container.sh +529 -0
- forge/_extensions/skills/qa/scripts/walkthrough-state.py +1137 -0
- forge/_extensions/skills/review/SKILL.md +125 -0
- forge/_extensions/skills/review/references/claude-4.6.md +474 -0
- forge/_extensions/skills/review/references/claude-4.7.md +710 -0
- forge/_extensions/skills/review/references/gemini-3.1.md +546 -0
- forge/_extensions/skills/review/references/gpt-5.5.md +490 -0
- forge/_extensions/skills/review/references/skills-writing-guide.md +1588 -0
- forge/_extensions/skills/review/resources/code-anthropic.md +160 -0
- forge/_extensions/skills/review/resources/code-gemini.md +184 -0
- forge/_extensions/skills/review/resources/code-openai.md +203 -0
- forge/_extensions/skills/review/resources/code.md +160 -0
- forge/_extensions/skills/review-docs/SKILL.md +121 -0
- forge/_extensions/skills/review-docs/resources/docs-anthropic.md +170 -0
- forge/_extensions/skills/review-docs/resources/docs-gemini.md +204 -0
- forge/_extensions/skills/review-docs/resources/docs-openai.md +231 -0
- forge/_extensions/skills/review-docs/resources/docs.md +170 -0
- forge/_extensions/skills/smoke-test/SKILL.md +27 -0
- forge/_extensions/skills/smoke-test/scripts/smoke-test.sh +118 -0
- forge/_extensions/skills/understand/SKILL.md +148 -0
- forge/_extensions/skills/understand/resources/code-anthropic.md +163 -0
- forge/_extensions/skills/understand/resources/code-gemini.md +194 -0
- forge/_extensions/skills/understand/resources/code-openai.md +181 -0
- forge/_extensions/skills/understand/resources/code.md +163 -0
- forge/_extensions/skills/understand/resources/docs-anthropic.md +177 -0
- forge/_extensions/skills/understand/resources/docs-gemini.md +202 -0
- forge/_extensions/skills/understand/resources/docs-openai.md +191 -0
- forge/_extensions/skills/understand/resources/docs.md +177 -0
- forge/_extensions/skills/walkthrough/SKILL.md +599 -0
- forge/_extensions/skills/walkthrough/resources/checklist.md +765 -0
- forge/_extensions/skills/walkthrough/scripts/run-in-repo.sh +118 -0
- forge/_extensions/skills/walkthrough/scripts/setup-test-repo.sh +198 -0
- forge/_extensions/skills/walkthrough/scripts/walkthrough-state.py +1137 -0
- forge/backend/__init__.py +174 -0
- forge/backend/adapters/__init__.py +38 -0
- forge/backend/adapters/litellm.py +158 -0
- forge/backend/creation.py +89 -0
- forge/backend/registry.py +178 -0
- forge/cli/__init__.py +16 -0
- forge/cli/auth.py +483 -0
- forge/cli/backend.py +298 -0
- forge/cli/claude.py +411 -0
- forge/cli/config_cmd.py +303 -0
- forge/cli/extensions.py +1001 -0
- forge/cli/gc.py +165 -0
- forge/cli/guard.py +1018 -0
- forge/cli/guards.py +106 -0
- forge/cli/handoff.py +110 -0
- forge/cli/hooks/__init__.py +36 -0
- forge/cli/hooks/_group.py +20 -0
- forge/cli/hooks/_helpers.py +149 -0
- forge/cli/hooks/commands.py +1677 -0
- forge/cli/hooks/direct_commands.py +1304 -0
- forge/cli/hooks/install.py +232 -0
- forge/cli/hooks/policy.py +151 -0
- forge/cli/hooks/read_hygiene.py +74 -0
- forge/cli/hooks/verification.py +370 -0
- forge/cli/logs.py +406 -0
- forge/cli/main.py +292 -0
- forge/cli/proxy.py +1821 -0
- forge/cli/proxy_costs.py +313 -0
- forge/cli/search.py +416 -0
- forge/cli/session.py +892 -0
- forge/cli/session_addendum.py +81 -0
- forge/cli/session_fork.py +750 -0
- forge/cli/session_handoff.py +141 -0
- forge/cli/session_lifecycle.py +2053 -0
- forge/cli/session_manage.py +1336 -0
- forge/cli/session_memory.py +201 -0
- forge/cli/status_line.py +1398 -0
- forge/cli/workflow.py +1964 -0
- forge/config/__init__.py +110 -0
- forge/config/dataclass_utils.py +88 -0
- forge/config/defaults/__init__.py +0 -0
- forge/config/defaults/backends/__init__.py +0 -0
- forge/config/defaults/backends/litellm.yaml +196 -0
- forge/config/defaults/templates/__init__.py +0 -0
- forge/config/defaults/templates/litellm-anthropic-local.yaml +33 -0
- forge/config/defaults/templates/litellm-anthropic.yaml +24 -0
- forge/config/defaults/templates/litellm-gemini-flash-local.yaml +37 -0
- forge/config/defaults/templates/litellm-gemini-local.yaml +32 -0
- forge/config/defaults/templates/litellm-gemini-test.yaml +34 -0
- forge/config/defaults/templates/litellm-gemini.yaml +21 -0
- forge/config/defaults/templates/litellm-openai-codex-local.yaml +36 -0
- forge/config/defaults/templates/litellm-openai-local.yaml +38 -0
- forge/config/defaults/templates/litellm-openai.yaml +28 -0
- forge/config/defaults/templates/openrouter-anthropic.yaml +23 -0
- forge/config/defaults/templates/openrouter-deepseek.yaml +26 -0
- forge/config/defaults/templates/openrouter-gemini-flash.yaml +26 -0
- forge/config/defaults/templates/openrouter-gemini.yaml +23 -0
- forge/config/defaults/templates/openrouter-glm.yaml +23 -0
- forge/config/defaults/templates/openrouter-kimi.yaml +30 -0
- forge/config/defaults/templates/openrouter-minimax.yaml +26 -0
- forge/config/defaults/templates/openrouter-openai-codex.yaml +23 -0
- forge/config/defaults/templates/openrouter-openai.yaml +28 -0
- forge/config/defaults/templates/openrouter-qwen.yaml +25 -0
- forge/config/loader.py +675 -0
- forge/config/schema.py +448 -0
- forge/core/__init__.py +5 -0
- forge/core/auth/__init__.py +67 -0
- forge/core/auth/capabilities.py +219 -0
- forge/core/auth/credentials_file.py +244 -0
- forge/core/auth/protocols.py +18 -0
- forge/core/auth/secrets.py +243 -0
- forge/core/auth/template_secrets.py +112 -0
- forge/core/data/__init__.py +5 -0
- forge/core/data/model_catalog.yaml +1522 -0
- forge/core/data/pricing.yaml +140 -0
- forge/core/data/system_prompt_addendums/__init__.py +0 -0
- forge/core/data/system_prompt_addendums/gemini.md +330 -0
- forge/core/data/system_prompt_addendums/openai.md +328 -0
- forge/core/llm/__init__.py +231 -0
- forge/core/llm/clients/__init__.py +14 -0
- forge/core/llm/clients/base.py +115 -0
- forge/core/llm/clients/litellm.py +619 -0
- forge/core/llm/clients/openai_compat.py +244 -0
- forge/core/llm/clients/openrouter.py +234 -0
- forge/core/llm/credentials.py +439 -0
- forge/core/llm/detection.py +86 -0
- forge/core/llm/errors.py +44 -0
- forge/core/llm/protocols.py +80 -0
- forge/core/llm/types.py +176 -0
- forge/core/logging.py +146 -0
- forge/core/models/__init__.py +91 -0
- forge/core/models/catalog.py +467 -0
- forge/core/models/pricing.py +165 -0
- forge/core/models/types.py +167 -0
- forge/core/naming.py +212 -0
- forge/core/ops/__init__.py +73 -0
- forge/core/ops/context.py +141 -0
- forge/core/ops/gc.py +802 -0
- forge/core/ops/proxy.py +146 -0
- forge/core/ops/resolution.py +135 -0
- forge/core/ops/session.py +344 -0
- forge/core/ops/session_context.py +548 -0
- forge/core/paths.py +38 -0
- forge/core/process.py +54 -0
- forge/core/reactive/__init__.py +38 -0
- forge/core/reactive/cost_tracking.py +300 -0
- forge/core/reactive/env.py +180 -0
- forge/core/reactive/proxy.py +78 -0
- forge/core/reactive/routing.py +622 -0
- forge/core/reactive/session_runner.py +185 -0
- forge/core/reactive/structured_output.py +62 -0
- forge/core/reactive/tagger.py +94 -0
- forge/core/reactive/throttle.py +132 -0
- forge/core/state/__init__.py +59 -0
- forge/core/state/exceptions.py +59 -0
- forge/core/state/io.py +140 -0
- forge/core/state/lock.py +99 -0
- forge/core/state/timestamps.py +60 -0
- forge/core/transcript.py +78 -0
- forge/core/typing_helpers.py +24 -0
- forge/core/workqueue/__init__.py +67 -0
- forge/core/workqueue/queue.py +552 -0
- forge/core/workqueue/types.py +63 -0
- forge/guard/__init__.py +26 -0
- forge/guard/deterministic/__init__.py +26 -0
- forge/guard/deterministic/base.py +158 -0
- forge/guard/deterministic/coding_standards.py +256 -0
- forge/guard/deterministic/registry.py +148 -0
- forge/guard/deterministic/tdd.py +171 -0
- forge/guard/engine.py +216 -0
- forge/guard/protocols.py +91 -0
- forge/guard/queries.py +96 -0
- forge/guard/semantic/__init__.py +34 -0
- forge/guard/semantic/promotion.py +18 -0
- forge/guard/semantic/supervisor.py +813 -0
- forge/guard/semantic/verdict.py +183 -0
- forge/guard/store.py +124 -0
- forge/guard/team/__init__.py +6 -0
- forge/guard/team/config.py +24 -0
- forge/guard/team/handlers.py +209 -0
- forge/guard/team/prompts.py +41 -0
- forge/guard/types.py +125 -0
- forge/guard/workflow/__init__.py +17 -0
- forge/guard/workflow/branches.py +67 -0
- forge/guard/workflow/config.py +63 -0
- forge/guard/workflow/divergence.py +113 -0
- forge/guard/workflow/policy.py +87 -0
- forge/guard/workflow/stages.py +205 -0
- forge/install/__init__.py +55 -0
- forge/install/cli.py +281 -0
- forge/install/exceptions.py +163 -0
- forge/install/hooks.py +109 -0
- forge/install/installer.py +1037 -0
- forge/install/models.py +321 -0
- forge/install/preset.py +272 -0
- forge/install/settings_merge.py +831 -0
- forge/install/tracking.py +238 -0
- forge/install/version.py +141 -0
- forge/proxy/__init__.py +0 -0
- forge/proxy/base_client.py +181 -0
- forge/proxy/client_adapter.py +476 -0
- forge/proxy/client_factory.py +531 -0
- forge/proxy/converters.py +1206 -0
- forge/proxy/cost_logger.py +132 -0
- forge/proxy/cost_tracker.py +242 -0
- forge/proxy/data_models.py +338 -0
- forge/proxy/error_hints.py +92 -0
- forge/proxy/metrics.py +222 -0
- forge/proxy/model_spec.py +158 -0
- forge/proxy/proxies.py +333 -0
- forge/proxy/proxy_identity.py +134 -0
- forge/proxy/proxy_orchestrator.py +1018 -0
- forge/proxy/proxy_startup.py +54 -0
- forge/proxy/server.py +1561 -0
- forge/proxy/utils.py +537 -0
- forge/review/__init__.py +6 -0
- forge/review/adversarial.py +111 -0
- forge/review/consensus.py +236 -0
- forge/review/engine.py +356 -0
- forge/review/models.py +437 -0
- forge/review/resources/__init__.py +5 -0
- forge/review/resources/codereview-performance.md +85 -0
- forge/review/resources/codereview-quick.md +75 -0
- forge/review/resources/codereview-security.md +92 -0
- forge/review/resources/codereview.md +85 -0
- forge/review/resources/docreview-quick.md +75 -0
- forge/review/resources/docreview.md +86 -0
- forge/review/resources/thinkdeep.md +89 -0
- forge/review/routing.py +368 -0
- forge/review/synthesis.py +73 -0
- forge/runtime_config.py +438 -0
- forge/search/__init__.py +55 -0
- forge/search/bm25_store.py +264 -0
- forge/search/content_store.py +197 -0
- forge/search/engine.py +352 -0
- forge/search/exceptions.py +51 -0
- forge/search/extractor.py +234 -0
- forge/search/index_state.py +295 -0
- forge/search/store.py +215 -0
- forge/search/tokenizer.py +24 -0
- forge/session/__init__.py +130 -0
- forge/session/active.py +339 -0
- forge/session/artifacts.py +202 -0
- forge/session/claude/__init__.py +50 -0
- forge/session/claude/cleanup.py +105 -0
- forge/session/claude/invoke.py +236 -0
- forge/session/claude/paths.py +200 -0
- forge/session/cleanup.py +216 -0
- forge/session/config.py +34 -0
- forge/session/direct_model.py +107 -0
- forge/session/effective.py +169 -0
- forge/session/exceptions.py +255 -0
- forge/session/handoff.py +881 -0
- forge/session/handoff_agent.py +544 -0
- forge/session/hooks/__init__.py +35 -0
- forge/session/hooks/models.py +73 -0
- forge/session/hooks/session_start.py +507 -0
- forge/session/identity.py +84 -0
- forge/session/index.py +553 -0
- forge/session/manager.py +1506 -0
- forge/session/models.py +572 -0
- forge/session/overrides.py +344 -0
- forge/session/plan_resolution.py +286 -0
- forge/session/prev_sessions.py +128 -0
- forge/session/store.py +431 -0
- forge/session/validation.py +47 -0
- forge/session/worktree/__init__.py +65 -0
- forge/session/worktree/cleanup.py +262 -0
- forge/session/worktree/config_copy.py +203 -0
- forge/session/worktree/create.py +332 -0
- forge/sidecar/__init__.py +29 -0
- forge/sidecar/container.py +161 -0
- forge/sidecar/docker.py +86 -0
- forge/sidecar/secrets.py +19 -0
- multi_forge-0.2.0.dist-info/METADATA +242 -0
- multi_forge-0.2.0.dist-info/RECORD +311 -0
- multi_forge-0.2.0.dist-info/WHEEL +4 -0
- multi_forge-0.2.0.dist-info/entry_points.txt +2 -0
- multi_forge-0.2.0.dist-info/licenses/LICENSE +203 -0
- multi_forge-0.2.0.dist-info/licenses/NOTICE +14 -0
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<!-- prereq: 0.3 -->
|
|
2
|
+
|
|
3
|
+
## 3. Authentication (`forge authentication`)
|
|
4
|
+
|
|
5
|
+
Tests credential storage/resolution in `$FORGE_HOME/credentials.yaml` (named profiles).
|
|
6
|
+
|
|
7
|
+
> **Note:** These steps test Forge's credential management UI only. The keys stored via `forge authentication login` are
|
|
8
|
+
> NOT used by the proxy or Claude Code. The proxy gets its backend keys from environment variables injected at container
|
|
9
|
+
> start (`/etc/profile.d/forge-qa.sh`). You can use placeholder values (e.g., `sk-ant-manual-test-12345`) for all login
|
|
10
|
+
> prompts.
|
|
11
|
+
|
|
12
|
+
### 3.1 Login — Store Credentials
|
|
13
|
+
|
|
14
|
+
<!-- human:guided -->
|
|
15
|
+
|
|
16
|
+
In a **shell inside the QA container** (`docker exec -it $CONTAINER bash -l` — the container name is printed by
|
|
17
|
+
`start-container.sh`, usually `forge-qa`), run the command below. Enter a test API key when prompted (e.g.,
|
|
18
|
+
`sk-ant-manual-test-12345`). Input will be hidden.
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
# Store credentials for a single credential
|
|
22
|
+
forge authentication login -c anthropic-api
|
|
23
|
+
|
|
24
|
+
# Expected: prompts for ANTHROPIC_API_KEY (input hidden)
|
|
25
|
+
# Enter a test key, e.g.: sk-ant-manual-test-12345
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
- [ ] Prompts for API key with hidden input (characters not echoed)
|
|
29
|
+
- [ ] Shows "Credentials saved to $FORGE_HOME/credentials.yaml"
|
|
30
|
+
- [ ] File created with 0o600 permissions (`ls -la $FORGE_HOME/credentials.yaml`)
|
|
31
|
+
|
|
32
|
+
### 3.2 Login — Named Profiles
|
|
33
|
+
|
|
34
|
+
<!-- human:guided -->
|
|
35
|
+
|
|
36
|
+
In the **container shell**, store credentials under a named profile. Enter a different test key (e.g.,
|
|
37
|
+
`sk-ant-work-key-99999`).
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
# Store credentials in a named profile
|
|
41
|
+
forge authentication login -c anthropic-api --profile work
|
|
42
|
+
# Enter a different key, e.g.: sk-ant-work-key-99999
|
|
43
|
+
|
|
44
|
+
# Verify both profiles exist
|
|
45
|
+
forge authentication profiles
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
- [ ] `work` profile created separately from `default`
|
|
49
|
+
- [ ] `forge authentication profiles` shows both profiles with key counts
|
|
50
|
+
- [ ] Active profile marked with "← active"
|
|
51
|
+
|
|
52
|
+
### 3.3 Login — Keep Existing Values
|
|
53
|
+
|
|
54
|
+
<!-- human:guided -->
|
|
55
|
+
|
|
56
|
+
In the **container shell**, re-run login for the same credential. The existing value appears as a masked default (e.g.,
|
|
57
|
+
`ANTHROPIC_API_KEY [sk-a…5678]`). Press Enter to keep it.
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
# Re-run login for same credential — existing value shown as masked default
|
|
61
|
+
forge authentication login -c anthropic-api
|
|
62
|
+
|
|
63
|
+
# Expected: shows existing value like "ANTHROPIC_API_KEY [sk-a…5678]"
|
|
64
|
+
# Press Enter to keep existing value
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- [ ] Existing value shown as masked default (first 4 + last 4 chars)
|
|
68
|
+
- [ ] Pressing Enter preserves the existing value (not overwritten)
|
|
69
|
+
|
|
70
|
+
### 3.4 Status — Dual-View Output
|
|
71
|
+
|
|
72
|
+
<!-- auto -->
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Check credential status
|
|
76
|
+
forge authentication status
|
|
77
|
+
|
|
78
|
+
# Expected output has two sections:
|
|
79
|
+
# Configured capabilities:
|
|
80
|
+
# * anthropic-api ... (file:default)
|
|
81
|
+
#
|
|
82
|
+
# Credential details:
|
|
83
|
+
# anthropic-api
|
|
84
|
+
# * ANTHROPIC_API_KEY = sk-a…5678 (file:default)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
- [ ] Shows "Configured capabilities:" section with configured credentials
|
|
88
|
+
- [ ] Shows "Not configured (set up if needed):" section for unconfigured credentials
|
|
89
|
+
- [ ] Shows "Credential details:" section with per-variable source attribution
|
|
90
|
+
- [ ] Values are masked (never shown in full)
|
|
91
|
+
- [ ] All 5 credentials displayed (openrouter, anthropic-api, openai-api, gemini-api, litellm-remote)
|
|
92
|
+
- [ ] Unconfigured credentials show "not configured" (not "MISSING")
|
|
93
|
+
|
|
94
|
+
### 3.5 Status — Env Overrides File
|
|
95
|
+
|
|
96
|
+
<!-- auto -->
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
# Set env var that also exists in file
|
|
100
|
+
export ANTHROPIC_API_KEY=sk-ant-from-env-override
|
|
101
|
+
forge authentication status
|
|
102
|
+
|
|
103
|
+
# Expected: shows (env) source, not (file:default)
|
|
104
|
+
unset ANTHROPIC_API_KEY
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
- [ ] Env var takes precedence — shown as `(env)` not `(file:default)`
|
|
108
|
+
- [ ] After unsetting env var, status shows `(file:default)` again
|
|
109
|
+
|
|
110
|
+
### 3.6 Status — Named Profile
|
|
111
|
+
|
|
112
|
+
<!-- auto -->
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Check status for a specific profile
|
|
116
|
+
forge authentication status --profile work
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
- [ ] Shows `(file:work)` for keys stored in work profile
|
|
120
|
+
- [ ] Keys not in work profile shown as "not configured" (even if in default)
|
|
121
|
+
|
|
122
|
+
### 3.7 Profiles — List and Active Marker
|
|
123
|
+
|
|
124
|
+
<!-- auto -->
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
# List all profiles
|
|
128
|
+
forge authentication profiles
|
|
129
|
+
|
|
130
|
+
# Change active profile via env var
|
|
131
|
+
FORGE_PROFILE=work forge authentication profiles
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
- [ ] Shows profile names with key counts
|
|
135
|
+
- [ ] Default profile marked "← active" by default
|
|
136
|
+
- [ ] `FORGE_PROFILE=work` changes active marker to work
|
|
137
|
+
|
|
138
|
+
### 3.8 Logout — Remove Profile
|
|
139
|
+
|
|
140
|
+
<!-- auto -->
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Remove a profile (with confirmation)
|
|
144
|
+
printf 'y\n' | forge authentication logout --profile work
|
|
145
|
+
|
|
146
|
+
# Verify removed
|
|
147
|
+
forge authentication profiles
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
- [ ] Confirmation prompt shown (unless `-y` used)
|
|
151
|
+
- [ ] Profile removed from credentials file
|
|
152
|
+
- [ ] Other profiles unaffected
|
|
153
|
+
|
|
154
|
+
### 3.9 Logout — Skip Confirmation
|
|
155
|
+
|
|
156
|
+
<!-- human:guided -->
|
|
157
|
+
|
|
158
|
+
In the **container shell**, create a temp profile (enter any test key when prompted), then remove it with `-y`.
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
# Re-create and remove without confirmation
|
|
162
|
+
forge authentication login -c anthropic-api --profile temp
|
|
163
|
+
# Enter any test key
|
|
164
|
+
|
|
165
|
+
forge authentication logout --profile temp -y
|
|
166
|
+
forge authentication profiles
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- [ ] `-y` flag skips confirmation
|
|
170
|
+
- [ ] Profile removed immediately
|
|
171
|
+
|
|
172
|
+
### 3.10 Credential File Security
|
|
173
|
+
|
|
174
|
+
<!-- auto -->
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
# Check file permissions
|
|
178
|
+
ls -la $FORGE_HOME/credentials.yaml
|
|
179
|
+
# Expected: -rw------- (0o600)
|
|
180
|
+
|
|
181
|
+
# Check file content structure without exposing secrets
|
|
182
|
+
python3 -c "
|
|
183
|
+
import yaml, sys
|
|
184
|
+
with open('$FORGE_HOME/credentials.yaml') as f:
|
|
185
|
+
data = yaml.safe_load(f)
|
|
186
|
+
print('version:', data.get('version'))
|
|
187
|
+
print('profiles:', list(data.get('profiles', {}).keys()))
|
|
188
|
+
for name, profile in data.get('profiles', {}).items():
|
|
189
|
+
masked = {k: v[:6] + '...' if isinstance(v, str) and len(v) > 6 else v for k, v in profile.items()}
|
|
190
|
+
print(f' {name}: {masked}')
|
|
191
|
+
"
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
- [ ] Permissions are 0o600 (owner read/write only)
|
|
195
|
+
- [ ] File has `version: 1` field
|
|
196
|
+
- [ ] Profile names map to flat key-value pairs (values masked)
|
|
197
|
+
|
|
198
|
+
### 3.11 Credential Resolution in CredentialManager
|
|
199
|
+
|
|
200
|
+
<!-- auto -->
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
# Verify file-based credentials are actually used by the system
|
|
204
|
+
# Unset env var, rely on file-based credential
|
|
205
|
+
unset ANTHROPIC_API_KEY
|
|
206
|
+
|
|
207
|
+
# If you have a valid key stored via forge authentication login:
|
|
208
|
+
# Starting a session or proxy that needs ANTHROPIC_API_KEY should work
|
|
209
|
+
# without the env var set (it reads from $FORGE_HOME/credentials.yaml)
|
|
210
|
+
forge authentication status --profile default
|
|
211
|
+
# Should show: * ANTHROPIC_API_KEY = sk-a…xxxx (file:default)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
- [ ] Credential available via file when env var is unset
|
|
215
|
+
- [ ] `forge authentication status` confirms file source
|
|
216
|
+
|
|
217
|
+
### 3.12 Retired Credential Names
|
|
218
|
+
|
|
219
|
+
<!-- auto -->
|
|
220
|
+
|
|
221
|
+
```bash
|
|
222
|
+
# Old 'anthropic' name should produce migration guidance
|
|
223
|
+
forge authentication login -c anthropic 2>&1 || true
|
|
224
|
+
# Expected: exit 1, yellow message mentioning 'anthropic-api'
|
|
225
|
+
|
|
226
|
+
# Old 'litellm-local' name should explain it's not a credential
|
|
227
|
+
forge authentication login -c litellm-local 2>&1 || true
|
|
228
|
+
# Expected: exit 1, message mentioning gemini-api, openai-api, anthropic-api
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
- [ ] `anthropic` produces clear migration message pointing to `anthropic-api`
|
|
232
|
+
- [ ] `litellm-local` explains it's not a credential and lists provider alternatives
|
|
233
|
+
|
|
234
|
+
---
|
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
<!-- prereq: 0.3 -->
|
|
2
|
+
|
|
3
|
+
## 4. Proxy Management
|
|
4
|
+
|
|
5
|
+
Default QA runs this section through the OpenRouter provider profile. It requires the selected provider credentials
|
|
6
|
+
(`OPENROUTER_API_KEY` by default), not remote LiteLLM infrastructure unless QA was started with
|
|
7
|
+
`--provider-profile remote-litellm`. LiteLLM template list/show checks below are metadata-only.
|
|
8
|
+
|
|
9
|
+
### 4.1 List Proxies and Templates
|
|
10
|
+
|
|
11
|
+
<!-- auto -->
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# List existing proxies (none yet)
|
|
15
|
+
forge proxy list
|
|
16
|
+
|
|
17
|
+
# Expected: "No proxies found." + tip to run 'forge proxy template list'
|
|
18
|
+
# Templates are listed via the template subcommand, not inline in proxy list.
|
|
19
|
+
|
|
20
|
+
# List available templates
|
|
21
|
+
forge proxy template list
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
- [ ] `forge proxy list` shows "No proxies found." when none exist
|
|
25
|
+
- [ ] `forge proxy list` shows tip to run `forge proxy template list`
|
|
26
|
+
- [ ] `forge proxy template list` shows available templates (18 user-facing: litellm-anthropic, litellm-anthropic-local,
|
|
27
|
+
litellm-gemini, litellm-gemini-flash-local, litellm-gemini-local, litellm-openai, litellm-openai-codex-local,
|
|
28
|
+
litellm-openai-local, openrouter-anthropic, openrouter-deepseek, openrouter-gemini, openrouter-gemini-flash,
|
|
29
|
+
openrouter-glm, openrouter-kimi, openrouter-minimax, openrouter-openai, openrouter-openai-codex, openrouter-qwen)
|
|
30
|
+
- [ ] Internal test-only templates (e.g., litellm-gemini-test) are hidden from the default list
|
|
31
|
+
|
|
32
|
+
### 4.2 Create a Proxy
|
|
33
|
+
|
|
34
|
+
<!-- auto -->
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Clean up from previous runs
|
|
38
|
+
forge proxy delete "$FORGE_QA_GEMINI_PROXY" --force 2>/dev/null || true
|
|
39
|
+
forge proxy delete "$FORGE_QA_OPENAI_PROXY" --force 2>/dev/null || true
|
|
40
|
+
forge proxy delete "$FORGE_QA_ANTHROPIC_PROXY" --force 2>/dev/null || true
|
|
41
|
+
forge proxy delete openrouter-gemini --force 2>/dev/null || true
|
|
42
|
+
forge proxy delete openrouter-openai --force 2>/dev/null || true
|
|
43
|
+
forge proxy delete openrouter-deepseek --force 2>/dev/null || true
|
|
44
|
+
forge proxy delete openrouter-minimax --force 2>/dev/null || true
|
|
45
|
+
forge proxy delete test-proxy-nostart --force 2>/dev/null || true
|
|
46
|
+
|
|
47
|
+
# Create named role proxies used by downstream session/review steps.
|
|
48
|
+
forge proxy create "$FORGE_QA_GEMINI_TEMPLATE" --name "$FORGE_QA_GEMINI_PROXY"
|
|
49
|
+
|
|
50
|
+
# Create a named review proxy with per-tier overrides
|
|
51
|
+
forge proxy create "$FORGE_QA_OPENAI_TEMPLATE" --name "$FORGE_QA_OPENAI_PROXY" --opus-reasoning high
|
|
52
|
+
|
|
53
|
+
# Create workflow-default aliases so section 14 exercises production default proxy IDs.
|
|
54
|
+
# In remote-litellm profile these names intentionally point at remote LiteLLM-backed proxies.
|
|
55
|
+
forge proxy create "$FORGE_QA_GEMINI_TEMPLATE" --no-start --name openrouter-gemini
|
|
56
|
+
forge proxy create "$FORGE_QA_OPENAI_TEMPLATE" --no-start --name openrouter-openai
|
|
57
|
+
|
|
58
|
+
# Workflow proxies for cheap models (openrouter profile only).
|
|
59
|
+
# Started directly with canonical names so models.py proxy lookup matches the running port.
|
|
60
|
+
if [ -n "${FORGE_QA_DEEPSEEK_TEMPLATE:-}" ]; then
|
|
61
|
+
forge proxy create "$FORGE_QA_DEEPSEEK_TEMPLATE" --name openrouter-deepseek
|
|
62
|
+
fi
|
|
63
|
+
if [ -n "${FORGE_QA_MINIMAX_TEMPLATE:-}" ]; then
|
|
64
|
+
forge proxy create "$FORGE_QA_MINIMAX_TEMPLATE" --name openrouter-minimax
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
# Create config only (don't start the server)
|
|
68
|
+
forge proxy create "$FORGE_QA_OPENAI_TEMPLATE" --no-start --name test-proxy-nostart
|
|
69
|
+
|
|
70
|
+
# List proxies again
|
|
71
|
+
forge proxy list
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
- [ ] Named role proxies from `$FORGE_QA_*_TEMPLATE` created successfully (or note if skipped)
|
|
75
|
+
- [ ] Named role proxies, workflow aliases, and `test-proxy-nostart` appear in the list with expected base_url/port
|
|
76
|
+
information
|
|
77
|
+
- [ ] Per-tier overrides applied to `$FORGE_QA_OPENAI_PROXY`
|
|
78
|
+
- [ ] Workflow proxies (`openrouter-deepseek`, `openrouter-minimax`) started for openrouter profile (or skipped for
|
|
79
|
+
remote-litellm)
|
|
80
|
+
|
|
81
|
+
### 4.3 Show Proxy Details
|
|
82
|
+
|
|
83
|
+
<!-- prereq: 4.2 -->
|
|
84
|
+
|
|
85
|
+
<!-- auto -->
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Show details of a specific proxy (created in 4.2)
|
|
89
|
+
forge proxy show test-proxy-nostart
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
- [ ] Shows template, base_url, tier mappings
|
|
93
|
+
- [ ] Shows proxy configuration YAML (port, tiers, provider settings)
|
|
94
|
+
|
|
95
|
+
### 4.4 Proxy Edit and Validate
|
|
96
|
+
|
|
97
|
+
<!-- prereq: 4.2 -->
|
|
98
|
+
|
|
99
|
+
<!-- human:guided -->
|
|
100
|
+
|
|
101
|
+
In the **container shell**, run these commands to view, edit, validate, and delete a proxy. The `edit` command opens
|
|
102
|
+
`$EDITOR` — verify it launches.
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
# View proxy config
|
|
106
|
+
forge proxy show <proxy_id>
|
|
107
|
+
|
|
108
|
+
# Edit proxy config (opens in $EDITOR)
|
|
109
|
+
forge proxy edit <proxy_id>
|
|
110
|
+
|
|
111
|
+
# Validate proxy config
|
|
112
|
+
forge proxy validate <proxy_id>
|
|
113
|
+
|
|
114
|
+
# Delete a proxy
|
|
115
|
+
forge proxy delete <proxy_id>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
- [ ] `show` displays full proxy configuration
|
|
119
|
+
- [ ] `edit` opens proxy.yaml in editor
|
|
120
|
+
- [ ] `validate` reports config health
|
|
121
|
+
- [ ] `delete` removes proxy and cleans up registry
|
|
122
|
+
|
|
123
|
+
### 4.5 Proxy Clean
|
|
124
|
+
|
|
125
|
+
<!-- auto -->
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
# Clean up stale proxies (dead processes)
|
|
129
|
+
forge proxy clean
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
- [ ] Clean removes stale entries (or reports none found)
|
|
133
|
+
|
|
134
|
+
### 4.6 Launch Session with Host Proxy
|
|
135
|
+
|
|
136
|
+
<!-- prereq: 2.4, 4.2 -->
|
|
137
|
+
|
|
138
|
+
<!-- requires: api_key -->
|
|
139
|
+
|
|
140
|
+
<!-- human:guided -->
|
|
141
|
+
|
|
142
|
+
In the **container shell**, create a session bound to a proxy, then launch Claude through the proxy.
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
# Clean up from previous runs
|
|
146
|
+
forge session delete proxy-session --force 2>/dev/null || true
|
|
147
|
+
|
|
148
|
+
# Create a session bound to the proxy created in 4.2 (accepts proxy_id or template name)
|
|
149
|
+
forge session start proxy-session --proxy "$FORGE_QA_OPENAI_PROXY" --no-launch
|
|
150
|
+
|
|
151
|
+
# Verify session recorded proxy identity
|
|
152
|
+
cat .forge/sessions/proxy-session/forge.session.json | jq '.intent.proxy'
|
|
153
|
+
# Should show template and base_url fields
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- [ ] `--proxy` binds session to the named proxy
|
|
157
|
+
- [ ] Session manifest `.intent.proxy` shows template and base_url
|
|
158
|
+
|
|
159
|
+
Now launch Claude through the named proxy. This opens an interactive Claude session — exit with Ctrl-C or `/exit` when
|
|
160
|
+
done verifying.
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
# Launch Claude through the running proxy created in 4.2
|
|
164
|
+
forge claude start --proxy "$FORGE_QA_OPENAI_PROXY" -- --debug
|
|
165
|
+
# Claude should start with ANTHROPIC_BASE_URL pointing to the proxy
|
|
166
|
+
# Verify by checking the status line or running: echo $ANTHROPIC_BASE_URL inside Claude
|
|
167
|
+
# Exit Claude when done (Ctrl-C or /exit)
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- [ ] `forge claude start --proxy` starts Claude routed through the named proxy
|
|
171
|
+
- [ ] Status line shows proxy info (template, tier mappings) when running with proxy
|
|
172
|
+
|
|
173
|
+
### 4.7 Live % Commands in Proxy Session
|
|
174
|
+
|
|
175
|
+
<!-- prereq: 2.4, 4.2 -->
|
|
176
|
+
|
|
177
|
+
<!-- requires: api_key -->
|
|
178
|
+
|
|
179
|
+
<!-- human:guided -->
|
|
180
|
+
|
|
181
|
+
Now launch Claude (or reuse the session from 4.6):
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
forge claude start --proxy "$FORGE_QA_OPENAI_PROXY" -- --debug
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
In the **live Claude session**, type these prompts:
|
|
188
|
+
|
|
189
|
+
```
|
|
190
|
+
%help
|
|
191
|
+
%session list
|
|
192
|
+
%proxy list
|
|
193
|
+
%proxy show qa-openai
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
- [ ] `%help` returns help text listing available `%` commands
|
|
197
|
+
- [ ] `%session list` shows sessions (including proxy-session)
|
|
198
|
+
- [ ] `%proxy list` shows proxies from inside the session
|
|
199
|
+
- [ ] `%proxy show` displays proxy details (template, tier mappings)
|
|
200
|
+
- [ ] Commands are intercepted by `UserPromptSubmit` hook (not passed to Claude as prompts)
|
|
201
|
+
|
|
202
|
+
Exit the Claude session when done.
|
|
203
|
+
|
|
204
|
+
### 4.8 Proxy Delete UX (Confirmation + Smart-Pointer Semantics)
|
|
205
|
+
|
|
206
|
+
<!-- prereq: 4.2, 4.6 -->
|
|
207
|
+
|
|
208
|
+
<!-- human:guided -->
|
|
209
|
+
|
|
210
|
+
Test that `forge proxy delete` requires confirmation, shows the related shared-port items, and uses smart-pointer
|
|
211
|
+
semantics (only kills the server when deleting the last registry entry for that port).
|
|
212
|
+
|
|
213
|
+
In the **container shell**:
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
# Clean up from previous runs
|
|
217
|
+
forge proxy delete delete-test-proxy --force 2>/dev/null || true
|
|
218
|
+
|
|
219
|
+
# Create an alias on the same shared port as the QA OpenAI proxy
|
|
220
|
+
forge proxy create "$FORGE_QA_OPENAI_TEMPLATE" --no-start --name delete-test-proxy
|
|
221
|
+
|
|
222
|
+
# Try to delete the alias -- should prompt for confirmation and list the related proxy entry
|
|
223
|
+
forge proxy delete delete-test-proxy
|
|
224
|
+
# Choose N to cancel
|
|
225
|
+
# Expected:
|
|
226
|
+
# - confirmation prompt appears
|
|
227
|
+
# - related proxies on the same port are listed (including qa-openai)
|
|
228
|
+
# - no false warning about proxy-session/proxy-session-url just because they share port 8085
|
|
229
|
+
|
|
230
|
+
# Verify alias still exists after cancelling
|
|
231
|
+
forge proxy list
|
|
232
|
+
# Expected: delete-test-proxy still listed
|
|
233
|
+
|
|
234
|
+
# Now confirm deletion of the alias
|
|
235
|
+
forge proxy delete delete-test-proxy
|
|
236
|
+
# Choose y to confirm
|
|
237
|
+
# Expected:
|
|
238
|
+
# - "Deleted proxy 'delete-test-proxy'"
|
|
239
|
+
# - shared server references are kept alive via qa-openai
|
|
240
|
+
|
|
241
|
+
# Verify alias gone but qa-openai still present
|
|
242
|
+
forge proxy list
|
|
243
|
+
|
|
244
|
+
# Finally test deleting the primary QA proxy while shared-port aliases remain
|
|
245
|
+
forge proxy delete "$FORGE_QA_OPENAI_PROXY"
|
|
246
|
+
# Choose N to cancel
|
|
247
|
+
# Expected:
|
|
248
|
+
# - warning lists sessions that reference qa-openai (for example proxy-session / proxy-session-url)
|
|
249
|
+
# - prompt makes clear other proxies share this port
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- [ ] `forge proxy delete` prompts for confirmation (not auto-deleted)
|
|
253
|
+
- [ ] Deleting a non-terminal alias lists the related proxy entries sharing that port
|
|
254
|
+
- [ ] Choosing N cancels the delete; alias still in `forge proxy list`
|
|
255
|
+
- [ ] Choosing y deletes the alias while keeping the shared server alive
|
|
256
|
+
- [ ] Deleting the primary QA proxy lists related sessions and same-port aliases
|
|
257
|
+
- [ ] No false warnings about sessions when deleting a non-terminal alias that merely shares the same port
|
|
258
|
+
|
|
259
|
+
### 4.9 Template Management
|
|
260
|
+
|
|
261
|
+
<!-- auto -->
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
# List available templates
|
|
265
|
+
forge proxy template list
|
|
266
|
+
|
|
267
|
+
# Show a template
|
|
268
|
+
forge proxy template show litellm-openai-local
|
|
269
|
+
|
|
270
|
+
# Raw YAML output (no syntax highlighting)
|
|
271
|
+
forge proxy template show litellm-openai-local --raw
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- [ ] `forge proxy template list` shows all templates with source labels (built-in / customized)
|
|
275
|
+
- [ ] `forge proxy template show` displays template YAML
|
|
276
|
+
- [ ] `--raw` outputs plain YAML
|
|
277
|
+
|
|
278
|
+
### 4.10 Show Raw YAML for a Proxy Instance
|
|
279
|
+
|
|
280
|
+
<!-- prereq: 4.2 -->
|
|
281
|
+
|
|
282
|
+
<!-- auto -->
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
# Show raw YAML for an existing proxy instance (created earlier)
|
|
286
|
+
forge proxy show test-proxy-nostart --raw
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
- [ ] Proxy instance YAML printed (no syntax highlighting)
|
|
290
|
+
- [ ] YAML includes the expected template/provider fields
|
|
291
|
+
|
|
292
|
+
### 4.11 Set and Validate Proxy Config (No Editor)
|
|
293
|
+
|
|
294
|
+
<!-- prereq: 4.2 -->
|
|
295
|
+
|
|
296
|
+
<!-- auto -->
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
# Mutate a single value via CLI (no interactive editor)
|
|
300
|
+
forge proxy set test-proxy-nostart default_tier=opus
|
|
301
|
+
|
|
302
|
+
# Validate after mutation
|
|
303
|
+
forge proxy validate test-proxy-nostart
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
- [ ] `forge proxy set` succeeds
|
|
307
|
+
- [ ] `forge proxy validate` reports config is valid
|
|
308
|
+
|
|
309
|
+
### 4.12 Stop a Non-Running Proxy (Shared-Port Semantics)
|
|
310
|
+
|
|
311
|
+
<!-- prereq: 4.2 -->
|
|
312
|
+
|
|
313
|
+
<!-- auto -->
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
# test-proxy-nostart shares a port with the running QA OpenAI proxy.
|
|
317
|
+
# Smart-pointer semantics prevent stopping the shared server without --force.
|
|
318
|
+
forge proxy stop test-proxy-nostart 2>&1 || true
|
|
319
|
+
|
|
320
|
+
# Verify: error about shared port, not a silent no-op
|
|
321
|
+
forge proxy stop test-proxy-nostart 2>&1; echo "EXIT=$?"
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
- [ ] Command refuses to stop: reports other proxies share the port
|
|
325
|
+
- [ ] Exit code is non-zero (shared-port conflict)
|
|
326
|
+
|
|
327
|
+
### 4.13 Proxy Metrics (Running Proxy)
|
|
328
|
+
|
|
329
|
+
<!-- prereq: 4.2 -->
|
|
330
|
+
|
|
331
|
+
<!-- auto -->
|
|
332
|
+
|
|
333
|
+
```bash
|
|
334
|
+
# Metrics for a running proxy (QA Gemini proxy created in 4.2)
|
|
335
|
+
forge proxy metrics "$FORGE_QA_GEMINI_PROXY"
|
|
336
|
+
|
|
337
|
+
# JSON output
|
|
338
|
+
forge proxy metrics "$FORGE_QA_GEMINI_PROXY" --json
|
|
339
|
+
|
|
340
|
+
# All proxies
|
|
341
|
+
forge proxy metrics --all
|
|
342
|
+
|
|
343
|
+
# All proxies JSON (must be a single valid JSON object)
|
|
344
|
+
forge proxy metrics --all --json
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
- [ ] `forge proxy metrics` displays request counts, token totals, per-tier breakdown
|
|
348
|
+
- [ ] Per-tier breakdown includes avg latency
|
|
349
|
+
- [ ] `--json` outputs valid parseable JSON
|
|
350
|
+
- [ ] `--all --json` outputs a single valid JSON object (not one per proxy)
|
|
351
|
+
- [ ] Unreachable proxies show `null` in `--all --json` output
|
|
352
|
+
|
|
353
|
+
### 4.14 Proxy Metrics (Not Found / Shared-Port)
|
|
354
|
+
|
|
355
|
+
<!-- auto -->
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# Metrics for a non-existent proxy (not in registry)
|
|
359
|
+
forge proxy metrics nonexistent-proxy 2>&1; echo "EXIT=$?"
|
|
360
|
+
|
|
361
|
+
# Metrics for test-proxy-nostart: shares a port with qa-openai,
|
|
362
|
+
# so smart-pointer semantics mean it reports metrics from the shared server.
|
|
363
|
+
forge proxy metrics test-proxy-nostart
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
- [ ] Non-existent proxy shows error and exits non-zero
|
|
367
|
+
- [ ] Shared-port proxy (test-proxy-nostart) returns metrics from the shared server (exit 0)
|
|
368
|
+
|
|
369
|
+
### 4.15 Backend List (Proxy Dependency)
|
|
370
|
+
|
|
371
|
+
<!-- auto -->
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
# List running backend instances (LiteLLM, etc.)
|
|
375
|
+
forge backend list
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
- [ ] Shows "No backends found." (or lists running backends)
|
|
379
|
+
- [ ] Command suggests `forge backend create litellm` when empty
|
|
380
|
+
|
|
381
|
+
### 4.16 Backend Create (LiteLLM Config)
|
|
382
|
+
|
|
383
|
+
<!-- auto -->
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
# Create backend config (shared by all instances)
|
|
387
|
+
forge backend create litellm
|
|
388
|
+
|
|
389
|
+
# Show config + status (even if not running)
|
|
390
|
+
forge backend show litellm-4000 --raw
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
- [ ] Backend config created (or reports it already exists)
|
|
394
|
+
- [ ] `forge backend show` displays config YAML
|
|
395
|
+
|
|
396
|
+
### 4.17 OpenRouter Templates
|
|
397
|
+
|
|
398
|
+
<!-- auto -->
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
# List all templates -- should now include OpenRouter alongside LiteLLM
|
|
402
|
+
forge proxy template list
|
|
403
|
+
|
|
404
|
+
# Show each OpenRouter template
|
|
405
|
+
forge proxy template show openrouter-anthropic
|
|
406
|
+
forge proxy template show openrouter-openai
|
|
407
|
+
forge proxy template show openrouter-openai-codex
|
|
408
|
+
forge proxy template show openrouter-deepseek
|
|
409
|
+
forge proxy template show openrouter-gemini
|
|
410
|
+
forge proxy template show openrouter-gemini-flash
|
|
411
|
+
forge proxy template show openrouter-glm
|
|
412
|
+
forge proxy template show openrouter-kimi
|
|
413
|
+
forge proxy template show openrouter-minimax
|
|
414
|
+
forge proxy template show openrouter-qwen
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
- [ ] `forge proxy template list` shows 18 user-facing templates total (8 litellm + 10 openrouter)
|
|
418
|
+
- [ ] `openrouter-anthropic` maps tiers to Claude models (haiku=claude-haiku-4.5, sonnet=claude-sonnet-4.6,
|
|
419
|
+
opus=claude-opus-4.6)
|
|
420
|
+
- [ ] `openrouter-deepseek` maps tiers to DeepSeek models (haiku=deepseek-v4-flash, sonnet/opus=deepseek-v4-pro)
|
|
421
|
+
- [ ] `openrouter-glm` maps tiers to GLM models (haiku=glm-4.7-flash, sonnet/opus=glm-5.1)
|
|
422
|
+
- [ ] `openrouter-kimi` maps tiers to Gemma/Kimi models (haiku=gemma-4-31b-it, sonnet/opus=kimi-k2.6)
|
|
423
|
+
- [ ] `openrouter-minimax` maps tiers to Gemma/MiniMax models (haiku=gemma-4-31b-it, sonnet/opus=minimax-m2.7)
|
|
424
|
+
- [ ] `openrouter-qwen` maps tiers to Qwen models (haiku=qwen3.6-flash, sonnet=qwen3.6-plus, opus=qwen3.6-max-preview)
|
|
425
|
+
- [ ] `openrouter-openai` maps tiers to GPT models (haiku=gpt-5.4-mini, sonnet=gpt-5.5, opus=gpt-5.5)
|
|
426
|
+
- [ ] `openrouter-openai-codex` maps tiers to Codex models (haiku=gpt-5.1-codex-mini, sonnet=gpt-5.3-codex,
|
|
427
|
+
opus=gpt-5.5)
|
|
428
|
+
- [ ] `openrouter-gemini` maps tiers to Gemini models (haiku=gemini-2.5-flash, sonnet=gemini-3.1-pro-preview,
|
|
429
|
+
opus=gemini-3.1-pro-preview)
|
|
430
|
+
- [ ] `openrouter-gemini-flash` maps all tiers to gemini-2.5-flash with tier_overrides for reasoning_effort
|
|
431
|
+
(low/medium/high)
|
|
432
|
+
- [ ] Each OpenRouter template has a distinct default_port (8095-8104)
|
|
433
|
+
|
|
434
|
+
### 4.18 OpenRouter Proxy Create
|
|
435
|
+
|
|
436
|
+
<!-- auto -->
|
|
437
|
+
|
|
438
|
+
```bash
|
|
439
|
+
# Clean up from previous runs
|
|
440
|
+
forge proxy delete openrouter-test --force 2>/dev/null || true
|
|
441
|
+
|
|
442
|
+
# Create an OpenRouter proxy without starting it (no OPENROUTER_API_KEY needed for config-only)
|
|
443
|
+
forge proxy create openrouter-anthropic --name openrouter-test --no-start
|
|
444
|
+
|
|
445
|
+
# Show proxy details
|
|
446
|
+
forge proxy show openrouter-test
|
|
447
|
+
|
|
448
|
+
# Show raw YAML
|
|
449
|
+
forge proxy show openrouter-test --raw
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
- [ ] OpenRouter proxy created from template (exit 0)
|
|
453
|
+
- [ ] `forge proxy show` or `forge proxy validate` displays `Provider: openrouter`
|
|
454
|
+
- [ ] Raw YAML shows `provider: openrouter` and tier mappings with `anthropic/` prefixed model IDs
|
|
455
|
+
- [ ] Proxy uses port 8095 (openrouter-anthropic default)
|
|
456
|
+
|
|
457
|
+
### 4.19 Model Alternatives
|
|
458
|
+
|
|
459
|
+
<!-- prereq: 4.18 -->
|
|
460
|
+
|
|
461
|
+
<!-- auto -->
|
|
462
|
+
|
|
463
|
+
```bash
|
|
464
|
+
# Check model_alternatives in the openrouter-anthropic template
|
|
465
|
+
forge proxy template show openrouter-anthropic --raw | grep -A3 model_alternatives
|
|
466
|
+
|
|
467
|
+
# Check instance inherits alternatives
|
|
468
|
+
forge proxy show openrouter-test --raw | grep -A3 model_alternatives
|
|
469
|
+
|
|
470
|
+
echo "---"
|
|
471
|
+
|
|
472
|
+
# Clean up
|
|
473
|
+
forge proxy delete openrouter-test --force 2>/dev/null || true
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
- [ ] Template YAML includes `model_alternatives` section under opus tier
|
|
477
|
+
- [ ] Opus alternative maps `claude-opus-4-7` to `anthropic/claude-opus-4.7`
|
|
478
|
+
- [ ] Proxy instance inherits `model_alternatives` from template
|
|
479
|
+
- [ ] `openrouter-test` proxy cleaned up
|
|
480
|
+
|
|
481
|
+
---
|