openclaw-plugin-onepassword 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [0.1.0] - 2026-09-01
10
+
11
+ ### Added
12
+
13
+ - Initial release.
14
+ - **In-process store sync**: resolve `op://` references from 1Password using
15
+ `@1password/sdk` and write them into the OpenClaw shared store via the
16
+ `secrets.store.set` Gateway RPC, bypassing the exec secret sandbox introduced
17
+ in OpenClaw v2026.8.1. Runs at Gateway startup and on demand.
18
+ - **`onepassword.sync`** and **`onepassword.status`** gateway methods.
19
+ - **Optional agent tools**: `1password_list_vaults`, `1password_list_items`,
20
+ `1password_get_item`, `1password_read_field`, and (opt-in) `1password_create_item`,
21
+ `1password_update_item`, `1password_delete_item`. Concealed fields are redacted
22
+ by default.
23
+ - **Optional exec resolver mode** via `secretProviderIntegrations` for setups
24
+ that allowlist 1Password egress and prefer `op://` ids on `SecretRef`s.
25
+
26
+ [Unreleased]: https://github.com/ioleksiy/openclaw-plugin-onepassword/compare/v0.1.0...HEAD
27
+ [0.1.0]: https://github.com/ioleksiy/openclaw-plugin-onepassword/releases/tag/v0.1.0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 openclaw-plugin-onepassword contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,298 @@
1
+ # openclaw-plugin-onepassword
2
+
3
+ [![CI](https://github.com/ioleksiy/openclaw-plugin-onepassword/actions/workflows/ci.yml/badge.svg)](https://github.com/ioleksiy/openclaw-plugin-onepassword/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/openclaw-plugin-onepassword.svg)](https://www.npmjs.com/package/openclaw-plugin-onepassword)
5
+ [![license](https://img.shields.io/npm/l/openclaw-plugin-onepassword.svg)](./LICENSE)
6
+
7
+ A native [OpenClaw](https://openclaw.ai) plugin that resolves **[1Password](https://1password.com) secrets in-process** inside the Gateway — no `op` CLI, no child process, no exec sandbox — and writes them into OpenClaw's shared secret store. It also exposes optional 1Password vault/item **agent tools**.
8
+
9
+ > **Why this exists.** OpenClaw **v2026.8.1** introduced a security sandbox for `exec` secret providers that blocks **filesystem writes and network access** during provider execution. That breaks the common pattern of using `op read` as an exec provider (you'll see errors like `sh: cannot open /tmp/op_out.txt` and blocked network calls). This plugin runs **inside the Gateway process**, where the sandbox does not apply, and uses the official [`@1password/sdk`](https://github.com/1Password/onepassword-sdk-js) over HTTPS.
10
+
11
+ ---
12
+
13
+ ## Contents
14
+
15
+ - [How it works](#how-it-works)
16
+ - [Requirements](#requirements)
17
+ - [Install](#install)
18
+ - [Quick start (store sync — recommended)](#quick-start-store-sync--recommended)
19
+ - [Configuration reference](#configuration-reference)
20
+ - [Agent tools (optional)](#agent-tools-optional)
21
+ - [Gateway methods](#gateway-methods)
22
+ - [Refreshing secrets at runtime](#refreshing-secrets-at-runtime)
23
+ - [Exec resolver mode (advanced)](#exec-resolver-mode-advanced)
24
+ - [Security notes](#security-notes)
25
+ - [Troubleshooting](#troubleshooting)
26
+ - [Development](#development)
27
+ - [Publishing](#publishing)
28
+ - [License](#license)
29
+
30
+ ---
31
+
32
+ ## How it works
33
+
34
+ There are two ways a plugin can feed secrets to OpenClaw. This plugin ships both, but **the in-process store sync is the recommended path** because it is the only one that reliably bypasses the exec sandbox.
35
+
36
+ | Mode | Runs where | Bypasses exec sandbox? | `SecretRef` you write | Vault/item paths live in |
37
+ | ---------------------------- | ---------------------- | -------------------------------- | -------------------------------------- | ----------------------------- |
38
+ | **Store sync** (default) | In-process (Gateway) | ✅ Yes | `source: "store"` | plugin `config.secrets` map |
39
+ | **Exec resolver** (advanced) | Sandboxed child `node` | ⚠️ Only with egress allowlisting | `source: "exec"` + `pluginIntegration` | the `SecretRef.id` (`op://…`) |
40
+
41
+ **Store sync**, in one picture:
42
+
43
+ ```
44
+ Gateway start / onepassword.sync
45
+
46
+
47
+ read OP_SERVICE_ACCOUNT_TOKEN (env)
48
+
49
+
50
+ @1password/sdk ──HTTPS──▶ 1Password API
51
+
52
+
53
+ secrets.store.set { name, value } (in-process Gateway RPC)
54
+
55
+
56
+ OpenClaw shared store ◀── resolved by SecretRefs with source:"store"
57
+ ```
58
+
59
+ The plugin never writes secrets to `openclaw.json`, environment variables, or disk of its own. Values live only in OpenClaw's store (SQLite, `0600`/`0700` permissions, team scope).
60
+
61
+ ## Requirements
62
+
63
+ - **OpenClaw** `>= 2026.8.0` (Gateway runs on Node `>= 22.22.3`).
64
+ - A **1Password service account** token — see [1Password service accounts](https://developer.1password.com/docs/service-accounts). The service account must have access to the vaults/items you reference.
65
+
66
+ ## Install
67
+
68
+ From npm (recommended):
69
+
70
+ ```bash
71
+ openclaw plugins install openclaw-plugin-onepassword
72
+ ```
73
+
74
+ Or from a local checkout:
75
+
76
+ ```bash
77
+ openclaw plugins install ./openclaw-plugin-onepassword
78
+ ```
79
+
80
+ Then provide the service account token to the **Gateway process** via the environment variable (default `OP_SERVICE_ACCOUNT_TOKEN`). Keep it out of `openclaw.json`, docker-compose files, and shell history — use your process manager's secret mechanism (systemd `LoadCredential`, Docker/Kubernetes secrets, etc.).
81
+
82
+ ```bash
83
+ export OP_SERVICE_ACCOUNT_TOKEN="ops_..."
84
+ ```
85
+
86
+ ## Quick start (store sync — recommended)
87
+
88
+ 1. **Enable the plugin and map store keys to 1Password references** in `openclaw.json`:
89
+
90
+ ```json
91
+ {
92
+ "plugins": {
93
+ "entries": {
94
+ "onepassword": {
95
+ "enabled": true,
96
+ "config": {
97
+ "serviceAccountTokenEnvVar": "OP_SERVICE_ACCOUNT_TOKEN",
98
+ "secrets": {
99
+ "SLACK_BOT_TOKEN": "op://MyVault/SlackBot/bot_token",
100
+ "SLACK_APP_TOKEN": "op://MyVault/SlackBot/app_token",
101
+ "OPENAI_API_KEY": "op://MyVault/OpenAI/credential"
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }
108
+ ```
109
+
110
+ Store keys must match `^[A-Z][A-Z0-9_]{0,127}$` (OpenClaw store-id grammar). Values are standard [1Password secret references](https://developer.1password.com/docs/cli/secret-references/): `op://Vault/Item[/Section]/Field`.
111
+
112
+ 2. **Reference those store keys** anywhere OpenClaw accepts a `SecretRef`, using `source: "store"`:
113
+
114
+ ```json
115
+ {
116
+ "channels": {
117
+ "slack": {
118
+ "accounts": {
119
+ "myworkspace": {
120
+ "botToken": { "source": "store", "id": "SLACK_BOT_TOKEN" },
121
+ "appToken": { "source": "store", "id": "SLACK_APP_TOKEN" }
122
+ }
123
+ }
124
+ }
125
+ },
126
+ "agents": {
127
+ "defaults": {
128
+ "model": {
129
+ "providers": {
130
+ "openai": {
131
+ "apiKey": { "source": "store", "id": "OPENAI_API_KEY" }
132
+ }
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ 3. **Start the Gateway.** On startup the plugin fetches each reference from 1Password and writes it into the store; the referencing channels/providers then initialize with resolved credentials. Because store values persist, subsequent restarts are covered even before the first sync completes.
141
+
142
+ See [`examples/`](./examples) for complete config files.
143
+
144
+ ## Configuration reference
145
+
146
+ All keys live under `plugins.entries.onepassword.config`.
147
+
148
+ | Key | Type | Default | Description |
149
+ | --------------------------- | ------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- |
150
+ | `serviceAccountTokenEnvVar` | string | `"OP_SERVICE_ACCOUNT_TOKEN"` | Name of the environment variable holding the service account token. |
151
+ | `secrets` | object | `{}` | Map of **store key → `op://…` reference**. Store keys must match `^[A-Z][A-Z0-9_]{0,127}$`. |
152
+ | `syncOnStartup` | boolean | `true` | Fetch `secrets` from 1Password and write to the store at Gateway startup. |
153
+ | `failFastOnStartup` | boolean | `false` | If `true`, a startup sync failure throws and prevents startup. If `false`, log and fall back to last-known-good store values. |
154
+ | `integrationName` | string | `"openclaw-plugin-onepassword"` | Integration name reported to 1Password audit logs. |
155
+ | `requestTimeoutMs` | number | `15000` | Per-operation timeout for 1Password SDK calls. |
156
+ | `tools.enabled` | boolean | `false` | Register the read-only 1Password agent tools. |
157
+ | `tools.allowWrite` | boolean | `false` | Also register create/update/delete tools. Requires `tools.enabled`. |
158
+
159
+ The plugin **hardcodes no vault names, item paths, or field names**. The only 1Password-specific configuration is the env var name and the `secrets` map you provide.
160
+
161
+ ## Agent tools (optional)
162
+
163
+ Set `tools.enabled: true` to expose in-process tools to the agent. Read tools redact concealed field values by default.
164
+
165
+ | Tool | Requires `allowWrite` | Description |
166
+ | ----------------------- | --------------------- | -------------------------------------------------------------------------- |
167
+ | `1password_list_vaults` | | List vaults accessible to the service account. |
168
+ | `1password_list_items` | | List item overviews in a vault. |
169
+ | `1password_get_item` | | Get a full item (concealed fields redacted unless `includeSecrets: true`). |
170
+ | `1password_read_field` | | Resolve a single `op://…` reference to its value. |
171
+ | `1password_create_item` | ✅ | Create a new item. |
172
+ | `1password_update_item` | ✅ | Update an existing item. |
173
+ | `1password_delete_item` | ✅ | Delete an item. |
174
+
175
+ ```json
176
+ {
177
+ "plugins": {
178
+ "entries": {
179
+ "onepassword": {
180
+ "enabled": true,
181
+ "config": { "tools": { "enabled": true, "allowWrite": false } }
182
+ }
183
+ }
184
+ }
185
+ }
186
+ ```
187
+
188
+ ## Gateway methods
189
+
190
+ Both require the `operator.admin` scope.
191
+
192
+ - **`onepassword.sync`** — re-fetch every configured secret from 1Password and write it into the store. Returns `{ written, total, resolveErrors, storeErrors }`.
193
+ - **`onepassword.status`** — non-secret health/config summary: `{ version, serviceAccountTokenEnvVar, tokenPresent, syncOnStartup, managedStoreKeys, toolsEnabled, toolsWriteEnabled }`.
194
+
195
+ ## Refreshing secrets at runtime
196
+
197
+ - **Rotate a value in 1Password**, then call **`onepassword.sync`** (or restart the Gateway). The plugin re-fetches and writes fresh values; `secrets.store.set` triggers a live runtime refresh so dependent channels/providers pick up the new value without a full restart.
198
+ - `openclaw secrets reload` re-reads the **store**; run `onepassword.sync` first if you need the store repopulated from 1Password.
199
+
200
+ > Prefer native `openclaw secrets reload` to re-fetch directly from 1Password? Use the [exec resolver mode](#exec-resolver-mode-advanced), which OpenClaw re-invokes on reload — at the cost of requiring egress allowlisting.
201
+
202
+ ## Exec resolver mode (advanced)
203
+
204
+ The plugin also declares a `secretProviderIntegrations` entry so it can act as a plugin-managed **exec** secret provider using standard `op://` ids:
205
+
206
+ ```json
207
+ {
208
+ "secrets": {
209
+ "providers": {
210
+ "op": {
211
+ "source": "exec",
212
+ "pluginIntegration": { "pluginId": "onepassword", "integrationId": "op" }
213
+ }
214
+ }
215
+ },
216
+ "channels": {
217
+ "slack": {
218
+ "accounts": {
219
+ "myworkspace": {
220
+ "botToken": {
221
+ "source": "exec",
222
+ "provider": "op",
223
+ "id": "op://MyVault/SlackBot/bot_token"
224
+ }
225
+ }
226
+ }
227
+ }
228
+ }
229
+ }
230
+ ```
231
+
232
+ **Caveat:** this runs OpenClaw's resolver as a **sandboxed child `node` process** (`command: "${node}"`). Under the v2026.8.1 sandbox its network is blocked, so it can only reach the 1Password API if you allowlist egress for secret resolution:
233
+
234
+ ```json
235
+ {
236
+ "secrets": {
237
+ "egressProxy": {
238
+ "enabled": true,
239
+ "allowedHosts": ["my.1password.com", "my.1password.eu", "my.1password.ca"]
240
+ }
241
+ }
242
+ }
243
+ ```
244
+
245
+ Use the host that matches your 1Password account region. If your environment cannot allow this egress, use the [store sync](#quick-start-store-sync--recommended) mode instead. The exec resolver reads the token from `OP_SERVICE_ACCOUNT_TOKEN` (override the env var name with `OP_RESOLVER_TOKEN_ENV_VAR`).
246
+
247
+ ## Security notes
248
+
249
+ - **The token is the crown jewel.** Anyone with the service account token has the service account's access. Scope the service account to the minimum vaults required, and inject the token via your platform's secret mechanism — never commit it.
250
+ - **No plaintext secrets in config.** The plugin only reads an env var name and `op://` references; resolved values live only in OpenClaw's store.
251
+ - **Plugin code runs in your Gateway process** with full Gateway privileges (this is true of every OpenClaw plugin). Review the source before installing.
252
+ - **Write tools are opt-in** (`tools.allowWrite`) and **concealed fields are redacted** by read tools unless `includeSecrets: true`.
253
+ - Report vulnerabilities per [SECURITY.md](./SECURITY.md).
254
+
255
+ ## Troubleshooting
256
+
257
+ | Symptom | Likely cause / fix |
258
+ | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
259
+ | `... token not found` at startup | The env var named by `serviceAccountTokenEnvVar` is unset on the Gateway process. |
260
+ | `SECRETS_PROVIDER_DEGRADED` for a store ref | The store key hasn't been populated yet — run `onepassword.sync`, or check the startup logs for resolve errors. |
261
+ | Resolve error `NOT_FOUND` | The `op://` reference is wrong or the service account can't access that vault/item/field. |
262
+ | Exec resolver returns nothing | Sandbox is blocking network — add your 1Password host to `secrets.egressProxy.allowedHosts`, or switch to store sync. |
263
+
264
+ ## Development
265
+
266
+ ```bash
267
+ npm install --ignore-scripts # openclaw's preinstall gate is skipped here
268
+ npm run build # tsc -> dist/
269
+ npm run typecheck
270
+ npm run lint
271
+ npm test
272
+ ```
273
+
274
+ > `--ignore-scripts` is used because the `openclaw` dev dependency runs a Node-version preinstall check; the plugin itself only needs its type definitions to build and test.
275
+
276
+ ## Releasing
277
+
278
+ Releases are **automated on push to `main`**. The `Release` workflow publishes to
279
+ npm (with [provenance](https://docs.npmjs.com/generating-provenance-statements))
280
+ and creates a GitHub release **only when the version in `package.json` is not yet
281
+ on npm** — ordinary commits are a no-op.
282
+
283
+ To cut a release:
284
+
285
+ ```bash
286
+ npm run release:prepare -- <x.y.z> # bumps the 3 version files + rolls CHANGELOG
287
+ # edit CHANGELOG.md for the new section, then:
288
+ npm run verify
289
+ git commit -am "chore(release): v<x.y.z>" && git push origin main
290
+ ```
291
+
292
+ The workflow publishes the package and tags `v<x.y.z>`. It requires an `NPM_TOKEN`
293
+ repository secret (an npm **automation** token). See [CONTRIBUTING.md → Releasing](./CONTRIBUTING.md#releasing)
294
+ and [AGENTS.md](./AGENTS.md) for details and the semver policy.
295
+
296
+ ## License
297
+
298
+ [MIT](./LICENSE)
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Plugin configuration parsing and validation.
3
+ *
4
+ * The plugin intentionally hardcodes no vault names, item paths, or field
5
+ * names. Everything 1Password-specific is supplied by the operator: the
6
+ * service-account token comes from an environment variable, and the
7
+ * `op://Vault/Item/Field` references live in the operator's `openclaw.json`.
8
+ */
9
+ /** Store keys use OpenClaw's env-var grammar. */
10
+ export declare const STORE_KEY_PATTERN: RegExp;
11
+ /** 1Password secret reference grammar, e.g. `op://Vault/Item/field`. */
12
+ export declare const OP_REFERENCE_PATTERN: RegExp;
13
+ export declare const DEFAULT_TOKEN_ENV_VAR = "OP_SERVICE_ACCOUNT_TOKEN";
14
+ export declare const DEFAULT_INTEGRATION_NAME = "openclaw-plugin-onepassword";
15
+ export declare const DEFAULT_REQUEST_TIMEOUT_MS = 15000;
16
+ export interface OnePasswordToolsConfig {
17
+ enabled: boolean;
18
+ allowWrite: boolean;
19
+ }
20
+ export interface OnePasswordPluginConfig {
21
+ serviceAccountTokenEnvVar: string;
22
+ integrationName: string;
23
+ requestTimeoutMs: number;
24
+ syncOnStartup: boolean;
25
+ failFastOnStartup: boolean;
26
+ /** Map of OpenClaw store key -> 1Password secret reference. */
27
+ secrets: Record<string, string>;
28
+ tools: OnePasswordToolsConfig;
29
+ }
30
+ export declare class ConfigError extends Error {
31
+ readonly name = "ConfigError";
32
+ }
33
+ /** Validate a single `storeKey -> op://...` secrets mapping. */
34
+ export declare function parseSecretsMap(value: unknown): Record<string, string>;
35
+ /**
36
+ * Parse and validate the plugin config, applying defaults. Throws
37
+ * {@link ConfigError} on any invalid input so misconfiguration surfaces at
38
+ * startup instead of at first secret resolution.
39
+ */
40
+ export declare function parsePluginConfig(input: unknown): OnePasswordPluginConfig;
41
+ /**
42
+ * Read the service account token from the configured environment variable.
43
+ * Returns `undefined` when the variable is unset or empty so callers can
44
+ * decide whether that is fatal (startup sync) or merely degrading (tools).
45
+ */
46
+ export declare function readServiceAccountToken(config: Pick<OnePasswordPluginConfig, "serviceAccountTokenEnvVar">, env?: NodeJS.ProcessEnv): string | undefined;
47
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,iDAAiD;AACjD,eAAO,MAAM,iBAAiB,QAA4B,CAAC;AAE3D,wEAAwE;AACxE,eAAO,MAAM,oBAAoB,QAAsB,CAAC;AAExD,eAAO,MAAM,qBAAqB,6BAA6B,CAAC;AAChE,eAAO,MAAM,wBAAwB,gCAAgC,CAAC;AACtE,eAAO,MAAM,0BAA0B,QAAS,CAAC;AAEjD,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,yBAAyB,EAAE,MAAM,CAAC;IAClC,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,+DAA+D;IAC/D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,sBAAsB,CAAC;CAC/B;AAED,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAyB,IAAI,iBAAiB;CAC/C;AAiCD,gEAAgE;AAChE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAiBtE;AAYD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,uBAAuB,CAgBzE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE,2BAA2B,CAAC,EAClE,GAAG,GAAE,MAAM,CAAC,UAAwB,GACnC,MAAM,GAAG,SAAS,CAMpB"}
package/dist/config.js ADDED
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Plugin configuration parsing and validation.
3
+ *
4
+ * The plugin intentionally hardcodes no vault names, item paths, or field
5
+ * names. Everything 1Password-specific is supplied by the operator: the
6
+ * service-account token comes from an environment variable, and the
7
+ * `op://Vault/Item/Field` references live in the operator's `openclaw.json`.
8
+ */
9
+ /** Store keys use OpenClaw's env-var grammar. */
10
+ export const STORE_KEY_PATTERN = /^[A-Z][A-Z0-9_]{0,127}$/;
11
+ /** 1Password secret reference grammar, e.g. `op://Vault/Item/field`. */
12
+ export const OP_REFERENCE_PATTERN = /^op:\/\/[^/]+\/.+/;
13
+ export const DEFAULT_TOKEN_ENV_VAR = "OP_SERVICE_ACCOUNT_TOKEN";
14
+ export const DEFAULT_INTEGRATION_NAME = "openclaw-plugin-onepassword";
15
+ export const DEFAULT_REQUEST_TIMEOUT_MS = 15_000;
16
+ export class ConfigError extends Error {
17
+ name = "ConfigError";
18
+ }
19
+ function asRecord(value) {
20
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
21
+ return {};
22
+ }
23
+ return value;
24
+ }
25
+ function optionalString(value, field) {
26
+ if (value === undefined)
27
+ return undefined;
28
+ if (typeof value !== "string" || value.trim().length === 0) {
29
+ throw new ConfigError(`"${field}" must be a non-empty string.`);
30
+ }
31
+ return value.trim();
32
+ }
33
+ function optionalBoolean(value, field) {
34
+ if (value === undefined)
35
+ return undefined;
36
+ if (typeof value !== "boolean") {
37
+ throw new ConfigError(`"${field}" must be a boolean.`);
38
+ }
39
+ return value;
40
+ }
41
+ function optionalPositiveNumber(value, field) {
42
+ if (value === undefined)
43
+ return undefined;
44
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
45
+ throw new ConfigError(`"${field}" must be a positive number.`);
46
+ }
47
+ return value;
48
+ }
49
+ /** Validate a single `storeKey -> op://...` secrets mapping. */
50
+ export function parseSecretsMap(value) {
51
+ const raw = asRecord(value);
52
+ const out = {};
53
+ for (const [key, ref] of Object.entries(raw)) {
54
+ if (!STORE_KEY_PATTERN.test(key)) {
55
+ throw new ConfigError(`secrets store key "${key}" is invalid; keys must match ${STORE_KEY_PATTERN.source} (uppercase env-var grammar).`);
56
+ }
57
+ if (typeof ref !== "string" || !OP_REFERENCE_PATTERN.test(ref)) {
58
+ throw new ConfigError(`secrets["${key}"] must be a 1Password reference like "op://Vault/Item/field"; got ${JSON.stringify(ref)}.`);
59
+ }
60
+ out[key] = ref;
61
+ }
62
+ return out;
63
+ }
64
+ function parseTools(value) {
65
+ const raw = asRecord(value);
66
+ const enabled = optionalBoolean(raw.enabled, "tools.enabled") ?? false;
67
+ const allowWrite = optionalBoolean(raw.allowWrite, "tools.allowWrite") ?? false;
68
+ if (allowWrite && !enabled) {
69
+ throw new ConfigError('"tools.allowWrite" requires "tools.enabled" to be true.');
70
+ }
71
+ return { enabled, allowWrite };
72
+ }
73
+ /**
74
+ * Parse and validate the plugin config, applying defaults. Throws
75
+ * {@link ConfigError} on any invalid input so misconfiguration surfaces at
76
+ * startup instead of at first secret resolution.
77
+ */
78
+ export function parsePluginConfig(input) {
79
+ const raw = asRecord(input);
80
+ return {
81
+ serviceAccountTokenEnvVar: optionalString(raw.serviceAccountTokenEnvVar, "serviceAccountTokenEnvVar") ??
82
+ DEFAULT_TOKEN_ENV_VAR,
83
+ integrationName: optionalString(raw.integrationName, "integrationName") ?? DEFAULT_INTEGRATION_NAME,
84
+ requestTimeoutMs: optionalPositiveNumber(raw.requestTimeoutMs, "requestTimeoutMs") ??
85
+ DEFAULT_REQUEST_TIMEOUT_MS,
86
+ syncOnStartup: optionalBoolean(raw.syncOnStartup, "syncOnStartup") ?? true,
87
+ failFastOnStartup: optionalBoolean(raw.failFastOnStartup, "failFastOnStartup") ?? false,
88
+ secrets: parseSecretsMap(raw.secrets),
89
+ tools: parseTools(raw.tools),
90
+ };
91
+ }
92
+ /**
93
+ * Read the service account token from the configured environment variable.
94
+ * Returns `undefined` when the variable is unset or empty so callers can
95
+ * decide whether that is fatal (startup sync) or merely degrading (tools).
96
+ */
97
+ export function readServiceAccountToken(config, env = process.env) {
98
+ const token = env[config.serviceAccountTokenEnvVar];
99
+ if (typeof token !== "string" || token.trim().length === 0) {
100
+ return undefined;
101
+ }
102
+ return token.trim();
103
+ }
104
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * openclaw-plugin-onepassword
3
+ *
4
+ * Native OpenClaw plugin that resolves 1Password secrets *in-process* (inside
5
+ * the Gateway, not as a sandboxed child) and writes them into OpenClaw's shared
6
+ * secret store, plus optional agent tools for vault/item operations.
7
+ *
8
+ * Why in-process: OpenClaw v2026.8.1 sandboxes exec secret providers, blocking
9
+ * filesystem writes and network access — which breaks `op read` and any other
10
+ * network-dependent exec resolver. Running inside the Gateway process avoids the
11
+ * sandbox entirely, so the official `@1password/sdk` can reach the 1Password API
12
+ * over HTTPS normally.
13
+ */
14
+ declare const _default: Omit<{
15
+ id: string;
16
+ name: string;
17
+ description: string;
18
+ kind?: import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginDefinition["kind"];
19
+ configSchema?: import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginConfigSchema | (() => import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginConfigSchema);
20
+ reload?: import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginDefinition["reload"];
21
+ nodeHostCommands?: import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginDefinition["nodeHostCommands"];
22
+ securityAuditCollectors?: import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginDefinition["securityAuditCollectors"];
23
+ register: NonNullable<import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginDefinition["register"]>;
24
+ }, "configSchema"> & {
25
+ configSchema: import("openclaw/plugin-sdk/plugin-entry").OpenClawPluginConfigSchema;
26
+ };
27
+ export default _default;
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;;;;;;;;;;;;;;AAwFH,wBAqHG"}