Skip to main content
This page describes the protocol between oc (the core CLI) and plugins (standalone binaries). If you’re using oc to call plugins, this explains what happens under the hood. If you’re building a plugin, this is the contract you need to implement.

Discovery

Plugins are executable binaries stored in ~/.onecli/plugins/ with the naming convention oc-{name}. When you run oc google calendar list-events, oc looks for a binary at ~/.onecli/plugins/oc-google. The plugin directory can be overridden with ONECLI_PLUGIN_DIR.

Manifest

Every plugin must respond to the manifest subcommand with a JSON object describing its capabilities:
oc-google manifest
{
  "name": "google",
  "version": "v1.0.1",
  "description": "Gmail, Calendar, Drive...",
  "auth": {
    "required": true,
    "local_flow": "oauth",
    "scopes": ["calendar.readonly", "gmail.readonly"]
  },
  "commands": [
    {
      "name": "calendar list-events",
      "description": "List upcoming calendar events",
      "args": [
        {
          "name": "--limit",
          "required": false,
          "description": "Maximum number of events to return"
        }
      ]
    }
  ]
}

Manifest fields

FieldTypeDescription
namestringPlugin name (matches binary name without oc- prefix)
versionstringSemantic version
descriptionstringShort description of what the plugin provides
auth.requiredbooleanWhether credentials are needed to run commands
auth.local_flowstringAuth flow type (e.g., "oauth"). Optional.
auth.scopesstring[]OAuth scopes or permission descriptions. Optional.
commandsarrayAvailable commands with descriptions and arguments
commands[].namestringCommand name (can include subcommands separated by spaces)
commands[].descriptionstringWhat the command does
commands[].argsarrayAvailable arguments
commands[].args[].namestringArgument name
commands[].args[].requiredbooleanWhether the argument is required
commands[].args[].descriptionstringWhat the argument does

Auth interface

Plugins that require authentication must respond to the auth subcommand. The auth flow uses newline-delimited JSON (NDJSON) on stdout:
oc-google auth
{"status": "pending", "url": "https://accounts.google.com/o/oauth2/auth?...", "code": "ABCD-1234"}
{"status": "ok", "credentials": {"access_token": "ya29...", "refresh_token": "1//...", "token_type": "Bearer", "expires_at": "2025-06-01T00:00:00Z"}}
oc reads the stdout stream and stores the first response where status is "ok" and credentials.access_token is non-empty.

Auth response fields

FieldTypeDescription
statusstring"pending", "ok", "authenticated", or "expired"
credentials.access_tokenstringThe primary authentication token
credentials.refresh_tokenstringOptional refresh token
credentials.token_typestringToken type (usually "Bearer")
credentials.expires_atstringISO 8601 expiration timestamp
urlstringURL the user should visit (for device/OAuth flows)
codestringCode to display to the user (for device flows)
messagestringHuman-readable status message

Command execution

When oc runs a plugin command, it:
  1. Resolves credentials from the vault or local storage
  2. Injects credentials as environment variables into the plugin’s process
  3. Executes the plugin binary as a subprocess with the remaining arguments
  4. Passes stdin/stdout/stderr directly between the caller and the plugin

Environment variable injection

Credentials are passed as environment variables with the naming convention OC_{PLUGINNAME}_{FIELD}:
VariableSource
OC_GOOGLE_ACCESS_TOKENcredentials.access_token
OC_GOOGLE_REFRESH_TOKENcredentials.refresh_token
OC_GOOGLE_TOKEN_TYPEcredentials.token_type
OC_GOOGLE_EXPIRES_ATcredentials.expires_at
The plugin name is uppercased. All four fields are set if available.

Execution model

oc google calendar list-events --limit 5

├── Finds: ~/.onecli/plugins/oc-google
├── Resolves credentials for "google"
├── Sets env: OC_GOOGLE_ACCESS_TOKEN=..., etc.
├── Executes: oc-google calendar list-events --limit 5

└── Plugin writes JSON to stdout, oc passes it through
The plugin inherits the caller’s stdin, stdout, and stderr. oc does not modify the plugin’s output; it passes through directly.

Meta-commands

Two subcommands are special and run without credentials:
  • manifest returns the plugin’s manifest JSON
  • auth runs the plugin’s authentication flow
All other subcommands go through credential resolution first.

Archive format

Plugin releases follow the goreleaser naming convention:
oc-{name}_{version}_{os}_{arch}.tar.gz
Example: oc-google_1.0.1_darwin_arm64.tar.gz Each release must include a checksums.txt file with SHA256 hashes:
a1b2c3d4...  oc-google_1.0.1_darwin_arm64.tar.gz
e5f6g7h8...  oc-google_1.0.1_linux_amd64.tar.gz