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 +27 -0
- package/LICENSE +21 -0
- package/README.md +298 -0
- package/dist/config.d.ts +47 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +104 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +180 -0
- package/dist/index.js.map +1 -0
- package/dist/op-client.d.ts +41 -0
- package/dist/op-client.d.ts.map +1 -0
- package/dist/op-client.js +122 -0
- package/dist/op-client.js.map +1 -0
- package/dist/resolver.d.ts +37 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +98 -0
- package/dist/resolver.js.map +1 -0
- package/dist/secret-sync.d.ts +54 -0
- package/dist/secret-sync.d.ts.map +1 -0
- package/dist/secret-sync.js +64 -0
- package/dist/secret-sync.js.map +1 -0
- package/dist/tools.d.ts +32 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +261 -0
- package/dist/tools.js.map +1 -0
- package/dist/version.d.ts +9 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/examples/openclaw.exec-mode.json +58 -0
- package/examples/openclaw.store-mode.json +40 -0
- package/examples/openclaw.tools-mode.json +17 -0
- package/openclaw.plugin.json +104 -0
- package/package.json +99 -0
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
|
+
[](https://github.com/ioleksiy/openclaw-plugin-onepassword/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/openclaw-plugin-onepassword)
|
|
5
|
+
[](./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)
|
package/dist/config.d.ts
ADDED
|
@@ -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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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|