Files
OpenViking/examples/openclaw-plugin/tests
Ziyang Guo abc387955d feat(openclaw-plugin): SecretRef support for config.apiKey (#3522) (#3618)
Issue #3522 — the OpenClaw plugin's `config.apiKey` only accepted a plain
string, resolved through local `${ENV_VAR}` interpolation. `INSTALL*.md`
documented this as a known limitation: users who store their other OpenClaw
provider credentials (LLM, TTS, MCP servers) through the standard
`{source, id[, provider]}` SecretRef mechanism (env / file mount /
exec-backed vault such as 1Password, Vault, gopass) had to keep the
OpenViking key as cleartext inside `openclaw.json`.

### config.ts — `string | OpenVikingSecretRef` widening

* Add `OpenVikingSecretRef = "env"|"file"|"exec"` discriminated union type,
  matching the shape OpenClaw core uses for its own credential fields
  (`env` + `file` implemented in-plugin, `exec` forwarded to `child_process`
  so providers like `@transmitt0r/openclaw-plugin-onepassword` can manage
  the OpenViking key without SDK coupling).
* Add `resolveSecret()` resolver with explicit, actionable errors:
  - env: unset var = throw, no silent empty fallback
  - file: `~` expanded, UTF-8 read, whitespace trimmed; unreadable file
    rethrows with the OpenViking field name prefixed so config misconfigs
    surface with a clear label and path
  - exec: lazy `require("node:child_process").execFileSync(provider,[id])`,
    stdout trimmed, 15s timeout; errors prefixed with provider + id
  - unknown source / missing id / missing exec provider = explicit throw
* `memoryOpenVikingConfigSchema.parse()` widens `rawApiKey` to
  `string | OpenVikingSecretRef`, then passes it through
  `resolveSecret(rawApiKey, "config.apiKey")` *before* the existing
  `resolveEnvVars` pass. Plain strings transparently fall through
  `resolveSecret` unchanged, so `${ENV_VAR}` interpolation is preserved
  100% backward-compatibly.
* `OPENVIKING_API_KEY` env fallback is unchanged and triggers only when the
  `apiKey` config key is absent — a user who deliberately sets `apiKey: ""`
  still gets "" (explicitly unauthenticated), not the env fallback.
* `uiHints.apiKey.help` documents the SecretRef shape and recommends it.

### openclaw.plugin.json — widening schema + UI hints

* `configSchema.properties.apiKey` becomes `oneOf: [string, env ref, file ref, exec ref]`.
  Each object variant has a `title`, `additionalProperties: false`,
  `required`, and explicit description per field, so OpenClaw's config UI
  can render them individually instead of showing a generic JSON object blob.
* `uiHints.apiKey.help` matches the new config.ts wording.

### INSTALL.md / INSTALL-ZH.md — SecretRef usage tables

Replace the old "plaintext / chmod 0600" caveat bullet with a 3-row table
(env / file / exec) showing example JSON + notes (Kubernetes secretKeyRef
mount for `file`, 1Password `op://` URL convention for `exec`). The
backward-compat string path is retained at the end of the new bullet so
existing deployments that haven't migrated yet still get the old permission
advice — no surprise behaviour for upgrading users.

### tests/ut/config.test.ts — SecretRef regression suite (10 new cases)

Under a new `describe("… SecretRef (#3522)")`:

1. Backward compat: `${OV_KEY}` interpolation still resolves.
2. env source — happy path with a fresh env var.
3. env source — unset var throws, no silent fallback.
4. file source — real `mkdtemp`-created file, trimmed whitespace. Cleanup
   in `afterEach`.
5. file source — missing-path error message contains readable label + path.
6. exec source — `vi.spyOn(child_process.execFileSync)` asserts provider +
   args, stdout trimmed.
7. exec source — missing `provider` field errors.
8. Schema validation — unknown `source` and missing `id` each throw with
   error messages that name the problem.
9. Env fallback boundary — explicit `apiKey: ""` is NOT overridden by
   OPENVIKING_API_KEY, but `apiKey` absent IS (backward-compat behaviour
   contract pinned with a test so future refactors can't regress).

Covers every branch inside `resolveSecret()`, plus the backward-compat
contracts issue #3522 called out.
2026-07-30 16:21:06 +08:00
..