Skip to content

fix(cli): sync proxy env to ~/.claude/settings.json for agent workers - #881

Open
SebasSotoA wants to merge 8 commits into
Alishahryar1:mainfrom
SebasSotoA:fix/fcc-claude-agents-settings-sync
Open

fix(cli): sync proxy env to ~/.claude/settings.json for agent workers#881
SebasSotoA wants to merge 8 commits into
Alishahryar1:mainfrom
SebasSotoA:fix/fcc-claude-agents-settings-sync

Conversation

@SebasSotoA

@SebasSotoA SebasSotoA commented Jun 20, 2026

Copy link
Copy Markdown

Summary

  • Add \cli/claude_settings_sync.py\ to merge FCC proxy \ANTHROPIC_*\ env into ~/.claude/settings.json\ \env\ block.
  • Invoke sync on \ cc-claude\ launch and on admin apply so agent daemon workers inherit proxy config.
  • Add tests and README note.

Test plan

  • Run unit tests: \ ests/cli/test_claude_settings_sync.py, \ ests/cli/test_entrypoints.py\
  • Launch \ cc-claude\ with proxy env configured; confirm ~/.claude/settings.json\ \env\ updated.
  • Trigger agent workers; confirm no /login\ prompt when proxy auth is valid.
  • Admin apply path still syncs settings.

Fixes #880

Greptile Summary

This PR defers ~/.claude/settings.json writes until the proxy server is confirmed reachable, so a failed bind no longer corrupts agent worker config. It also unifies the admin-browser-open and settings-sync paths into a single _schedule_on_server_ready daemon thread.

  • _schedule_on_server_ready replaces _schedule_open_admin_browser; it polls /health for up to 30 s before syncing or opening the browser, so a port-in-use error leaves settings.json intact.
  • Admin apply now always syncs using the live (pre-restart) proxy URL (app.state.live_proxy_root_url) and skips sync when the token comes only from the process environment, preventing shell-only secrets from being persisted.

Confidence Score: 5/5

The core correctness goal — never writing settings.json before the server is confirmed live — is achieved cleanly; the only finding is an unused helper that should be removed.

The pre-bind sync problem is correctly solved: the daemon thread gates all writes on a successful /health poll, admin apply uses the captured live URL rather than the pending new address, and the process-token guard prevents shell secrets from being persisted. Test coverage is thorough across the new sync paths. The one leftover is should_defer_claude_settings_sync, which is defined, exported, and tested but no longer called from any production code — a dead-code cleanup, not a correctness issue.

cli/claude_settings_sync.py — should_defer_claude_settings_sync and its test in tests/cli/test_claude_settings_sync.py can be removed since the production call site was eliminated in this PR.

Important Files Changed

Filename Overview
cli/claude_settings_sync.py New module handling atomic settings writes with symlink resolution, temp-file cleanup, permission hardening, and best-effort error handling. should_defer_claude_settings_sync is defined and tested but no longer called from any production path after the admin-apply refactor.
cli/entrypoints.py Core fix lands here: _schedule_open_admin_browser unified into _schedule_on_server_ready which defers all writes until /health succeeds, so a failed bind never overwrites settings.json.
api/admin_routes.py Admin apply now always syncs using the live (pre-restart) proxy URL rather than deferring on pending_fields; skips sync when token is process-only. Clean implementation with appropriate fallback.
api/app.py Captures live_proxy_root_url at app creation time; _run_supervised_server overrides it with the same value, providing a sane default for direct-ASGI entry points.
cli/launchers/claude.py Sync added after preflight_proxy confirms the server is reachable; guarded by uses_process_anthropic_auth_token(). Correct placement.
tests/cli/test_claude_settings_sync.py Comprehensive unit tests covering file creation, merge semantics, symlink handling, permission hardening, concurrent writes, temp-file cleanup, and invalid-JSON skip. Solid coverage.
tests/cli/test_entrypoints.py Tests renamed and updated to match _schedule_on_server_ready; new cases for sync/skip-sync paths use a synchronous thread shim to exercise the callback body. Launcher tests correctly patch the sync function to avoid real file writes.
tests/api/test_admin.py New tests exercise live-URL sync on port change, mixed-field applies, unchanged-PORT applies, previous-restart-pending applies, and process-token skip. Good edge-case coverage.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant serve as serve() loop
    participant rss as _run_supervised_server
    participant thread as fcc-server-ready thread
    participant health as /health
    participant sync as sync_claude_settings
    participant fs as ~/.claude/settings.json

    serve->>rss: "open_admin_browser=True"
    rss->>rss: set app.state.live_proxy_root_url
    rss->>thread: _schedule_on_server_ready(settings)
    rss->>rss: server.run() [blocks]
    thread->>health: preflight_proxy() poll (30s)
    health-->>thread: None (reachable)
    thread->>sync: proxy_root_url, auth_token
    sync->>fs: atomic write (temp + replace)
    Note over serve,fs: Admin apply path
    participant admin as /admin/api/config/apply
    admin->>admin: write_managed_env()
    admin->>admin: cache_clear() + get fresh settings
    admin->>sync: live_proxy_root_url (not pending port)
    sync->>fs: atomic write
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant serve as serve() loop
    participant rss as _run_supervised_server
    participant thread as fcc-server-ready thread
    participant health as /health
    participant sync as sync_claude_settings
    participant fs as ~/.claude/settings.json

    serve->>rss: "open_admin_browser=True"
    rss->>rss: set app.state.live_proxy_root_url
    rss->>thread: _schedule_on_server_ready(settings)
    rss->>rss: server.run() [blocks]
    thread->>health: preflight_proxy() poll (30s)
    health-->>thread: None (reachable)
    thread->>sync: proxy_root_url, auth_token
    sync->>fs: atomic write (temp + replace)
    Note over serve,fs: Admin apply path
    participant admin as /admin/api/config/apply
    admin->>admin: write_managed_env()
    admin->>admin: cache_clear() + get fresh settings
    admin->>sync: live_proxy_root_url (not pending port)
    sync->>fs: atomic write
Loading

Comments Outside Diff (4)

  1. tests/cli/test_entrypoints.py, line 510-524 (link)

    P1 Mock settings sync in the launcher entrypoint tests that should not touch user state. With this PR, launch([]) calls the real sync_claude_settings() before reaching the patched Popen path, and the command-resolution failure test also writes settings before resolve_client_binary() returns None. These tests do not redirect HOME or patch the sync function, so running them can mutate the test runner's real ~/.claude/settings.json while they are only trying to assert launch behavior. Patch cli.launchers.claude.sync_claude_settings or redirect HOME for these test cases.

    Artifacts

    Repro: focused launcher harness mirroring the affected test without patching settings sync

    • Contains supporting evidence from the run (text/x-python; charset=utf-8).

    Repro: harness output showing fake HOME settings.json creation and synced env contents

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

  2. api/admin_routes.py, line 144 (link)

    P1 Keep restart pending until the automatic restart has actually completed. This branch queues the restart callback as a background task, then immediately clears admin_pending_fields. If a HOST/PORT apply is followed by another apply while the old server is still running, or if the callback fails/cannot bind the new address, later token-only applies see no pending restart and can sync ~/.claude/settings.json to the not-yet-serving address. Keep the pending restart state until the supervisor has confirmed the restart, or track a restart-in-progress state that still blocks settings sync.

  3. api/admin_routes.py, line 141-145 (link)

    P1 Keep restart pending until the automatic restart has actually succeeded. This branch clears admin_pending_fields immediately after scheduling the background callback, before _invoke_admin_restart_callback() runs. If that callback raises or fails to make the supervisor restart, or if another Admin apply is processed during that window, the sync guard no longer sees the pending HOST/PORT restart and can write ~/.claude/settings.json with the new address even though the old server is still the one serving traffic. Keep the pending fields until the restart callback completes and the new server is live, or track a separate restart-in-progress state for the sync guard.

    Artifacts

    Repro: executable admin apply harness with failing restart callback and isolated HOME

    • Contains supporting evidence from the run (text/x-python; charset=utf-8).

    Repro: command output showing callback failure, cleared pending fields, and settings.json written with new port

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

  4. api/app.py, line 101-105 (link)

    Avoid guessed live URL. This stores a proxy URL derived from Settings, but the supported direct ASGI path can bind uvicorn with CLI overrides, for example uvicorn server:app --port 9090 while the config still says 8082. Admin apply later trusts app.state.live_proxy_root_url and syncs ~/.claude/settings.json to the configured port, so agent workers can be pointed at a proxy URL that is not actually serving. Use an address captured from the serving path/request context, or skip this fallback when the supervisor did not set the live URL.

    Artifacts

    Repro: focused runtime harness for direct ASGI admin apply live URL mismatch

    • Contains supporting evidence from the run (text/x-python; charset=utf-8).

    Repro: runtime output showing HTTP 200 OK and synced configured-port URL despite actual request port 9090

    • Keeps the command output available without making the summary code-heavy.

    View artifacts

    T-Rex Ran code and verified through T-Rex

Reviews (14): Last reviewed commit: "fix: defer settings sync until server is..." | Re-trigger Greptile

fcc-claude only injected ANTHROPIC_* env into the launched process.
claude agents spawns daemon workers as plain claude with empty env,
causing /login prompts. Auto-sync FCC proxy settings into the Claude
Code settings.json env block on fcc-claude launch and admin apply.
Comment thread cli/claude_settings_sync.py Outdated
Comment thread cli/launchers/claude.py Outdated
Comment thread api/admin_routes.py Outdated
Comment thread cli/claude_settings_sync.py Outdated
Comment thread cli/claude_settings_sync.py Outdated
Comment thread api/admin_routes.py Outdated
- Strip stale ANTHROPIC_* env keys before writing FCC proxy values
- Make sync best-effort: skip on invalid paths/content instead of raising
- Restrict settings file permissions on Unix (0o600)
- Defer admin apply sync when PORT changes until proxy restart
- Patch sync in launcher tests to avoid mutating real ~/.claude
@SebasSotoA

SebasSotoA commented Jun 20, 2026

Copy link
Copy Markdown
Author

Addressed all Greptile review annotations in bbe28e4:

  1. Stale ANTHROPIC_ env* — sync now strips inherited ANTHROPIC_* keys before writing FCC-managed values (matches build_claude_launcher_env behavior).
  2. Do not block launchsync_claude_settings() is best-effort only; warns and returns instead of raising on IO/parse errors.
  3. Keep apply stable — same best-effort behavior; admin apply no longer fails when settings write fails.
  4. Avoid destructive replacement — invalid JSON / non-object / directory / UTF-8 issues skip sync and leave the existing file untouched.
  5. Restrict token permissions — settings file chmod 0o600 on Unix after atomic write.
  6. Defer PORT sync — admin apply skips Claude settings sync when PORT is in pending restart fields.
  7. Mock settings sync in tests — launcher tests patch sync_claude_settings to avoid mutating real ~/.claude/settings.json.

Added tests for stale-key stripping, skip-on-invalid-json, directory path, PORT deferral, and admin sync gating.

Comment thread api/admin_routes.py Outdated
Comment thread cli/claude_settings_sync.py Outdated
Comment thread cli/claude_settings_sync.py Outdated
Comment thread api/admin_routes.py Outdated
Comment thread cli/claude_settings_sync.py
- Defer sync on HOST changes in addition to PORT
- Use NamedTemporaryFile for atomic writes to avoid temp-file collisions
- Harden file permissions on no-op (already up-to-date) sync path
- Always call sync_claude_settings in admin apply (remove deferral logic)
- Update and add tests for all four changes
@SebasSotoA

Copy link
Copy Markdown
Author

Addressing Greptile 1/5 review (commit 03b8acb)

All five issues from the latest review have been fixed:

1. Mixed applies skip sync (api/admin_routes.py)
Removed should_defer_claude_settings_sync guard entirely. sync_claude_settings is now always called immediately after the cache is cleared — even when PORT or HOST is in the changeset. The call is best-effort (returns bool, never throws), so there is no risk of blocking a restart.

2. Defer HOST changes too (cli/claude_settings_sync.py)
should_defer_claude_settings_sync now checks {HOST, PORT} (set intersection) instead of only PORT. The function is retained for any other callers, and its tests were updated with HOST/empty-list assertions.

3. Unique temp files (cli/claude_settings_sync.py)
Replaced the fixed .tmp path (path.with_suffix(.tmp)) with tempfile.NamedTemporaryFile(dir=path.parent, prefix=.fcc-, suffix=.tmp, delete=False). Each concurrent sync now gets a distinct temp path, eliminating the race condition. Covered by a new threading test.

4. Sync after restart (api/admin_routes.py)
Handled by fix #1 — since deferral is gone, the sync fires with the newly cached settings (including the new PORT/HOST) immediately, so no separate post-restart hook is needed.

5. Harden existing files (cli/claude_settings_sync.py)
Added _restrict_settings_permissions(path) before the return False on the no-op path so that pre-existing files with permissive modes are hardened even when no content change is required. Covered by a new Unix-mode test (skipped on Windows).

Test results: 58 passed, 2 skipped (Unix chmod tests correctly skipped on Windows).

@greptileai please re-review.

Comment thread cli/claude_settings_sync.py Outdated
Comment thread cli/claude_settings_sync.py
Comment thread tests/api/test_admin.py
Comment thread cli/claude_settings_sync.py Outdated
@SebasSotoA

Copy link
Copy Markdown
Author

Addressing Greptile 4/5 review (commit 9ca3f20)

All four issues from the latest review have been fixed:

1. Preserve symlinked settings (cli/claude_settings_sync.py)
Added write_path = path.resolve() if path.is_symlink() else path before the write block. _load_settings_object already reads through symlinks; os.replace now targets the resolved real file so the symlink is preserved intact. Covered by test_sync_follows_symlink_to_write_resolved_target (skipped on Windows where symlinks require elevated privileges).

2. Clean failed temp files (cli/claude_settings_sync.py)
Introduced temp_path: Path | None = None before the try-block. After os.replace succeeds it is set to None ("installed — do not unlink"). In the except OSError path, if temp_path is not None the file is unlinked before returning, preventing token leakage. Covered by test_sync_cleans_up_temp_file_on_replace_failure.

3. Avoid pre-restart sync for PORT-only changes (api/admin_routes.py)
Re-introduced selective deferral: the filtered payload keys are saved before write_managed_env, then sync_claude_settings is only called when the changeset is not exclusively HOST/PORT fields. This means:

  • PORT-only changes: defer (no immediate sync; next fcc-claude launch will re-sync with the new address)
  • PORT + TOKEN mixed: sync immediately (token must be updated now)
  • TOKEN-only: sync immediately

test_admin_apply_defers_sync_when_only_port_changes now asserts assert_not_called(). Mixed and token-only tests remain assert_called_once().

4. Preserve non-string env entries (cli/claude_settings_sync.py)
Removed isinstance(value, str) from _merge_fcc_env. The filter now only strips ANTHROPIC_* keys and FCC-managed keys, keeping unrelated entries like {"MY_FLAG": True, "TIMEOUT": 30} intact. Return type updated from dict[str, str] to dict[str, object]. Covered by test_sync_preserves_non_string_env_entries.

Test results: 37 passed, 3 skipped (Unix-only tests correctly skipped on Windows).

@greptileai please re-review.

Comment thread api/admin_routes.py Outdated
Comment thread api/admin_routes.py Outdated
@SebasSotoA

Copy link
Copy Markdown
Author

Addressing the 2 remaining considerations (commit bfd99ec)

Both considerations were rooted in the settings-sync deferral guard being too narrow. They are now fixed together, plus a supervisor-side sync that closes the loop so deferred changes are never left stale.

1. Check pending restarts across applies (api/admin_routes.py)
The deferral now considers restarts pending from a previous apply, not just the current request. It computes:

pending_now = set(result["pending_fields"])
pending_before = set(getattr(request.app.state, "admin_pending_fields", []))
restart_pending = (pending_now | pending_before) & {"HOST", "PORT"}

A token-only apply while a PORT/HOST restart is still awaiting a manual restart now defers, so settings.json is never rewritten with a not-yet-serving address. Covered by test_admin_apply_defers_sync_when_restart_pending_from_previous_apply.

2. Defer on actual changed fields, not submitted keys (api/admin_routes.py)
The guard is now based on result["pending_fields"] (what actually changed) instead of the submitted keys. Submitting PORT/HOST with their existing values yields pending_fields == [], so the sync still runs and can repair a stale or missing settings.json. Covered by test_admin_apply_syncs_when_restart_field_submitted_unchanged.

Closing the loop: sync on every server start (cli/entrypoints.py)
The serve() supervisor now calls a best-effort sync_claude_settings on each server start (initial boot and after every restart), writing the address the new server actually binds to:

settings = get_settings()
_sync_claude_settings_on_start(settings)

This means deferred HOST/PORT changes are persisted as soon as the new server is live, and for mixed PORT + token applies the token is applied immediately after restart. This resolves the earlier "mixed applies skip the token" concern without ever writing a not-yet-serving address: the token is deferred only briefly and then applied by the supervisor once the new port is actually serving. Covered by test_serve_syncs_claude_settings_on_each_start.

Updated behavior matrix:

  • Token-only, nothing pending: sync now
  • HOST/PORT changed: defer, repaired on next server start
  • HOST/PORT submitted unchanged: sync now (no pending restart)
  • Token-only while HOST/PORT pending from before: defer
  • Mixed HOST/PORT + token: defer, both applied on next server start

Test results: 63 passed, 3 skipped (Unix-only tests skipped on Windows).

@greptileai please re-review.

Comment thread cli/entrypoints.py Outdated
Comment thread cli/claude_settings_sync.py
Comment thread cli/entrypoints.py Outdated
@SebasSotoA

Copy link
Copy Markdown
Author

Revised approach: always sync the live address + current token (commit 2fbb799)

The previous "defer on pending HOST/PORT restart" approach had two flaws (found in local review):

  1. Pending restart state was lost across applies (admin_pending_fields is overwritten each apply), so a later non-restart apply could sync the new, not-yet-serving address.
  2. Deferring the entire sync during a pending restart left agent workers with a stale ANTHROPIC_AUTH_TOKEN. Since ANTHROPIC_AUTH_TOKEN is neither restart-required nor session-sensitive and require_api_key reads it via cache-cleared get_settings, the live proxy validates the new token immediately, causing auth failures for workers still using the old token.

Both stem from the URL and the token living in the same env block. The fix removes deferral entirely and instead always syncs, using the address the proxy is actually bound to (not the pending settings):

api/admin_routes.py - apply_admin_config now always calls sync_claude_settings, with:

live_proxy_root_url = getattr(request.app.state, "live_proxy_root_url", None) or local_proxy_root_url(fresh_settings)
sync_claude_settings(proxy_root_url=live_proxy_root_url, auth_token=fresh_settings.anthropic_auth_token)

api/app.py - create_app records app.state.live_proxy_root_url = local_proxy_root_url(settings) at startup, so every entry point (including a direct uvicorn server:app) has the live address before any apply, rather than falling back to a pending value.

cli/entrypoints.py - _run_supervised_server also sets app.state.live_proxy_root_url for the bound settings, and serve() still re-syncs on each (re)start, so once the restarted server is live the new address + token are written to ~/.claude/settings.json.

Behavior matrix:

  • Token-only: sync now (live URL unchanged, new token).
  • HOST/PORT changed: sync now with the live (old) URL + token; the new URL is written by the supervisor once the restarted server is live.
  • HOST/PORT submitted unchanged: sync now (repairs stale/missing settings.json).
  • Mixed HOST/PORT + token: token applied now with the live URL; new URL written after restart.

Tests: the three deferral tests were replaced with live-URL assertions (test_admin_apply_syncs_live_url_on_port_change, test_admin_apply_syncs_token_with_live_url_on_mixed_fields, test_admin_apply_syncs_live_url_when_restart_pending_from_previous_apply), plus a check that _run_supervised_server records the live URL. Full suite: 63 passed, 3 skipped (Unix-only) for the touched files; 371 passed across tests/api.

@greptileai please re-review.

Comment thread cli/entrypoints.py Outdated
@SebasSotoA

Copy link
Copy Markdown
Author

Addressing Greptile P1: avoid persisting shell tokens (commit e0b2870)

A process-environment ANTHROPIC_AUTH_TOKEN (e.g. ANTHROPIC_AUTH_TOKEN=temp fcc-server) was being written into ~/.claude/settings.json, leaving a shell-only secret on disk and causing auth failures for claude agents workers after the server restarts without that token.

Fix: guard every sync_claude_settings call with uses_process_anthropic_auth_token() — the existing Settings helper that returns True only when the token comes from the process env and is not defined in any .env/managed config file. When it returns True, the persistent sync is skipped entirely; the token is still forwarded to the live child process / server as before.

Three sites guarded:

  • cli/launchers/claude.pylaunch(): sync skipped when token is shell-only; build_claude_launcher_env still receives the token for the live claude child process.
  • cli/entrypoints.py_sync_claude_settings_on_start(): early-returns, so fcc-server startup never persists a shell-only token.
  • api/admin_routes.pyapply_admin_config(): sync block skipped; restart metadata is still computed and returned normally.

Normal flow unchanged: when ANTHROPIC_AUTH_TOKEN is set in .env or the managed ~/.fcc/.env, uses_process_anthropic_auth_token() returns False and the sync proceeds as before.

New tests:

  • test_launch_claude_skips_sync_when_token_from_process_env — asserts sync not called, child env still has the token.
  • test_serve_start_sync_skips_for_process_token — directly calls _sync_claude_settings_on_start with process-token condition, asserts not called.
  • test_admin_apply_skips_sync_when_token_from_process_env — sets shell-only token, asserts admin apply does not persist it.

Test results: 187 passed, 3 skipped (Unix-only tests skipped on Windows).

@greptileai please re-review.

@greptile-apps

greptile-apps Bot commented Jun 20, 2026

Copy link
Copy Markdown

Want your agent to iterate on Greptile's feedback? Try greploops.

@SebasSotoA

Copy link
Copy Markdown
Author

Addressing Bugbot medium: sync deferred until server is reachable (commit 7fcb5e8)

The previous approach called _sync_claude_settings_on_start() in the serve() loop before _run_supervised_server() attempted to bind. If the bind failed (port in use, etc.) ~/.claude/settings.json was already rewritten with the proxy URL even though no server was listening, leaving agent workers with a broken configuration.

Fix: the settings sync is now deferred into the existing "server ready" daemon thread that already gates on /health being reachable (preflight_proxy returning None). The _schedule_open_admin_browser function was unified into _schedule_on_server_ready(settings, *, open_admin_browser) which:

  1. Polls /health with a 30 s deadline.
  2. Once reachable: opens the admin browser (if enabled) and syncs ~/.claude/settings.json (unless token is process-only).
  3. If the server never comes up, neither action fires — so a failed bind leaves settings.json intact.

The old pre-bind call in serve() was removed entirely; _run_supervised_server now always calls _schedule_on_server_ready.

Updated tests:

  • test_schedule_open_admin_browser_* → renamed to test_schedule_on_server_ready_* to match the new function.
  • test_schedule_on_server_ready_syncs_when_server_reachable: exercises the sync path via a synchronous thread shim.
  • test_schedule_on_server_ready_skips_sync_for_process_token: verifies no sync when token is shell-only.
  • test_schedule_on_server_ready_skips_browser_when_disabled: verifies browser skip while thread is still started (sync still runs).

Test results: 66 passed, 3 skipped on affected files; 372 passed across tests/api.

@greptileai please re-review.

Comment on lines +91 to +97
merged_env: dict[str, object] = {
key: value
for key, value in existing_env.items()
if isinstance(key, str)
and not key.startswith("ANTHROPIC_")
and key not in FCC_CLAUDE_ENV_KEYS
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Keep unrelated Anthropic env

This merge removes every existing ANTHROPIC_* setting, not just the FCC-managed proxy keys or stale auth endpoint keys. If a user stores another Claude/Anthropic option in ~/.claude/settings.json, such as a model or feature flag env var, running fcc-claude silently deletes it and later plain claude agent workers start with different settings. Limit the removal to the keys this sync owns or known conflicting auth/base-url keys.

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.

fcc-claude agents: dispatched worker sessions require /login because proxy env is not persisted

1 participant