Skip to content

feat(setup): persist PowerContext Agent authorization - #1548

Open
frf12 wants to merge 8 commits into
oceanbase:masterfrom
frf12:feat/persist-agent-authorization
Open

feat(setup): persist PowerContext Agent authorization#1548
frf12 wants to merge 8 commits into
oceanbase:masterfrom
frf12:feat/persist-agent-authorization

Conversation

@frf12

@frf12 frf12 commented Sep 10, 2026

Copy link
Copy Markdown
Member

Closes #1538.

Summary

  • Persist a setup-time PowerContext authorization in a host-owned, URL-bound credential file.
  • Load credentials for Codex, Claude Code, DSH, OpenCode, Pi, and WorkBuddy without requiring an export before every session.
  • Keep credentials out of plugin configuration and diagnostics; reject unsafe permissions, malformed records, and URL mismatches.
  • Add setup authorization state reporting and user-facing quick-start documentation.

Validation

  • make check
  • Focused Python suites: 111 passed
  • OpenCode: 25 passed
  • Pi: 50 passed
  • DSH: 177 passed

This PR supersedes the closed draft PR #1539 and contains implementation, tests, and user-facing documentation only.

AI usage

OpenAI Codex was used to inspect the repository, implement the change, and run validation. The human selected the host-owned credential persistence policy.

Comment thread src/powercontext/cli/system.py Outdated
authorization_state = configure_stored_authorization(
"codex",
server_url=DEFAULT_CLAUDE_CODE_SERVER_URL,
value=os.environ.get("POWERCONTEXT_CLIENT_API_TOKEN"),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] Wire saved credentials into native MCP

Saving this file only updates the hook credential source. Codex's .mcp.json still reads Authorization from the environment; Claude Code and WorkBuddy have the same gap. With a matching saved credential and no auth environment variable, the native Codex CLI sent /mcp without Authorization. Adding the environment override completed initialize and tools/list. Please connect native MCP to the same URL-bound credential source so setup actually removes the need for per-session exports.

if path.is_symlink() or not path.is_file() or stat.S_IMODE(path.stat().st_mode) & 0o077:
return None
payload = json.loads(path.read_text(encoding="utf-8"))
if payload.get("version") != 1 or _http_base_url(payload["server_url"]) != _http_base_url(server_url):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P1] Validate saved base URLs without requiring /mcp

_http_base_url() requires its input to end in /mcp, but both the saved URL and the server_url argument are already base URLs. A normal setup therefore raises ValueError here and silently discards the saved credential. I reproduced setup returning configured while CodexPluginSettings.authorization remained None. Please use base-URL normalization for this comparison and cover the setup-to-load path.

Comment thread src/powercontext/cli/workbuddy.py Outdated
data_dir=str(data_dir),
authorization_state=configure_stored_authorization(
"workbuddy",
server_url="http://127.0.0.1:8000",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Bind the credential to the configured server

This hardcodes port 8000 even when the host uses another endpoint. Running the WorkBuddy installer with POWERCONTEXT_WORKBUDDY_SERVER_URL=http://127.0.0.1:18765 still returned configured and saved port 8000; the hook then rejected the credential because the URLs differed. DSH, OpenCode, and Pi also hardcode this value. Please resolve the effective host endpoint before persisting the credential.

def _stored_authorization(*, server_url: str, root: Path) -> str | None:
path = root / "powercontext" / "credentials.json"
try:
if path.is_symlink() or not path.is_file() or stat.S_IMODE(path.stat().st_mode) & 0o077:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Restrict the POSIX permission check to POSIX

All six plugin loaders apply mode & 0o077 unconditionally, although the shared reader explicitly excludes Windows. Windows emulates these mode bits without separate owner/group/other permissions, so this rejects normally saved credentials there. Please use platform-appropriate permission handling. This finding is based on static analysis, not a Windows run; see the Python and Node documentation.

stored_url = normalize_server_url(payload["server_url"])
authorization = normalize_authorization(payload["authorization"])
effective_url = normalize_server_url(server_url)
except (OSError, TypeError, ValueError, json.JSONDecodeError):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Handle malformed credential records consistently

A file containing {"version":1} raises an uncaught KeyError when the required fields are read, so rerunning setup fails instead of reporting invalid. Separately, a file containing [] raises AttributeError in all three Python plugin loaders at payload.get(). Both cases were reproduced. Please validate the object and field types and return the documented invalid/no-credential result for malformed records.

return AuthorizationResolution("configured", authorization)


def clear_stored_authorization(path: Path) -> Literal["cleared", "not_configured"]:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

[P2] Expose credential clearing through setup

This helper has no production callers, and the setup commands do not expose --clear-authorization. Running setup workbuddy --clear-authorization --json returns No such option; rerunning without a token preserves the saved credential. Please wire the explicit clear option into the supported setup commands, as required by #1538, while preserving unrelated host configuration.

@frf12
frf12 force-pushed the feat/persist-agent-authorization branch from 4f021ce to 66ce067 Compare September 10, 2026 09:13

@Teingi Teingi left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@frf12
frf12 force-pushed the feat/persist-agent-authorization branch from fd5eb3d to 17a4916 Compare September 10, 2026 09:52
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.

feat(setup): persist PowerContext Agent authorization

2 participants