Skip to content

Instantly share code, notes, and snippets.

@moinulmoin
Last active February 8, 2026 16:00
Show Gist options
  • Select an option

  • Save moinulmoin/6af2a7b1fd5aac44aabe95c06191dc3a to your computer and use it in GitHub Desktop.

Select an option

Save moinulmoin/6af2a7b1fd5aac44aabe95c06191dc3a to your computer and use it in GitHub Desktop.
AMP Free Tier + Local OAuth Fallback Guide — Use AMP's free $10/day first, auto-fallback to your local OAuth subs (Claude, Gemini, Codex, Copilot, Kiro)

AMP Free Tier + Local OAuth Fallback Guide

Use AMP's free $10/day grant first. When exhausted, switch to your own subscriptions (Claude, Gemini, Codex, GitHub Copilot, Kiro, etc.) — all from within AMP CLI. No restart needed.

Prerequisites: Add Prefer-Upstream Feature

CLIProxyAPI / CLIProxyAPIPlus doesn't include the prefer-upstream feature out of the box. You need to add it first.

Drop IMPLEMENT-PREFER-UPSTREAM.md into your AI coding agent (Claude Code, AMP, Cursor, etc.) and ask it to implement the changes. It's 7 small edits that follow the existing ForceModelMappings pattern.

Or apply manually — the file has exact code snippets for each change.


How It Works

AMP CLI  -->  CLIProxyAPIPlus (localhost:8317)  -->  ampcode.com (free tier)
                                                      |
                                                      v (any 4xx/5xx = auto-fallback)
                                                  Local OAuth providers
                                                  (Claude, Gemini, Codex, Copilot, Kiro...)

Two modes, switchable live from AMP CLI:

  • Free mode (default): All requests go to ampcode.com. On any error, auto-falls back to local.
  • Local mode: Requests go directly to your OAuth subscriptions. Zero ampcode.com usage.

1. Clone & Build

git clone https://github.com/router-for-me/CLIProxyAPIPlus.git
cd CLIProxyAPIPlus
go build -o cli-proxy-api ./cmd/server

2. Authenticate Your Providers

Run whichever logins match your subscriptions:

# Google (Gemini)
./cli-proxy-api --login

# Claude (Anthropic)
./cli-proxy-api --claude-login

# Codex (OpenAI / ChatGPT Plus/Pro)
./cli-proxy-api --codex-login

# GitHub Copilot
./cli-proxy-api --github-copilot-login

# Kiro (AWS CodeWhisperer)
./cli-proxy-api --kiro-aws-authcode
# or: --kiro-login (Google OAuth), --kiro-import (from Kiro IDE)

# Qwen
./cli-proxy-api --qwen-login

Tokens are saved to ~/.cli-proxy-api/. You can log in to multiple providers — more fallback options.

3. Create config.yaml

In the CLIProxyAPIPlus/ directory:

port: 8317

# Secret key AMP uses to authenticate with this proxy (change this!)
api-keys: ["YOUR_PROXY_SECRET_KEY"]

# Amp integration
ampcode:
  upstream-url: "https://ampcode.com"
  upstream-api-key: "sgamp_user_XXXX_YYYY"  # Your ampcode.com API key
  prefer-upstream: true                      # Start in free tier mode
  restrict-management-to-localhost: false

# Management API (needed for the mode-toggle skill)
remote-management:
  allow-remote: true
  secret-key: "YOUR_MANAGEMENT_KEY"  # Auto-hashed on first startup (change this!)
  disable-control-panel: true

# Auth token directory
auth-dir: "~/.cli-proxy-api"

# Recommended settings
request-retry: 3
quota-exceeded:
  switch-project: true
  switch-preview-model: true

Where to find your ampcode.com API key: Check ~/.local/share/amp/secrets.json — the value after "apiKey@https://ampcode.com/" is your key.

4. Configure AMP to Use the Proxy

a) Point AMP at the proxy

Edit ~/.config/amp/settings.json — add amp.url:

{
  "amp.url": "http://localhost:8317"
}

Keep your existing settings (mcpServers, git settings, etc.) — just add the amp.url line.

b) Add the proxy API key to AMP secrets

Edit ~/.local/share/amp/secrets.json — add the proxy entry:

{
  "apiKey@https://ampcode.com/": "sgamp_user_XXXX_YYYY",
  "apiKey@http://localhost:8317": "YOUR_PROXY_SECRET_KEY"
}

The YOUR_PROXY_SECRET_KEY must match the api-keys value in your config.yaml.

5. Create the Mode-Toggle Skill

AMP uses skills for custom functionality. Create this skill so you can say "switch to local mode" or "switch to free tier" directly in AMP.

mkdir -p ~/.config/agents/skills/amp-mode
cat > ~/.config/agents/skills/amp-mode/SKILL.md << 'SKILL'
---
name: amp-mode
description: Switch AMP routing mode between free tier (ampcode.com) and local OAuth providers (Claude, Gemini, Codex, Copilot, Kiro). Use when the user says "switch to local", "switch to free", "check mode", "use local providers", "use free tier", or anything about changing the routing mode.
---

# AMP Routing Mode Toggle

You can switch AMP's routing mode by calling the CLIProxyAPI management API.

## Check current mode

```bash
curl -s http://localhost:8317/v0/management/ampcode/prefer-upstream -H "Authorization: Bearer YOUR_MANAGEMENT_KEY"

If prefer-upstream is true → FREE TIER mode (ampcode.com first, auto-fallback to local on any error). If prefer-upstream is false → LOCAL OAUTH mode (uses your subscriptions directly).

Switch to LOCAL OAUTH mode

curl -s -X PUT http://localhost:8317/v0/management/ampcode/prefer-upstream \
  -H "Authorization: Bearer YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": false}'

Tell the user: "Switched to LOCAL OAUTH mode. Requests now go directly to your subscriptions."

Switch to FREE TIER mode

curl -s -X PUT http://localhost:8317/v0/management/ampcode/prefer-upstream \
  -H "Authorization: Bearer YOUR_MANAGEMENT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"value": true}'

Tell the user: "Switched to FREE TIER mode. Requests route to ampcode.com first (auto-falls back to local on any error)."

Important

  • The change takes effect immediately — no restart needed.
  • Always confirm the action to the user after running the curl command.
  • If the user just says "switch mode" without specifying, check the current mode first and switch to the opposite. SKILL

> Replace `YOUR_MANAGEMENT_KEY` with the plaintext `secret-key` from your `config.yaml` (before it gets hashed).

After creating the skill, restart AMP. It will appear under **skill: invoke** in the command palette, and AMP can also use it autonomously when you say things like "switch to local".

## 6. Start & Use

```bash
cd CLIProxyAPIPlus
./cli-proxy-api

Then open AMP CLI and start coding. That's it.

Daily Workflow

Time What happens Action
Start of day Free tier active Just code normally
Free tier exhausted Auto-fallback kicks in Nothing to do — it just works
Want to skip upstream? Tell AMP: "switch to local mode" Skips ampcode.com round-trip
Next day Tell AMP: "switch to free tier" Back to ampcode.com

Commands You Can Say in AMP

  • "switch to free tier" — use ampcode.com free grant
  • "switch to local mode" — use your OAuth subscriptions directly
  • "check mode status" — see which mode is active

Auto-Fallback

In free tier mode, if ampcode.com returns any error (402 payment required, 429 rate limit, 5xx server error, etc.), the proxy automatically falls back to your local providers for that request. No manual intervention needed — you can keep coding without interruption.


Supported Local Providers

Provider Login Command Subscription
Google Gemini --login Free / Google One AI
Claude --claude-login Pro / Max
Codex (OpenAI) --codex-login ChatGPT Plus / Pro
GitHub Copilot --github-copilot-login Copilot subscription
Kiro (AWS) --kiro-aws-authcode AWS Builder ID
Qwen --qwen-login Free
Vertex AI --vertex-import key.json GCP account

The more providers you authenticate, the more fallback options you have.


Troubleshooting

AMP can't connect: Check that amp.url in settings.json matches the proxy port, and the API key in secrets.json matches api-keys in config.yaml.

Mode toggle skill not showing: Make sure the skill is at ~/.config/agents/skills/amp-mode/SKILL.md and restart AMP. Skills are discovered at startup.

No local fallback happening: Run your OAuth logins first (--login, --claude-login, etc.). Check proxy logs for "using local provider" vs "forwarding to ampcode.com".

Revert to direct AMP (no proxy): Remove "amp.url" from settings.json and the "apiKey@http://localhost:8317" line from secrets.json.


Links

Implement "Prefer Upstream" for AMP Free Tier + Local Fallback

Drop this file into your AI coding agent (Claude Code, AMP, Cursor, etc.) and ask it to implement the changes.

This adds a prefer-upstream mode to CLIProxyAPI / CLIProxyAPIPlus that routes AMP requests to ampcode.com first (free $10/day), with automatic fallback to local OAuth providers on rate limit (429) or server error (5xx).

Reference implementation: https://gist.github.com/moinulmoin/6af2a7b1fd5aac44aabe95c06191dc3a


What This Adds

  • prefer-upstream config flag — when true, all provider requests go to ampcode.com first
  • Auto-fallback — if ampcode.com returns 429 or 5xx, the request transparently falls back to local providers
  • Management API — GET/PUT /v0/management/ampcode/prefer-upstream for runtime toggling (no restart)
  • Hot-reload — config file changes are detected and applied live
  • AMP Toolbox tool — switch modes by talking to AMP: "switch to local mode", "switch to free tier"

Code Changes (7 files)

All changes follow the exact same pattern as the existing ForceModelMappings feature. Find each ForceModelMappings reference and add the PreferUpstream equivalent next to it.


1. internal/config/config.go — Add config field

Find the AmpCode struct, specifically the ForceModelMappings field. Add after it:

// PreferUpstream when true, routes all provider requests to ampcode.com
// instead of local OAuth providers. Useful for consuming AMP's free daily
// grant before falling back to local subscriptions.
PreferUpstream bool `yaml:"prefer-upstream" json:"prefer-upstream"`

2. internal/api/modules/amp/amp.go — Add getter method

Find the forceModelMappings() method. Add after it:

// preferUpstream returns whether requests should be routed to ampcode.com first
func (m *AmpModule) preferUpstream() bool {
	m.configMu.RLock()
	defer m.configMu.RUnlock()
	if m.lastConfig == nil {
		return false
	}
	return m.lastConfig.PreferUpstream
}

3. internal/api/modules/amp/fallback_handlers.go — Core routing logic

This is the main change. Three parts:

3a. Add field to FallbackHandler struct

Find the FallbackHandler struct. Add a new field:

preferUpstream     func() bool

So the struct becomes:

type FallbackHandler struct {
	getProxy           func() *httputil.ReverseProxy
	modelMapper        ModelMapper
	forceModelMappings func() bool
	preferUpstream     func() bool   // <-- ADD THIS
}

3b. Update NewFallbackHandlerWithMapper constructor

Change the function signature to accept preferUpstream as a 4th parameter, and store it:

func NewFallbackHandlerWithMapper(getProxy func() *httputil.ReverseProxy, mapper ModelMapper, forceModelMappings func() bool, preferUpstream func() bool) *FallbackHandler {
	if forceModelMappings == nil {
		forceModelMappings = func() bool { return false }
	}
	return &FallbackHandler{
		getProxy:           getProxy,
		modelMapper:        mapper,
		forceModelMappings: forceModelMappings,
		preferUpstream:     preferUpstream,   // <-- ADD THIS
	}
}

3c. Add prefer-upstream routing block in WrapHandler

In the WrapHandler method, find the line that checks forceMappings (the line with fh.forceModelMappings != nil && fh.forceModelMappings()). Insert BEFORE that entire block (before the forceMappings variable declaration):

// PREFER UPSTREAM: Route directly to ampcode.com (free tier) instead of local providers.
// If upstream returns ANY error (4xx/5xx), auto-fallback to local providers.
if fh.preferUpstream != nil && fh.preferUpstream() {
	proxy := fh.getProxy()
	if proxy != nil {
		logAmpRouting(RouteTypeAmpCredits, modelName, "", "", requestPath)
		c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes))

		// Use a response recorder to detect errors and auto-fallback
		rec := &responseRecorder{ResponseWriter: c.Writer, statusCode: 200}
		proxy.ServeHTTP(rec, c.Request)

		// If upstream succeeded or is streaming, we're done
		if !rec.shouldFallback() || rec.written {
			return
		}

		// Upstream returned error — fall through to local providers
		log.Warnf("amp prefer-upstream: upstream returned %d, falling back to local providers", rec.statusCode)
		c.Request.Body = io.NopCloser(bytes.NewReader(bodyBytes))
		// Continue to normal routing below
	}
}

3d. Add responseRecorder type

Add at the bottom of the file (after the existing extractModelFromRequest function):

// responseRecorder wraps http.ResponseWriter to capture the status code
// before any bytes are written. This allows detecting upstream errors
// (4xx/5xx) and falling back to local providers in prefer-upstream mode.
type responseRecorder struct {
	http.ResponseWriter
	statusCode int
	written    bool // true once any body bytes have been flushed to the client
}

// shouldFallback returns true for any error status (4xx/5xx) that indicates
// we should try local providers instead.
func (r *responseRecorder) shouldFallback() bool {
	return r.statusCode >= 400
}

func (r *responseRecorder) WriteHeader(code int) {
	r.statusCode = code
	// Only forward success/redirect responses to the client (1xx-3xx)
	if code < 400 {
		r.ResponseWriter.WriteHeader(code)
	}
}

func (r *responseRecorder) Write(b []byte) (int, error) {
	if r.shouldFallback() && !r.written {
		// Suppress the upstream error body — we'll fall back to local providers
		return len(b), nil
	}
	r.written = true
	return r.ResponseWriter.Write(b)
}

// Flush delegates to the underlying writer so SSE streaming works in the success path.
func (r *responseRecorder) Flush() {
	if f, ok := r.ResponseWriter.(http.Flusher); ok {
		f.Flush()
	}
}

Make sure "net/http" is in the import block (needed for http.ResponseWriter, http.Flusher).


4. internal/api/modules/amp/routes.go — Pass preferUpstream to constructors

Find every call to NewFallbackHandlerWithMapper. There are two:

  1. In registerManagementRoutes (the Gemini v1beta1 fallback)
  2. In registerProviderAliases (the main provider fallback)

Both currently pass 3 arguments. Add m.preferUpstream as the 4th argument to each:

// Before:
NewFallbackHandlerWithMapper(func() *httputil.ReverseProxy { return m.getProxy() }, m.modelMapper, m.forceModelMappings)

// After:
NewFallbackHandlerWithMapper(func() *httputil.ReverseProxy { return m.getProxy() }, m.modelMapper, m.forceModelMappings, m.preferUpstream)

5. internal/api/handlers/management/config_lists.go — Management API handlers

Find GetAmpForceModelMappings and PutAmpForceModelMappings. Add after them:

// GetAmpPreferUpstream returns whether upstream routing is preferred.
func (h *Handler) GetAmpPreferUpstream(c *gin.Context) {
	if h == nil || h.cfg == nil {
		c.JSON(200, gin.H{"prefer-upstream": false})
		return
	}
	c.JSON(200, gin.H{"prefer-upstream": h.cfg.AmpCode.PreferUpstream})
}

// PutAmpPreferUpstream updates the prefer upstream setting.
func (h *Handler) PutAmpPreferUpstream(c *gin.Context) {
	h.updateBoolField(c, func(v bool) { h.cfg.AmpCode.PreferUpstream = v })
}

6. internal/api/server.go — Register management routes

Find the force-model-mappings route registrations (3 lines: GET, PUT, PATCH). Add after them:

mgmt.GET("/ampcode/prefer-upstream", s.mgmt.GetAmpPreferUpstream)
mgmt.PUT("/ampcode/prefer-upstream", s.mgmt.PutAmpPreferUpstream)
mgmt.PATCH("/ampcode/prefer-upstream", s.mgmt.PutAmpPreferUpstream)

7. internal/watcher/diff/config_diff.go — Hot-reload detection

Find the ForceModelMappings diff check. Add after it:

if oldCfg.AmpCode.PreferUpstream != newCfg.AmpCode.PreferUpstream {
	changes = append(changes, fmt.Sprintf("ampcode.prefer-upstream: %t -> %t", oldCfg.AmpCode.PreferUpstream, newCfg.AmpCode.PreferUpstream))
}

Build & Verify

go build -o cli-proxy-api ./cmd/server
go test ./internal/api/modules/amp/ -v

Config Setup

Add to your config.yaml:

ampcode:
  upstream-url: "https://ampcode.com"
  upstream-api-key: "your-ampcode-api-key"
  prefer-upstream: true    # Use AMP free tier first
  restrict-management-to-localhost: false

remote-management:
  allow-remote: true
  secret-key: "your-management-key"

AMP Skill for Mode Toggling (optional but recommended)

AMP uses skills (not toolbox tools) for custom functionality. Create this skill so you can say "switch to local mode" directly in AMP.

mkdir -p ~/.config/agents/skills/amp-mode

Create ~/.config/agents/skills/amp-mode/SKILL.md:

---
name: amp-mode
description: Switch AMP routing mode between free tier (ampcode.com) and local OAuth providers. Use when the user says "switch to local", "switch to free", "check mode", or anything about changing the routing mode.
---

# AMP Routing Mode Toggle

Switch modes by calling the CLIProxyAPI management API.

## Check current mode
```bash
curl -s http://localhost:8317/v0/management/ampcode/prefer-upstream -H "Authorization: Bearer your-management-key"
```

## Switch to LOCAL OAUTH mode
```bash
curl -s -X PUT http://localhost:8317/v0/management/ampcode/prefer-upstream \
  -H "Authorization: Bearer your-management-key" \
  -H "Content-Type: application/json" \
  -d '{"value": false}'
```

## Switch to FREE TIER mode
```bash
curl -s -X PUT http://localhost:8317/v0/management/ampcode/prefer-upstream \
  -H "Authorization: Bearer your-management-key" \
  -H "Content-Type: application/json" \
  -d '{"value": true}'
```

## Important
- The change takes effect immediately — no restart needed.
- Always confirm the action to the user after running the curl command.
- If the user just says "switch mode", check the current mode first and switch to the opposite.

Replace your-management-key with the plaintext secret-key from your config.yaml.

After creating the skill, restart AMP. It appears under skill: invoke in the command palette, and AMP can invoke it autonomously when you say things like "switch to local".


AMP CLI Configuration

~/.config/amp/settings.json — add:

{
  "amp.url": "http://localhost:8317"
}

~/.local/share/amp/secrets.json — add proxy key:

{
  "apiKey@http://localhost:8317": "your-api-key-from-config-yaml"
}

Reference

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment