Skip to content

feat(plugin): expose credential.activate to server plugins - #52797

Merged
rekram1-node merged 8 commits into
anomalyco:devfrom
remorses:v2-plugin-connection-activate
Oct 13, 2026
Merged

rekram1-node merged 8 commits into
anomalyco:devfrom
remorses:v2-plugin-connection-activate

Conversation

@remorses

@remorses remorses commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #51410

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Server plugins can list saved accounts (ctx.integration.get(...).data.connections) but cannot switch the active one. The HTTP API already has POST /api/credential/:credentialID/activate (client.credential.activate, used by the TUI and opencode auth switch), but the server-plugin context had no credential domain.

This adds ctx.credential.activate({ credentialID }), following the same convention as the other HTTP-backed plugin methods:

  • typed from the client (Pick<CredentialApi, "activate">, like plugin: Pick<PluginApi, "list">)
  • Promise adapter goes through adaptApiMethod(CredentialEndpoints["credential.activate"], ...)
  • same input and result as client.credential.activate

Only activate is exposed. credential.list returns secret values, so I left it out.

I need it for a rate-limit fallback plugin: in the retry hook it activates the next account of the same provider, and the retried attempt resolves the new credential.

await ctx.session.hook("retry", async (event) => {
  if (event.error.type !== "provider.rate-limit") return
  const { data } = await ctx.integration.get({ integrationID: event.model.providerID })
  const next = data.connections.find((c) => c.type === "credential" && c.id !== current)
  if (next?.type === "credential") await ctx.credential.activate({ credentialID: next.id })
  event.decision = { retry: true, delay: 0 }
})

Changes:

  • client/src/promise/api.ts: export CredentialApi
  • plugin/src/{promise,effect}/plugin.ts: credential: Pick<CredentialApi, "activate"> on the context
  • plugin/src/promise/adapter.ts: wired through the credential.activate endpoint
  • core/src/plugin/host.ts: wired to integration.connection.activate
  • docs: new Credentials section with example and reference

How did you verify your code works?

  • new test in core/test/plugin.test.ts: a Promise plugin activates the older of two saved credentials, connection.active returns it
  • bun test test/plugin in core passes, bun run typecheck and bun run lint pass, pre-push bun run check passes

Screenshots / recordings

N/A

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

The following comment was made by an LLM, it may be inaccurate:

…vate

Match the HTTP client like the other plugin domains: ctx.credential.activate({ credentialID })
is typed from Client["credential"] and adapted through the credential.activate endpoint,
instead of a positional plugin-only integration.connection.activate(id).

Session: ses_eedc354e4ffevSfxNa6i90GdAS
@remorses remorses changed the title feat(plugin): expose integration.connection.activate to server plugins feat(plugin): expose credential.activate to server plugins Oct 6, 2026
@thdxr
thdxr changed the base branch from v2 to dev October 10, 2026 19:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: v2 plugin API: expose credential list/activate on ctx.integration

2 participants