Skip to content

[FEATURE]: exec variable substitution #7487

Open

Description

@nikaro

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

I would like to be able to do exec variables substitution, example for retrieving credentials from op (1Password CLI):

{
  "$schema": "https://opencode.ai/config.json",
  "provider": {
    "anthropic": {
      "models": {},
      "options": {
        "apiKey": "{exec:op read 'op://Vault/Provider/token'}"
      }
    }
  }
}

Could be interesting for these issues:

Looks like a previous attempt at something like that was done: #2405

Activity

  1. added
    discussionUsed for feature requests, proposals, ideas, etc. Open discussion
    on Jan 9, 2026
  2. github-actions commented on Jan 9, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    Feel free to ignore if none of these address your specific case.

  3. nikaro commented on Jan 17, 2026

    @nikaro
    Author

    @thdxr any chance this would get accepted as a feature? 🥺

  4. nikaro commented on Jan 20, 2026

    @nikaro
    Author

    Closing in favor of older issue: #231

  5. reopened this on Sep 24, 2026
  6. nikaro commented on Sep 24, 2026

    @nikaro
    Author

    Re-opening as #231 has been closed without solution.
    At the time i did an implementation PR: #8917

  7. kfox1111 commented on Oct 1, 2026

    @kfox1111

    Love to be able to do this to be able to read tokens automatically from spire, or run it in agent substrate and use creds injected by the substrate. With software supply chain issues growing, we shouldnt be using static tokens anymore. We're one more trivy exploit away from disaster without this sort of thing.

  8. tkoop commented on Oct 1, 2026

    @tkoop

    I sometimes use OpenCode to do basic IT work on the local computer, or even to SSH into another computer and do IT work there. Often this requires a password for sudo, or an SSH password. It would be great if there was a way I could type in my sudo or SSH password into OpenCode in such a way that it would be remembered locally, but not stored in the chat history (or sent to the model).

    (Cursor does this very nicely.)

  9. opencodejr commented on Oct 29, 2026

    @opencodejr

    Thanks for the request! You're asking for an {exec:...} config substitution, next to {env:...} and {file:...}, so secrets like API keys can come from a command such as op read instead of sitting in plaintext. Maintainers will consider it.

    Related:

    For provider API keys you can do this now with a small local plugin. It runs your command once at startup and sets the provider's apiKey. Save it as .opencode/plugins/exec-secrets.ts in your project, or in ~/.config/opencode/plugins/ to use it everywhere:

    import { Plugin } from "@opencode/plugin"
    import { execSync } from "node:child_process"
    
    // providerID -> shell command that prints the API key on stdout
    const commands: Record<string, string> = {
      anthropic: "op read 'op://Vault/Provider/token'",
    }
    
    export default Plugin.define({
      id: "local.exec-secrets",
      async setup(ctx) {
        const keys = Object.fromEntries(
          Object.entries(commands).map(([id, command]) => [
            id,
            execSync(command, { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] }).trim(),
          ]),
        )
        await ctx.provider.transform((editor) => {
          for (const [id, apiKey] of Object.entries(keys)) {
            if (!editor.get(id)) continue
            editor.update(id, (provider) => {
              provider.settings = { ...provider.settings, apiKey }
            })
          }
        })
      },
    })

    I've checked that this type-checks against the v2 plugin API, but I haven't run it in a live session. With it in place you can leave apiKey out of your opencode.json. It only covers provider API keys; a general {exec:} that works in any config field would need support in OpenCode itself.

  10. tthoma24 commented on Oct 29, 2026

    @tthoma24

    Thanks for the request! You're asking for an {exec:...} config substitution, next to {env:...} and {file:...}, so secrets like API keys can come from a command such as op read instead of sitting in plaintext. Maintainers will consider it.

    Related:

    For provider API keys you can do this now with a small local plugin. It runs your command once at startup and sets the provider's apiKey. Save it as .opencode/plugins/exec-secrets.ts in your project, or in ~/.config/opencode/plugins/ to use it everywhere:

    import { Plugin } from "@opencode/plugin"
    import { execSync } from "node:child_process"

    // providerID -> shell command that prints the API key on stdout
    const commands: Record<string, string> = {
    anthropic: "op read 'op://Vault/Provider/token'",
    }

    export default Plugin.define({
    id: "local.exec-secrets",
    async setup(ctx) {
    const keys = Object.fromEntries(
    Object.entries(commands).map(([id, command]) => [
    id,
    execSync(command, { encoding: "utf8", stdio: ["ignore", "pipe", "inherit"] }).trim(),
    ]),
    )
    await ctx.provider.transform((editor) => {
    for (const [id, apiKey] of Object.entries(keys)) {
    if (!editor.get(id)) continue
    editor.update(id, (provider) => {
    provider.settings = { ...provider.settings, apiKey }
    })
    }
    })
    },
    })
    I've checked that this type-checks against the v2 plugin API, but I haven't run it in a live session. With it in place you can leave apiKey out of your opencode.json. It only covers provider API keys; a general {exec:} that works in any config field would need support in OpenCode itself.

    Is this AI Slop? There already was a PR and implementation of this and the maintainers did nothing with it until the AI autoclosed it.

  11. nikaro commented on Nov 6, 2026

    @nikaro
    Author

    It actually works. Good enough for me. Feel free to close the issue if you consider it does not have its place in OpenCode core.

    Edit: Actually maybe not, it though it worked because the opencode server --service from another session was still running...

  12. nikaro commented on Nov 7, 2026

    @nikaro
    Author

    Here is what worked:

    import { Plugin } from "@opencode/plugin";
    import { execSync } from "node:child_process";
    
    // Infomaniak is a models.dev catalog provider: opencode derives its baseURL from
    // $INFOMANIAK_PRODUCT_ID and resolves the credential from $INFOMANIAK_API_KEY at
    // request time. Only the key is secret; fetch it from gopass into the environment.
    export default Plugin.define({
      id: "local.exec-secrets",
      async setup() {
        process.env.INFOMANIAK_API_KEY = execSync("gopass show infomaniak_api_key", {
          encoding: "utf8",
          stdio: ["ignore", "pipe", "inherit"],
        }).trim();
      },
    });
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

discussionUsed for feature requests, proposals, ideas, etc. Open discussiondocs

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions