@graphorin/secret-1password 0.5.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 +12 -0
- package/LICENSE +21 -0
- package/README.md +170 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/op-cli.d.ts +93 -0
- package/dist/op-cli.d.ts.map +1 -0
- package/dist/op-cli.js +180 -0
- package/dist/op-cli.js.map +1 -0
- package/dist/resolver.d.ts +72 -0
- package/dist/resolver.d.ts.map +1 -0
- package/dist/resolver.js +71 -0
- package/dist/resolver.js.map +1 -0
- package/package.json +63 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# @graphorin/secret-1password
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Initial release of the reference 1Password secret-resolver adapter for the
|
|
8
|
+
Graphorin framework. Registers the `op://` scheme on top of the
|
|
9
|
+
`@graphorin/security` resolver registry, shells out to the official
|
|
10
|
+
1Password CLI, and serves as the canonical template community packages
|
|
11
|
+
should follow when wiring HashiCorp Vault, AWS Secrets Manager, GCP
|
|
12
|
+
Secret Manager, Azure Key Vault, Bitwarden, or Unix `pass`.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Oleksiy Stepurenko
|
|
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,170 @@
|
|
|
1
|
+
# @graphorin/secret-1password
|
|
2
|
+
|
|
3
|
+
> Reference 1Password secret-resolver adapter for the
|
|
4
|
+
> [Graphorin](https://github.com/o-stepper/graphorin) framework.
|
|
5
|
+
> Registers the `op://` scheme on top of `@graphorin/security`'s
|
|
6
|
+
> pluggable `SecretResolver` registry by shelling out to the official
|
|
7
|
+
> 1Password CLI (`op read 'op://<vault>/<item>/<field>'`).
|
|
8
|
+
>
|
|
9
|
+
> Project Graphorin · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko ·
|
|
10
|
+
> <https://github.com/o-stepper/graphorin>
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Status
|
|
15
|
+
|
|
16
|
+
- **Published:** v0.5.0 (optional sub-pack)
|
|
17
|
+
- Reference adapter — community packages should follow this template
|
|
18
|
+
when wiring HashiCorp Vault, AWS Secrets Manager, GCP Secret
|
|
19
|
+
Manager, Azure Key Vault, Bitwarden, or Unix `pass`.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pnpm add @graphorin/secret-1password
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The package shells out to the **system** `op` binary; install it from
|
|
30
|
+
[1Password's official CLI distribution](https://developer.1password.com/docs/cli/get-started/).
|
|
31
|
+
The package does NOT bundle the CLI — the operator chooses how it is
|
|
32
|
+
provisioned (homebrew, scoop, apt, official tarball, k8s init
|
|
33
|
+
container, etc.).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
### Local interactive mode
|
|
40
|
+
|
|
41
|
+
Sign in to 1Password once via `eval $(op signin)`; the resolver picks
|
|
42
|
+
up the cached session token from your shell.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { registerResolver, resolveSecret } from '@graphorin/security';
|
|
46
|
+
import { onePasswordResolver } from '@graphorin/secret-1password';
|
|
47
|
+
|
|
48
|
+
registerResolver(onePasswordResolver);
|
|
49
|
+
|
|
50
|
+
const apiKey = await resolveSecret('op://Personal/OpenAI/api-key');
|
|
51
|
+
await apiKey.use((raw) => callOpenAI(raw));
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Headless / CI mode
|
|
55
|
+
|
|
56
|
+
Use a [1Password Service Account](https://developer.1password.com/docs/service-accounts/)
|
|
57
|
+
token. The resolver forwards it via `OP_SERVICE_ACCOUNT_TOKEN`:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { createOnePasswordResolver } from '@graphorin/secret-1password';
|
|
61
|
+
|
|
62
|
+
registerResolver(
|
|
63
|
+
createOnePasswordResolver({
|
|
64
|
+
serviceAccountToken: process.env.OP_SERVICE_ACCOUNT_TOKEN!,
|
|
65
|
+
timeoutMs: 10_000,
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 1Password Connect
|
|
71
|
+
|
|
72
|
+
For self-hosted Connect deployments:
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
registerResolver(
|
|
76
|
+
createOnePasswordResolver({
|
|
77
|
+
connect: {
|
|
78
|
+
host: process.env.OP_CONNECT_HOST!,
|
|
79
|
+
token: process.env.OP_CONNECT_TOKEN!,
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Multiple accounts
|
|
86
|
+
|
|
87
|
+
Pass `--account` through:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
registerResolver(
|
|
91
|
+
createOnePasswordResolver({
|
|
92
|
+
account: 'team-graphorin.1password.com',
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
## URI format
|
|
100
|
+
|
|
101
|
+
`op://<vault>/<item>/[section/]<field>`
|
|
102
|
+
|
|
103
|
+
Per 1Password, vault / item / field names are case-insensitive. The
|
|
104
|
+
resolver lowercases the URI before forwarding to the CLI; pass
|
|
105
|
+
`preserveCase: true` to opt out.
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
|
|
109
|
+
```text
|
|
110
|
+
op://Personal/OpenAI/api-key
|
|
111
|
+
op://Production/Stripe/credentials/live-secret-key
|
|
112
|
+
op://Engineering/Postgres/section/connection-string
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
117
|
+
## Error handling
|
|
118
|
+
|
|
119
|
+
Every error surfaces as a typed `OpCliError` with a `kind` field and
|
|
120
|
+
an actionable hint:
|
|
121
|
+
|
|
122
|
+
| `kind` | When it fires | Hint |
|
|
123
|
+
|-------------------------|------------------------------------------------------------|-------------------------------------------------------------------------------------------------|
|
|
124
|
+
| `binary-missing` | `op` not on PATH. | Install the 1Password CLI. |
|
|
125
|
+
| `signed-out` | `op` reports "not signed in" or session expired. | Run `eval $(op signin)` (interactive) or set `OP_SERVICE_ACCOUNT_TOKEN` (headless). |
|
|
126
|
+
| `reference-not-found` | The vault / item / field does not exist. | Verify with `op item get <item> --vault <vault>`. |
|
|
127
|
+
| `timeout` | The CLI did not return within the timeout. | Increase `timeoutMs` or check 1Password connectivity. |
|
|
128
|
+
| `unknown` | Any other non-zero exit code. | Inspect the captured stderr. |
|
|
129
|
+
|
|
130
|
+
The resolver wraps `OpCliError` in `SecretResolutionError` (the
|
|
131
|
+
canonical `@graphorin/security` error) so existing `catch` paths in
|
|
132
|
+
agent code keep working.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Template for community resolvers
|
|
137
|
+
|
|
138
|
+
The package is intentionally tiny (~170 LOC). The structure is the
|
|
139
|
+
canonical template community packages should follow when wiring
|
|
140
|
+
HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key
|
|
141
|
+
Vault, Bitwarden, or Unix `pass`:
|
|
142
|
+
|
|
143
|
+
1. Build a `<Tool>Cli` interface that captures the upstream tool's
|
|
144
|
+
surface (read / list / health-check); ship a default
|
|
145
|
+
implementation that spawns the binary or calls the SDK.
|
|
146
|
+
2. Build a `create<Tool>Resolver(options)` factory that returns a
|
|
147
|
+
`SecretResolver` honouring the upstream URI scheme.
|
|
148
|
+
3. Validate the URI shape locally; the upstream tool is the source of
|
|
149
|
+
truth for the actual lookup.
|
|
150
|
+
4. Wrap upstream errors in `SecretResolutionError` so existing `catch`
|
|
151
|
+
paths keep working.
|
|
152
|
+
5. Test with a stub `<Tool>Cli` — never reach the network or the
|
|
153
|
+
real binary in CI.
|
|
154
|
+
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
## Related decisions
|
|
158
|
+
|
|
159
|
+
- ADR-026 — `SecretValue` and `SecretsStore` end-to-end contract.
|
|
160
|
+
- ADR-028 — `SecretRef` URI scheme (`env:` / `keyring:` / `file:` / `encrypted-file:` / `op://` / `vault://` / `ref:`).
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## License
|
|
165
|
+
|
|
166
|
+
MIT © 2026 Oleksiy Stepurenko
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
**Project Graphorin** · v0.5.0 · MIT License · © 2026 Oleksiy Stepurenko · <https://github.com/o-stepper/graphorin>
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { OpCli, OpCliError, OpCliErrorKind, OpCliReadOptions, OpCliReadResult, createDefaultOpCli, createOpCli } from "./op-cli.js";
|
|
2
|
+
import { OnePasswordResolverOptions, createOnePasswordResolver, normalizeOpUri, onePasswordResolver } from "./resolver.js";
|
|
3
|
+
|
|
4
|
+
//#region src/index.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @graphorin/secret-1password — reference 1Password secret-resolver
|
|
8
|
+
* adapter for the Graphorin framework.
|
|
9
|
+
*
|
|
10
|
+
* Registers the `op://` scheme on top of `@graphorin/security`'s
|
|
11
|
+
* pluggable `SecretResolver` registry by shelling out to the official
|
|
12
|
+
* 1Password CLI:
|
|
13
|
+
*
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { registerResolver } from '@graphorin/security';
|
|
16
|
+
* import { onePasswordResolver } from '@graphorin/secret-1password';
|
|
17
|
+
*
|
|
18
|
+
* registerResolver(onePasswordResolver);
|
|
19
|
+
*
|
|
20
|
+
* // Anywhere downstream:
|
|
21
|
+
* const apiKey = await resolveSecret('op://Personal/OpenAI/api-key');
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The adapter is also the canonical template community packages
|
|
25
|
+
* should follow when wiring HashiCorp Vault, AWS Secrets Manager, GCP
|
|
26
|
+
* Secret Manager, Azure Key Vault, Bitwarden, or Unix `pass` as
|
|
27
|
+
* additional `SecretResolver` implementations.
|
|
28
|
+
*
|
|
29
|
+
* @packageDocumentation
|
|
30
|
+
*/
|
|
31
|
+
/** Canonical version constant. Mirrors the `package.json` version. */
|
|
32
|
+
declare const VERSION = "0.5.0";
|
|
33
|
+
//#endregion
|
|
34
|
+
export { type OnePasswordResolverOptions, type OpCli, OpCliError, type OpCliErrorKind, type OpCliReadOptions, type OpCliReadResult, VERSION, createDefaultOpCli, createOnePasswordResolver, createOpCli, normalizeOpUri, onePasswordResolver };
|
|
35
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AA2BA;;;;;;;;;;;;;;;;;;;;;;cAAa,OAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { OpCliError, createDefaultOpCli, createOpCli } from "./op-cli.js";
|
|
2
|
+
import { createOnePasswordResolver, normalizeOpUri, onePasswordResolver } from "./resolver.js";
|
|
3
|
+
|
|
4
|
+
//#region src/index.ts
|
|
5
|
+
/**
|
|
6
|
+
* @graphorin/secret-1password — reference 1Password secret-resolver
|
|
7
|
+
* adapter for the Graphorin framework.
|
|
8
|
+
*
|
|
9
|
+
* Registers the `op://` scheme on top of `@graphorin/security`'s
|
|
10
|
+
* pluggable `SecretResolver` registry by shelling out to the official
|
|
11
|
+
* 1Password CLI:
|
|
12
|
+
*
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { registerResolver } from '@graphorin/security';
|
|
15
|
+
* import { onePasswordResolver } from '@graphorin/secret-1password';
|
|
16
|
+
*
|
|
17
|
+
* registerResolver(onePasswordResolver);
|
|
18
|
+
*
|
|
19
|
+
* // Anywhere downstream:
|
|
20
|
+
* const apiKey = await resolveSecret('op://Personal/OpenAI/api-key');
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* The adapter is also the canonical template community packages
|
|
24
|
+
* should follow when wiring HashiCorp Vault, AWS Secrets Manager, GCP
|
|
25
|
+
* Secret Manager, Azure Key Vault, Bitwarden, or Unix `pass` as
|
|
26
|
+
* additional `SecretResolver` implementations.
|
|
27
|
+
*
|
|
28
|
+
* @packageDocumentation
|
|
29
|
+
*/
|
|
30
|
+
/** Canonical version constant. Mirrors the `package.json` version. */
|
|
31
|
+
const VERSION = "0.5.0";
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
export { OpCliError, VERSION, createDefaultOpCli, createOnePasswordResolver, createOpCli, normalizeOpUri, onePasswordResolver };
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * @graphorin/secret-1password — reference 1Password secret-resolver\n * adapter for the Graphorin framework.\n *\n * Registers the `op://` scheme on top of `@graphorin/security`'s\n * pluggable `SecretResolver` registry by shelling out to the official\n * 1Password CLI:\n *\n * ```ts\n * import { registerResolver } from '@graphorin/security';\n * import { onePasswordResolver } from '@graphorin/secret-1password';\n *\n * registerResolver(onePasswordResolver);\n *\n * // Anywhere downstream:\n * const apiKey = await resolveSecret('op://Personal/OpenAI/api-key');\n * ```\n *\n * The adapter is also the canonical template community packages\n * should follow when wiring HashiCorp Vault, AWS Secrets Manager, GCP\n * Secret Manager, Azure Key Vault, Bitwarden, or Unix `pass` as\n * additional `SecretResolver` implementations.\n *\n * @packageDocumentation\n */\n\n/** Canonical version constant. Mirrors the `package.json` version. */\nexport const VERSION = '0.5.0';\n\nexport {\n createDefaultOpCli,\n createOpCli,\n type OpCli,\n OpCliError,\n type OpCliErrorKind,\n type OpCliReadOptions,\n type OpCliReadResult,\n} from './op-cli.js';\nexport {\n createOnePasswordResolver,\n normalizeOpUri,\n type OnePasswordResolverOptions,\n onePasswordResolver,\n} from './resolver.js';\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,MAAa,UAAU"}
|
package/dist/op-cli.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
//#region src/op-cli.d.ts
|
|
4
|
+
|
|
5
|
+
/** @stable */
|
|
6
|
+
interface OpCliReadResult {
|
|
7
|
+
readonly value: string;
|
|
8
|
+
readonly exitCode: number;
|
|
9
|
+
readonly durationMs: number;
|
|
10
|
+
}
|
|
11
|
+
/** @stable */
|
|
12
|
+
interface OpCli {
|
|
13
|
+
/**
|
|
14
|
+
* Resolve a single `op://...` reference. Returns the **trimmed**
|
|
15
|
+
* stdout. Throws {@link OpCliError} when the binary is missing, the
|
|
16
|
+
* user is signed out, the reference does not resolve, or the call
|
|
17
|
+
* exceeds the timeout.
|
|
18
|
+
*/
|
|
19
|
+
read(uri: string, options?: OpCliReadOptions): Promise<OpCliReadResult>;
|
|
20
|
+
}
|
|
21
|
+
/** @stable */
|
|
22
|
+
interface OpCliReadOptions {
|
|
23
|
+
/** Override the binary path. Default `'op'` (looked up on `$PATH`). */
|
|
24
|
+
readonly binary?: string;
|
|
25
|
+
/** Hard timeout in milliseconds. Default `15000`. */
|
|
26
|
+
readonly timeoutMs?: number;
|
|
27
|
+
/** Override `process.env`. Default forwards the parent process. */
|
|
28
|
+
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
29
|
+
/** Optional 1Password Connect / Service-Account token forwarding. */
|
|
30
|
+
readonly serviceAccountToken?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Optional 1Password Connect host + token tuple. When set the
|
|
33
|
+
* resolver wires them through the `OP_CONNECT_HOST` /
|
|
34
|
+
* `OP_CONNECT_TOKEN` env vars (the canonical Connect-mode contract
|
|
35
|
+
* documented by 1Password).
|
|
36
|
+
*/
|
|
37
|
+
readonly connect?: {
|
|
38
|
+
readonly host: string;
|
|
39
|
+
readonly token: string;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Optional `--account` override forwarded to the CLI. Useful when
|
|
43
|
+
* the operator is signed in to multiple 1Password accounts.
|
|
44
|
+
*/
|
|
45
|
+
readonly account?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Optional `--no-color` flag suppression. The resolver always sets
|
|
48
|
+
* `--no-color` so terminal colour codes do not leak into the
|
|
49
|
+
* resolved value; pass `true` to opt out.
|
|
50
|
+
*/
|
|
51
|
+
readonly preserveColor?: boolean;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Typed error raised by the CLI wrapper. Carries a `kind` so callers
|
|
55
|
+
* can distinguish operator-fixable failure modes.
|
|
56
|
+
*
|
|
57
|
+
* @stable
|
|
58
|
+
*/
|
|
59
|
+
declare class OpCliError extends Error {
|
|
60
|
+
readonly name = "OpCliError";
|
|
61
|
+
readonly kind: OpCliErrorKind;
|
|
62
|
+
readonly exitCode?: number;
|
|
63
|
+
readonly stderr?: string;
|
|
64
|
+
readonly hint?: string;
|
|
65
|
+
constructor(kind: OpCliErrorKind, message: string, options?: {
|
|
66
|
+
cause?: unknown;
|
|
67
|
+
exitCode?: number;
|
|
68
|
+
stderr?: string;
|
|
69
|
+
hint?: string;
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** @stable */
|
|
73
|
+
type OpCliErrorKind = 'binary-missing' | 'signed-out' | 'reference-not-found' | 'timeout' | 'unknown';
|
|
74
|
+
/**
|
|
75
|
+
* Default {@link OpCli} implementation. Spawns `op read --no-color
|
|
76
|
+
* --reveal '<uri>'` with the configured timeout and inherits the
|
|
77
|
+
* parent environment.
|
|
78
|
+
*
|
|
79
|
+
* @stable
|
|
80
|
+
*/
|
|
81
|
+
declare function createDefaultOpCli(): OpCli;
|
|
82
|
+
/**
|
|
83
|
+
* {@link OpCli} factory with an injectable `spawn` (for tests). Production
|
|
84
|
+
* code uses {@link createDefaultOpCli}.
|
|
85
|
+
*
|
|
86
|
+
* @stable
|
|
87
|
+
*/
|
|
88
|
+
declare function createOpCli(deps?: {
|
|
89
|
+
readonly spawn?: typeof spawn;
|
|
90
|
+
}): OpCli;
|
|
91
|
+
//#endregion
|
|
92
|
+
export { OpCli, OpCliError, OpCliErrorKind, OpCliReadOptions, OpCliReadResult, createDefaultOpCli, createOpCli };
|
|
93
|
+
//# sourceMappingURL=op-cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"op-cli.d.ts","names":[],"sources":["../src/op-cli.ts"],"sourcesContent":[],"mappings":";;;;;AAoEgC,UArDf,eAAA,CAqDe;EAAK,SAAA,KAAA,EAAA,MAAA;EA0BzB,SAAA,QAAc,EAAA,MAAA;EAcV,SAAA,UAAA,EAAkB,MAAA;AAkBlC;;UAxGiB,KAAA;;;;;;;8BAOa,mBAAmB,QAAQ;;;UAIxC,gBAAA;;;;;;iBAMA,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA6Bb,UAAA,SAAmB,KAAA;;iBAEf;;;;oBAMP;;;;;;;;KAkBE,cAAA;;;;;;;;iBAcI,kBAAA,CAAA,GAAsB;;;;;;;iBAkBtB,WAAA;0BAA4C;IAAe"}
|
package/dist/op-cli.js
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
//#region src/op-cli.ts
|
|
4
|
+
/**
|
|
5
|
+
* Thin wrapper around the 1Password CLI (`op`). Spawns `op` with the
|
|
6
|
+
* canonical `read` subcommand, captures stdout, and surfaces typed
|
|
7
|
+
* errors for the most common operator-fixable failure modes.
|
|
8
|
+
*
|
|
9
|
+
* The wrapper is split out so the resolver can be unit-tested without
|
|
10
|
+
* touching the real `op` binary — tests inject a stub `OpCli`
|
|
11
|
+
* implementation through {@link createOnePasswordResolver}.
|
|
12
|
+
*
|
|
13
|
+
* @packageDocumentation
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Typed error raised by the CLI wrapper. Carries a `kind` so callers
|
|
17
|
+
* can distinguish operator-fixable failure modes.
|
|
18
|
+
*
|
|
19
|
+
* @stable
|
|
20
|
+
*/
|
|
21
|
+
var OpCliError = class extends Error {
|
|
22
|
+
name = "OpCliError";
|
|
23
|
+
kind;
|
|
24
|
+
exitCode;
|
|
25
|
+
stderr;
|
|
26
|
+
hint;
|
|
27
|
+
constructor(kind, message, options) {
|
|
28
|
+
super(message, options?.cause !== void 0 ? { cause: options.cause } : void 0);
|
|
29
|
+
this.kind = kind;
|
|
30
|
+
if (options?.exitCode !== void 0) this.exitCode = options.exitCode;
|
|
31
|
+
if (options?.stderr !== void 0) this.stderr = options.stderr;
|
|
32
|
+
if (options?.hint !== void 0) this.hint = options.hint;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Default {@link OpCli} implementation. Spawns `op read --no-color
|
|
37
|
+
* --reveal '<uri>'` with the configured timeout and inherits the
|
|
38
|
+
* parent environment.
|
|
39
|
+
*
|
|
40
|
+
* @stable
|
|
41
|
+
*/
|
|
42
|
+
function createDefaultOpCli() {
|
|
43
|
+
return createOpCli();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Grace period after SIGTERM before escalating to SIGKILL (SPL-22). A
|
|
47
|
+
* well-behaved `op` exits on SIGTERM well within this window; a wedged one
|
|
48
|
+
* (ignoring SIGTERM in an uninterruptible read) is force-killed so the hard
|
|
49
|
+
* timeout actually settles the promise.
|
|
50
|
+
*/
|
|
51
|
+
const SIGKILL_GRACE_MS = 1e3;
|
|
52
|
+
/**
|
|
53
|
+
* {@link OpCli} factory with an injectable `spawn` (for tests). Production
|
|
54
|
+
* code uses {@link createDefaultOpCli}.
|
|
55
|
+
*
|
|
56
|
+
* @stable
|
|
57
|
+
*/
|
|
58
|
+
function createOpCli(deps = {}) {
|
|
59
|
+
const spawnFn = deps.spawn ?? spawn;
|
|
60
|
+
return { async read(uri, options = {}) {
|
|
61
|
+
const binary = options.binary ?? "op";
|
|
62
|
+
const timeoutMs = options.timeoutMs ?? 15e3;
|
|
63
|
+
const args = ["read", "--reveal"];
|
|
64
|
+
if (options.preserveColor !== true) args.push("--no-color");
|
|
65
|
+
if (options.account !== void 0 && options.account.length > 0) args.push("--account", options.account);
|
|
66
|
+
args.push(uri);
|
|
67
|
+
const env = {};
|
|
68
|
+
const baseEnv = options.env ?? process.env;
|
|
69
|
+
for (const [k, v] of Object.entries(baseEnv)) if (typeof v === "string") env[k] = v;
|
|
70
|
+
if (options.serviceAccountToken !== void 0) env.OP_SERVICE_ACCOUNT_TOKEN = options.serviceAccountToken;
|
|
71
|
+
if (options.connect !== void 0) {
|
|
72
|
+
env.OP_CONNECT_HOST = options.connect.host;
|
|
73
|
+
env.OP_CONNECT_TOKEN = options.connect.token;
|
|
74
|
+
}
|
|
75
|
+
const started = performance.now();
|
|
76
|
+
return new Promise((resolve, reject) => {
|
|
77
|
+
let stdout = "";
|
|
78
|
+
let stderr = "";
|
|
79
|
+
let killedByTimeout = false;
|
|
80
|
+
let proc;
|
|
81
|
+
try {
|
|
82
|
+
proc = spawnFn(binary, args, {
|
|
83
|
+
env,
|
|
84
|
+
stdio: [
|
|
85
|
+
"ignore",
|
|
86
|
+
"pipe",
|
|
87
|
+
"pipe"
|
|
88
|
+
]
|
|
89
|
+
});
|
|
90
|
+
} catch (err) {
|
|
91
|
+
reject(new OpCliError("binary-missing", `'${binary}' is not installed or not on PATH. Install the 1Password CLI: https://developer.1password.com/docs/cli/get-started`, {
|
|
92
|
+
cause: err,
|
|
93
|
+
hint: "install the 1Password CLI"
|
|
94
|
+
}));
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
let graceTimer;
|
|
98
|
+
const timer = setTimeout(() => {
|
|
99
|
+
killedByTimeout = true;
|
|
100
|
+
try {
|
|
101
|
+
proc.kill("SIGTERM");
|
|
102
|
+
} catch {}
|
|
103
|
+
graceTimer = setTimeout(() => {
|
|
104
|
+
try {
|
|
105
|
+
proc.kill("SIGKILL");
|
|
106
|
+
} catch {}
|
|
107
|
+
reject(new OpCliError("timeout", `op CLI timed out after ${timeoutMs} ms while resolving '${uri}' and did not exit on SIGTERM.`, {
|
|
108
|
+
stderr,
|
|
109
|
+
hint: "increase --op-timeout-ms or check 1Password connectivity"
|
|
110
|
+
}));
|
|
111
|
+
}, SIGKILL_GRACE_MS);
|
|
112
|
+
graceTimer.unref?.();
|
|
113
|
+
}, timeoutMs);
|
|
114
|
+
timer.unref?.();
|
|
115
|
+
proc.stdout?.on("data", (chunk) => {
|
|
116
|
+
stdout += chunk.toString("utf8");
|
|
117
|
+
});
|
|
118
|
+
proc.stderr?.on("data", (chunk) => {
|
|
119
|
+
stderr += chunk.toString("utf8");
|
|
120
|
+
});
|
|
121
|
+
proc.on("error", (err) => {
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
if (graceTimer !== void 0) clearTimeout(graceTimer);
|
|
124
|
+
if (err.code === "ENOENT") {
|
|
125
|
+
reject(new OpCliError("binary-missing", `'${binary}' is not installed or not on PATH. Install the 1Password CLI: https://developer.1password.com/docs/cli/get-started`, {
|
|
126
|
+
cause: err,
|
|
127
|
+
hint: "install the 1Password CLI"
|
|
128
|
+
}));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
reject(new OpCliError("unknown", `op CLI invocation failed: ${err.message}`, { cause: err }));
|
|
132
|
+
});
|
|
133
|
+
proc.on("close", (code) => {
|
|
134
|
+
clearTimeout(timer);
|
|
135
|
+
if (graceTimer !== void 0) clearTimeout(graceTimer);
|
|
136
|
+
const durationMs = performance.now() - started;
|
|
137
|
+
if (killedByTimeout) {
|
|
138
|
+
reject(new OpCliError("timeout", `op CLI timed out after ${timeoutMs} ms while resolving '${uri}'.`, {
|
|
139
|
+
stderr,
|
|
140
|
+
hint: "increase --op-timeout-ms or check 1Password connectivity"
|
|
141
|
+
}));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
if (code !== 0) {
|
|
145
|
+
const kind = classifyExitError(code ?? -1, stderr);
|
|
146
|
+
reject(new OpCliError(kind, `op CLI exited with code ${code} for '${uri}'${stderr.length > 0 ? `: ${stderr.trim()}` : ""}`, {
|
|
147
|
+
...code !== null ? { exitCode: code } : {},
|
|
148
|
+
stderr,
|
|
149
|
+
hint: hintForExitKind(kind)
|
|
150
|
+
}));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
resolve(Object.freeze({
|
|
154
|
+
value: stdout.replace(/\r?\n$/, ""),
|
|
155
|
+
exitCode: 0,
|
|
156
|
+
durationMs
|
|
157
|
+
}));
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
} };
|
|
161
|
+
}
|
|
162
|
+
function classifyExitError(_code, stderr) {
|
|
163
|
+
const lower = stderr.toLowerCase();
|
|
164
|
+
if (lower.includes("not signed in") || lower.includes("please sign in") || lower.includes("not authenticated") || lower.includes("session expired")) return "signed-out";
|
|
165
|
+
if (lower.includes("couldn't find") || lower.includes("does not exist") || lower.includes("item not found") || lower.includes("field not found") || lower.includes("reference not found")) return "reference-not-found";
|
|
166
|
+
return "unknown";
|
|
167
|
+
}
|
|
168
|
+
function hintForExitKind(kind) {
|
|
169
|
+
switch (kind) {
|
|
170
|
+
case "signed-out": return "run 'eval $(op signin)' (interactive) or set OP_SERVICE_ACCOUNT_TOKEN (headless).";
|
|
171
|
+
case "reference-not-found": return "verify the reference with 'op item get <item> --vault <vault>' before retrying.";
|
|
172
|
+
case "timeout": return "check 1Password connectivity / increase --op-timeout-ms.";
|
|
173
|
+
case "binary-missing": return "install the 1Password CLI: https://developer.1password.com/docs/cli/get-started";
|
|
174
|
+
default: return "check the op CLI stderr output for details.";
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
//#endregion
|
|
179
|
+
export { OpCliError, createDefaultOpCli, createOpCli };
|
|
180
|
+
//# sourceMappingURL=op-cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"op-cli.js","names":["env: Record<string, string>","proc: ReturnType<typeof spawn>","graceTimer: ReturnType<typeof setTimeout> | undefined"],"sources":["../src/op-cli.ts"],"sourcesContent":["/**\n * Thin wrapper around the 1Password CLI (`op`). Spawns `op` with the\n * canonical `read` subcommand, captures stdout, and surfaces typed\n * errors for the most common operator-fixable failure modes.\n *\n * The wrapper is split out so the resolver can be unit-tested without\n * touching the real `op` binary — tests inject a stub `OpCli`\n * implementation through {@link createOnePasswordResolver}.\n *\n * @packageDocumentation\n */\n\nimport { spawn } from 'node:child_process';\n\n/** @stable */\nexport interface OpCliReadResult {\n readonly value: string;\n readonly exitCode: number;\n readonly durationMs: number;\n}\n\n/** @stable */\nexport interface OpCli {\n /**\n * Resolve a single `op://...` reference. Returns the **trimmed**\n * stdout. Throws {@link OpCliError} when the binary is missing, the\n * user is signed out, the reference does not resolve, or the call\n * exceeds the timeout.\n */\n read(uri: string, options?: OpCliReadOptions): Promise<OpCliReadResult>;\n}\n\n/** @stable */\nexport interface OpCliReadOptions {\n /** Override the binary path. Default `'op'` (looked up on `$PATH`). */\n readonly binary?: string;\n /** Hard timeout in milliseconds. Default `15000`. */\n readonly timeoutMs?: number;\n /** Override `process.env`. Default forwards the parent process. */\n readonly env?: Readonly<Record<string, string | undefined>>;\n /** Optional 1Password Connect / Service-Account token forwarding. */\n readonly serviceAccountToken?: string;\n /**\n * Optional 1Password Connect host + token tuple. When set the\n * resolver wires them through the `OP_CONNECT_HOST` /\n * `OP_CONNECT_TOKEN` env vars (the canonical Connect-mode contract\n * documented by 1Password).\n */\n readonly connect?: { readonly host: string; readonly token: string };\n /**\n * Optional `--account` override forwarded to the CLI. Useful when\n * the operator is signed in to multiple 1Password accounts.\n */\n readonly account?: string;\n /**\n * Optional `--no-color` flag suppression. The resolver always sets\n * `--no-color` so terminal colour codes do not leak into the\n * resolved value; pass `true` to opt out.\n */\n readonly preserveColor?: boolean;\n}\n\n/**\n * Typed error raised by the CLI wrapper. Carries a `kind` so callers\n * can distinguish operator-fixable failure modes.\n *\n * @stable\n */\nexport class OpCliError extends Error {\n override readonly name = 'OpCliError';\n readonly kind: OpCliErrorKind;\n readonly exitCode?: number;\n readonly stderr?: string;\n readonly hint?: string;\n\n constructor(\n kind: OpCliErrorKind,\n message: string,\n options?: {\n cause?: unknown;\n exitCode?: number;\n stderr?: string;\n hint?: string;\n },\n ) {\n super(message, options?.cause !== undefined ? { cause: options.cause } : undefined);\n this.kind = kind;\n if (options?.exitCode !== undefined) this.exitCode = options.exitCode;\n if (options?.stderr !== undefined) this.stderr = options.stderr;\n if (options?.hint !== undefined) this.hint = options.hint;\n }\n}\n\n/** @stable */\nexport type OpCliErrorKind =\n | 'binary-missing'\n | 'signed-out'\n | 'reference-not-found'\n | 'timeout'\n | 'unknown';\n\n/**\n * Default {@link OpCli} implementation. Spawns `op read --no-color\n * --reveal '<uri>'` with the configured timeout and inherits the\n * parent environment.\n *\n * @stable\n */\nexport function createDefaultOpCli(): OpCli {\n return createOpCli();\n}\n\n/**\n * Grace period after SIGTERM before escalating to SIGKILL (SPL-22). A\n * well-behaved `op` exits on SIGTERM well within this window; a wedged one\n * (ignoring SIGTERM in an uninterruptible read) is force-killed so the hard\n * timeout actually settles the promise.\n */\nconst SIGKILL_GRACE_MS = 1_000;\n\n/**\n * {@link OpCli} factory with an injectable `spawn` (for tests). Production\n * code uses {@link createDefaultOpCli}.\n *\n * @stable\n */\nexport function createOpCli(deps: { readonly spawn?: typeof spawn } = {}): OpCli {\n const spawnFn = deps.spawn ?? spawn;\n return {\n async read(uri: string, options: OpCliReadOptions = {}): Promise<OpCliReadResult> {\n const binary = options.binary ?? 'op';\n const timeoutMs = options.timeoutMs ?? 15_000;\n const args = ['read', '--reveal'];\n if (options.preserveColor !== true) args.push('--no-color');\n if (options.account !== undefined && options.account.length > 0) {\n args.push('--account', options.account);\n }\n args.push(uri);\n const env: Record<string, string> = {};\n const baseEnv = options.env ?? process.env;\n for (const [k, v] of Object.entries(baseEnv)) {\n if (typeof v === 'string') env[k] = v;\n }\n if (options.serviceAccountToken !== undefined) {\n env.OP_SERVICE_ACCOUNT_TOKEN = options.serviceAccountToken;\n }\n if (options.connect !== undefined) {\n env.OP_CONNECT_HOST = options.connect.host;\n env.OP_CONNECT_TOKEN = options.connect.token;\n }\n const started = performance.now();\n return new Promise<OpCliReadResult>((resolve, reject) => {\n let stdout = '';\n let stderr = '';\n let killedByTimeout = false;\n let proc: ReturnType<typeof spawn>;\n try {\n proc = spawnFn(binary, args, { env, stdio: ['ignore', 'pipe', 'pipe'] });\n } catch (err) {\n reject(\n new OpCliError(\n 'binary-missing',\n `'${binary}' is not installed or not on PATH. Install the 1Password CLI: https://developer.1password.com/docs/cli/get-started`,\n { cause: err, hint: 'install the 1Password CLI' },\n ),\n );\n return;\n }\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n const timer = setTimeout(() => {\n killedByTimeout = true;\n try {\n proc.kill('SIGTERM');\n } catch {\n // best-effort\n }\n // SPL-22: SIGTERM alone can hang forever if `op` ignores it. Escalate\n // to SIGKILL after a short grace AND reject from the timer, so the\n // promise settles even when `close` never fires.\n graceTimer = setTimeout(() => {\n try {\n proc.kill('SIGKILL');\n } catch {\n // best-effort\n }\n reject(\n new OpCliError(\n 'timeout',\n `op CLI timed out after ${timeoutMs} ms while resolving '${uri}' and did not exit on SIGTERM.`,\n { stderr, hint: 'increase --op-timeout-ms or check 1Password connectivity' },\n ),\n );\n }, SIGKILL_GRACE_MS);\n graceTimer.unref?.();\n }, timeoutMs);\n timer.unref?.();\n proc.stdout?.on('data', (chunk: Buffer) => {\n stdout += chunk.toString('utf8');\n });\n proc.stderr?.on('data', (chunk: Buffer) => {\n stderr += chunk.toString('utf8');\n });\n proc.on('error', (err) => {\n clearTimeout(timer);\n if (graceTimer !== undefined) clearTimeout(graceTimer);\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n reject(\n new OpCliError(\n 'binary-missing',\n `'${binary}' is not installed or not on PATH. Install the 1Password CLI: https://developer.1password.com/docs/cli/get-started`,\n { cause: err, hint: 'install the 1Password CLI' },\n ),\n );\n return;\n }\n reject(\n new OpCliError('unknown', `op CLI invocation failed: ${err.message}`, {\n cause: err,\n }),\n );\n });\n proc.on('close', (code) => {\n clearTimeout(timer);\n if (graceTimer !== undefined) clearTimeout(graceTimer);\n const durationMs = performance.now() - started;\n if (killedByTimeout) {\n reject(\n new OpCliError(\n 'timeout',\n `op CLI timed out after ${timeoutMs} ms while resolving '${uri}'.`,\n { stderr, hint: 'increase --op-timeout-ms or check 1Password connectivity' },\n ),\n );\n return;\n }\n if (code !== 0) {\n const kind = classifyExitError(code ?? -1, stderr);\n reject(\n new OpCliError(\n kind,\n `op CLI exited with code ${code} for '${uri}'${stderr.length > 0 ? `: ${stderr.trim()}` : ''}`,\n {\n ...(code !== null ? { exitCode: code } : {}),\n stderr,\n hint: hintForExitKind(kind),\n },\n ),\n );\n return;\n }\n resolve(Object.freeze({ value: stdout.replace(/\\r?\\n$/, ''), exitCode: 0, durationMs }));\n });\n });\n },\n };\n}\n\nfunction classifyExitError(_code: number, stderr: string): OpCliErrorKind {\n const lower = stderr.toLowerCase();\n if (\n lower.includes('not signed in') ||\n lower.includes('please sign in') ||\n lower.includes('not authenticated') ||\n lower.includes('session expired')\n ) {\n return 'signed-out';\n }\n if (\n lower.includes(\"couldn't find\") ||\n lower.includes('does not exist') ||\n lower.includes('item not found') ||\n lower.includes('field not found') ||\n lower.includes('reference not found')\n ) {\n return 'reference-not-found';\n }\n return 'unknown';\n}\n\nfunction hintForExitKind(kind: OpCliErrorKind): string {\n switch (kind) {\n case 'signed-out':\n return \"run 'eval $(op signin)' (interactive) or set OP_SERVICE_ACCOUNT_TOKEN (headless).\";\n case 'reference-not-found':\n return \"verify the reference with 'op item get <item> --vault <vault>' before retrying.\";\n case 'timeout':\n return 'check 1Password connectivity / increase --op-timeout-ms.';\n case 'binary-missing':\n return 'install the 1Password CLI: https://developer.1password.com/docs/cli/get-started';\n default:\n return 'check the op CLI stderr output for details.';\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { OpCli } from "./op-cli.js";
|
|
2
|
+
import { SecretResolver } from "@graphorin/core/contracts";
|
|
3
|
+
|
|
4
|
+
//#region src/resolver.d.ts
|
|
5
|
+
|
|
6
|
+
/** @stable */
|
|
7
|
+
interface OnePasswordResolverOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Inject a {@link OpCli} implementation. Defaults to a wrapper that
|
|
10
|
+
* spawns the system `op` binary. Tests pass a stub.
|
|
11
|
+
*/
|
|
12
|
+
readonly cli?: OpCli;
|
|
13
|
+
/** Override the CLI binary path. Default `'op'`. */
|
|
14
|
+
readonly binary?: string;
|
|
15
|
+
/** Hard timeout per resolve. Default `15000` ms. */
|
|
16
|
+
readonly timeoutMs?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Optional service-account token. When set the resolver forwards it
|
|
19
|
+
* via `OP_SERVICE_ACCOUNT_TOKEN` so the CLI runs in headless mode.
|
|
20
|
+
* The token is itself a secret — pass a previously-resolved
|
|
21
|
+
* `SecretValue` and use `.use(...)` to scope its lifetime.
|
|
22
|
+
*/
|
|
23
|
+
readonly serviceAccountToken?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Optional 1Password Connect host + token. Mutually exclusive with
|
|
26
|
+
* a service-account token at the CLI level (the CLI honours the
|
|
27
|
+
* Connect env vars if both are present).
|
|
28
|
+
*/
|
|
29
|
+
readonly connect?: {
|
|
30
|
+
readonly host: string;
|
|
31
|
+
readonly token: string;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Optional `--account` override. Useful when the operator is signed
|
|
35
|
+
* in to multiple 1Password accounts.
|
|
36
|
+
*/
|
|
37
|
+
readonly account?: string;
|
|
38
|
+
/**
|
|
39
|
+
* If `true`, do **not** lowercase the URI before forwarding to the
|
|
40
|
+
* CLI. Default `false`. Toggle only when interoperating with a
|
|
41
|
+
* deployment that intentionally relies on case-sensitive keys.
|
|
42
|
+
*/
|
|
43
|
+
readonly preserveCase?: boolean;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build a `SecretResolver` that honours the `op://` scheme. Register
|
|
47
|
+
* with `registerResolver(...)` from `@graphorin/security` at app
|
|
48
|
+
* bootstrap.
|
|
49
|
+
*
|
|
50
|
+
* @stable
|
|
51
|
+
*/
|
|
52
|
+
declare function createOnePasswordResolver(options?: OnePasswordResolverOptions): SecretResolver;
|
|
53
|
+
/**
|
|
54
|
+
* Cache the default-options resolver so callers that just want the
|
|
55
|
+
* happy-path behaviour can use a constant export.
|
|
56
|
+
*
|
|
57
|
+
* @stable
|
|
58
|
+
*/
|
|
59
|
+
declare const onePasswordResolver: SecretResolver;
|
|
60
|
+
/**
|
|
61
|
+
* Lowercase the authority + path segments of an `op://` URI so two
|
|
62
|
+
* configs that differ only in case resolve to the same value (matching
|
|
63
|
+
* 1Password's case-insensitive behaviour).
|
|
64
|
+
*
|
|
65
|
+
* Exposed for tests.
|
|
66
|
+
*
|
|
67
|
+
* @stable
|
|
68
|
+
*/
|
|
69
|
+
declare function normalizeOpUri(raw: string): string;
|
|
70
|
+
//#endregion
|
|
71
|
+
export { OnePasswordResolverOptions, createOnePasswordResolver, normalizeOpUri, onePasswordResolver };
|
|
72
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","names":[],"sources":["../src/resolver.ts"],"sourcesContent":[],"mappings":";;;;;;UAwBiB,0BAAA;;;;;iBAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAsCD,yBAAA,WACL,6BACR;;;;;;;cAkDU,qBAAqB;;;;;;;;;;iBA4ClB,cAAA"}
|
package/dist/resolver.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { OpCliError, createDefaultOpCli } from "./op-cli.js";
|
|
2
|
+
import { SecretResolutionError, SecretValue } from "@graphorin/security";
|
|
3
|
+
|
|
4
|
+
//#region src/resolver.ts
|
|
5
|
+
/**
|
|
6
|
+
* Build a `SecretResolver` that honours the `op://` scheme. Register
|
|
7
|
+
* with `registerResolver(...)` from `@graphorin/security` at app
|
|
8
|
+
* bootstrap.
|
|
9
|
+
*
|
|
10
|
+
* @stable
|
|
11
|
+
*/
|
|
12
|
+
function createOnePasswordResolver(options = {}) {
|
|
13
|
+
const cli = options.cli ?? createDefaultOpCli();
|
|
14
|
+
return {
|
|
15
|
+
scheme: "op",
|
|
16
|
+
async resolve(ref, ctx) {
|
|
17
|
+
const parsed = ref;
|
|
18
|
+
assertOpRef(parsed);
|
|
19
|
+
const normalized = options.preserveCase === true ? parsed.raw : normalizeOpUri(parsed.raw);
|
|
20
|
+
try {
|
|
21
|
+
const result = await cli.read(normalized, {
|
|
22
|
+
...options.binary !== void 0 ? { binary: options.binary } : {},
|
|
23
|
+
...options.timeoutMs !== void 0 ? { timeoutMs: options.timeoutMs } : {},
|
|
24
|
+
...options.serviceAccountToken !== void 0 ? { serviceAccountToken: options.serviceAccountToken } : {},
|
|
25
|
+
...options.connect !== void 0 ? { connect: options.connect } : {},
|
|
26
|
+
...options.account !== void 0 ? { account: options.account } : {}
|
|
27
|
+
});
|
|
28
|
+
if (result.value.length === 0) throw new SecretResolutionError("op", parsed.raw, "1Password CLI returned an empty value; verify the field exists and is non-empty.");
|
|
29
|
+
return SecretValue.fromString(result.value, { source: {
|
|
30
|
+
resolver: "op",
|
|
31
|
+
ref: parsed.raw
|
|
32
|
+
} });
|
|
33
|
+
} catch (err) {
|
|
34
|
+
if (err instanceof OpCliError) throw new SecretResolutionError("op", parsed.raw, `${err.message}${err.hint !== void 0 ? ` — hint: ${err.hint}` : ""}`, { cause: err });
|
|
35
|
+
throw err;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Cache the default-options resolver so callers that just want the
|
|
42
|
+
* happy-path behaviour can use a constant export.
|
|
43
|
+
*
|
|
44
|
+
* @stable
|
|
45
|
+
*/
|
|
46
|
+
const onePasswordResolver = createOnePasswordResolver();
|
|
47
|
+
function assertOpRef(parsed) {
|
|
48
|
+
if (parsed.scheme !== "op") throw new SecretResolutionError("op", parsed.raw, `expected scheme 'op', received '${parsed.scheme}'.`);
|
|
49
|
+
if (parsed.authority === void 0 || parsed.authority.length === 0) throw new SecretResolutionError("op", parsed.raw, "op:// SecretRef must include a vault authority (e.g. 'op://Personal/Item/field').");
|
|
50
|
+
if (parsed.path === void 0 || parsed.path.length <= 1) throw new SecretResolutionError("op", parsed.raw, "op:// SecretRef must include an item + field path segment (e.g. 'op://Vault/Item/field').");
|
|
51
|
+
const segments = parsed.path.split("/").filter((s) => s.length > 0);
|
|
52
|
+
if (segments.length < 2) throw new SecretResolutionError("op", parsed.raw, `op:// SecretRef must reference at least an item + field (got ${segments.length} path segment(s) after the vault).`);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Lowercase the authority + path segments of an `op://` URI so two
|
|
56
|
+
* configs that differ only in case resolve to the same value (matching
|
|
57
|
+
* 1Password's case-insensitive behaviour).
|
|
58
|
+
*
|
|
59
|
+
* Exposed for tests.
|
|
60
|
+
*
|
|
61
|
+
* @stable
|
|
62
|
+
*/
|
|
63
|
+
function normalizeOpUri(raw) {
|
|
64
|
+
const cuts = [raw.indexOf("?"), raw.indexOf("#")].filter((i) => i >= 0);
|
|
65
|
+
const split = cuts.length > 0 ? Math.min(...cuts) : raw.length;
|
|
66
|
+
return raw.slice(0, split).toLowerCase() + raw.slice(split);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { createOnePasswordResolver, normalizeOpUri, onePasswordResolver };
|
|
71
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","names":["onePasswordResolver: SecretResolver"],"sources":["../src/resolver.ts"],"sourcesContent":["/**\n * `op://`-scheme `SecretResolver` for the Graphorin framework. Wraps\n * the official 1Password CLI (`op`) per the canonical `op read` flow\n * documented at https://developer.1password.com/docs/cli/secrets-references/.\n *\n * URI shape (per 1Password): `op://<vault>/<item>/[section/]<field>`.\n * The resolver accepts any `op://` URI the CLI accepts (the CLI is\n * the source of truth — we forward verbatim) and rejects only the\n * obvious malformed cases at parse time.\n *\n * 1Password explicitly treats vault / item / field names as\n * case-insensitive; the resolver normalises the URI by lowercasing the\n * authority + path before forwarding to the CLI so two configs that\n * differ only in case resolve to the same value.\n *\n * @packageDocumentation\n */\n\nimport type { SecretResolver, SecretResolverContext } from '@graphorin/core/contracts';\nimport { type ParsedSecretRef, SecretResolutionError, SecretValue } from '@graphorin/security';\n\nimport { createDefaultOpCli, type OpCli, OpCliError } from './op-cli.js';\n\n/** @stable */\nexport interface OnePasswordResolverOptions {\n /**\n * Inject a {@link OpCli} implementation. Defaults to a wrapper that\n * spawns the system `op` binary. Tests pass a stub.\n */\n readonly cli?: OpCli;\n /** Override the CLI binary path. Default `'op'`. */\n readonly binary?: string;\n /** Hard timeout per resolve. Default `15000` ms. */\n readonly timeoutMs?: number;\n /**\n * Optional service-account token. When set the resolver forwards it\n * via `OP_SERVICE_ACCOUNT_TOKEN` so the CLI runs in headless mode.\n * The token is itself a secret — pass a previously-resolved\n * `SecretValue` and use `.use(...)` to scope its lifetime.\n */\n readonly serviceAccountToken?: string;\n /**\n * Optional 1Password Connect host + token. Mutually exclusive with\n * a service-account token at the CLI level (the CLI honours the\n * Connect env vars if both are present).\n */\n readonly connect?: { readonly host: string; readonly token: string };\n /**\n * Optional `--account` override. Useful when the operator is signed\n * in to multiple 1Password accounts.\n */\n readonly account?: string;\n /**\n * If `true`, do **not** lowercase the URI before forwarding to the\n * CLI. Default `false`. Toggle only when interoperating with a\n * deployment that intentionally relies on case-sensitive keys.\n */\n readonly preserveCase?: boolean;\n}\n\n/**\n * Build a `SecretResolver` that honours the `op://` scheme. Register\n * with `registerResolver(...)` from `@graphorin/security` at app\n * bootstrap.\n *\n * @stable\n */\nexport function createOnePasswordResolver(\n options: OnePasswordResolverOptions = {},\n): SecretResolver {\n const cli = options.cli ?? createDefaultOpCli();\n return {\n scheme: 'op',\n async resolve(ref, ctx?: SecretResolverContext): Promise<SecretValue> {\n void ctx;\n const parsed = ref as ParsedSecretRef;\n assertOpRef(parsed);\n const normalized = options.preserveCase === true ? parsed.raw : normalizeOpUri(parsed.raw);\n try {\n const result = await cli.read(normalized, {\n ...(options.binary !== undefined ? { binary: options.binary } : {}),\n ...(options.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}),\n ...(options.serviceAccountToken !== undefined\n ? { serviceAccountToken: options.serviceAccountToken }\n : {}),\n ...(options.connect !== undefined ? { connect: options.connect } : {}),\n ...(options.account !== undefined ? { account: options.account } : {}),\n });\n if (result.value.length === 0) {\n throw new SecretResolutionError(\n 'op',\n parsed.raw,\n '1Password CLI returned an empty value; verify the field exists and is non-empty.',\n );\n }\n return SecretValue.fromString(result.value, {\n source: { resolver: 'op', ref: parsed.raw },\n });\n } catch (err) {\n if (err instanceof OpCliError) {\n throw new SecretResolutionError(\n 'op',\n parsed.raw,\n `${err.message}${err.hint !== undefined ? ` — hint: ${err.hint}` : ''}`,\n { cause: err },\n );\n }\n throw err;\n }\n },\n };\n}\n\n/**\n * Cache the default-options resolver so callers that just want the\n * happy-path behaviour can use a constant export.\n *\n * @stable\n */\nexport const onePasswordResolver: SecretResolver = createOnePasswordResolver();\n\nfunction assertOpRef(parsed: ParsedSecretRef): void {\n if (parsed.scheme !== 'op') {\n throw new SecretResolutionError(\n 'op',\n parsed.raw,\n `expected scheme 'op', received '${parsed.scheme}'.`,\n );\n }\n if (parsed.authority === undefined || parsed.authority.length === 0) {\n throw new SecretResolutionError(\n 'op',\n parsed.raw,\n \"op:// SecretRef must include a vault authority (e.g. 'op://Personal/Item/field').\",\n );\n }\n if (parsed.path === undefined || parsed.path.length <= 1) {\n throw new SecretResolutionError(\n 'op',\n parsed.raw,\n \"op:// SecretRef must include an item + field path segment (e.g. 'op://Vault/Item/field').\",\n );\n }\n const segments = parsed.path.split('/').filter((s) => s.length > 0);\n if (segments.length < 2) {\n throw new SecretResolutionError(\n 'op',\n parsed.raw,\n 'op:// SecretRef must reference at least an item + field (got ' +\n `${segments.length} path segment(s) after the vault).`,\n );\n }\n}\n\n/**\n * Lowercase the authority + path segments of an `op://` URI so two\n * configs that differ only in case resolve to the same value (matching\n * 1Password's case-insensitive behaviour).\n *\n * Exposed for tests.\n *\n * @stable\n */\nexport function normalizeOpUri(raw: string): string {\n // Lowercase only the part up to the first `?` or `#` so query /\n // fragment components keep their original case (the CLI does not\n // currently use them, but forward-compat matters).\n const queryIdx = raw.indexOf('?');\n const fragIdx = raw.indexOf('#');\n const cuts = [queryIdx, fragIdx].filter((i) => i >= 0);\n const split = cuts.length > 0 ? Math.min(...cuts) : raw.length;\n const head = raw.slice(0, split).toLowerCase();\n const tail = raw.slice(split);\n return head + tail;\n}\n"],"mappings":";;;;;;;;;;;AAmEA,SAAgB,0BACd,UAAsC,EAAE,EACxB;CAChB,MAAM,MAAM,QAAQ,OAAO,oBAAoB;AAC/C,QAAO;EACL,QAAQ;EACR,MAAM,QAAQ,KAAK,KAAmD;GAEpE,MAAM,SAAS;AACf,eAAY,OAAO;GACnB,MAAM,aAAa,QAAQ,iBAAiB,OAAO,OAAO,MAAM,eAAe,OAAO,IAAI;AAC1F,OAAI;IACF,MAAM,SAAS,MAAM,IAAI,KAAK,YAAY;KACxC,GAAI,QAAQ,WAAW,SAAY,EAAE,QAAQ,QAAQ,QAAQ,GAAG,EAAE;KAClE,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,WAAW,GAAG,EAAE;KAC3E,GAAI,QAAQ,wBAAwB,SAChC,EAAE,qBAAqB,QAAQ,qBAAqB,GACpD,EAAE;KACN,GAAI,QAAQ,YAAY,SAAY,EAAE,SAAS,QAAQ,SAAS,GAAG,EAAE;KACrE,GAAI,QAAQ,YAAY,SAAY,EAAE,SAAS,QAAQ,SAAS,GAAG,EAAE;KACtE,CAAC;AACF,QAAI,OAAO,MAAM,WAAW,EAC1B,OAAM,IAAI,sBACR,MACA,OAAO,KACP,mFACD;AAEH,WAAO,YAAY,WAAW,OAAO,OAAO,EAC1C,QAAQ;KAAE,UAAU;KAAM,KAAK,OAAO;KAAK,EAC5C,CAAC;YACK,KAAK;AACZ,QAAI,eAAe,WACjB,OAAM,IAAI,sBACR,MACA,OAAO,KACP,GAAG,IAAI,UAAU,IAAI,SAAS,SAAY,YAAY,IAAI,SAAS,MACnE,EAAE,OAAO,KAAK,CACf;AAEH,UAAM;;;EAGX;;;;;;;;AASH,MAAaA,sBAAsC,2BAA2B;AAE9E,SAAS,YAAY,QAA+B;AAClD,KAAI,OAAO,WAAW,KACpB,OAAM,IAAI,sBACR,MACA,OAAO,KACP,mCAAmC,OAAO,OAAO,IAClD;AAEH,KAAI,OAAO,cAAc,UAAa,OAAO,UAAU,WAAW,EAChE,OAAM,IAAI,sBACR,MACA,OAAO,KACP,oFACD;AAEH,KAAI,OAAO,SAAS,UAAa,OAAO,KAAK,UAAU,EACrD,OAAM,IAAI,sBACR,MACA,OAAO,KACP,4FACD;CAEH,MAAM,WAAW,OAAO,KAAK,MAAM,IAAI,CAAC,QAAQ,MAAM,EAAE,SAAS,EAAE;AACnE,KAAI,SAAS,SAAS,EACpB,OAAM,IAAI,sBACR,MACA,OAAO,KACP,gEACK,SAAS,OAAO,oCACtB;;;;;;;;;;;AAaL,SAAgB,eAAe,KAAqB;CAMlD,MAAM,OAAO,CAFI,IAAI,QAAQ,IAAI,EACjB,IAAI,QAAQ,IAAI,CACA,CAAC,QAAQ,MAAM,KAAK,EAAE;CACtD,MAAM,QAAQ,KAAK,SAAS,IAAI,KAAK,IAAI,GAAG,KAAK,GAAG,IAAI;AAGxD,QAFa,IAAI,MAAM,GAAG,MAAM,CAAC,aAAa,GACjC,IAAI,MAAM,MAAM"}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@graphorin/secret-1password",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Reference 1Password secret-resolver adapter for the Graphorin framework. Registers the `op://` scheme on top of `@graphorin/security`'s pluggable `SecretResolver` registry by shelling out to the official 1Password CLI (`op read 'op://<vault>/<item>/<field>'`). The resolver enforces a strict timeout, validates the URI before invoking the CLI, redacts the resolved value behind a `SecretValue`, and surfaces actionable errors when the CLI is missing, the user is not signed in, or the requested field is not present. Serves as the canonical template community packages should follow when wiring HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault, Bitwarden, or Unix `pass` as additional `SecretResolver` implementations. Created and maintained by Oleksiy Stepurenko.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Oleksiy Stepurenko",
|
|
7
|
+
"homepage": "https://github.com/o-stepper/graphorin/tree/main/packages/secret-1password",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/o-stepper/graphorin.git",
|
|
11
|
+
"directory": "packages/secret-1password"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/o-stepper/graphorin/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"graphorin",
|
|
18
|
+
"ai",
|
|
19
|
+
"agents",
|
|
20
|
+
"framework",
|
|
21
|
+
"secrets",
|
|
22
|
+
"1password",
|
|
23
|
+
"op-cli",
|
|
24
|
+
"secret-resolver",
|
|
25
|
+
"secret-ref"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=22.0.0"
|
|
30
|
+
},
|
|
31
|
+
"main": "./dist/index.js",
|
|
32
|
+
"module": "./dist/index.js",
|
|
33
|
+
"types": "./dist/index.d.ts",
|
|
34
|
+
"sideEffects": false,
|
|
35
|
+
"exports": {
|
|
36
|
+
".": {
|
|
37
|
+
"types": "./dist/index.d.ts",
|
|
38
|
+
"import": "./dist/index.js"
|
|
39
|
+
},
|
|
40
|
+
"./package.json": "./package.json"
|
|
41
|
+
},
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"CHANGELOG.md",
|
|
46
|
+
"LICENSE"
|
|
47
|
+
],
|
|
48
|
+
"dependencies": {
|
|
49
|
+
"@graphorin/core": "0.5.0",
|
|
50
|
+
"@graphorin/security": "0.5.0"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public",
|
|
54
|
+
"provenance": true
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsdown",
|
|
58
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.tests.json",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"lint": "biome check .",
|
|
61
|
+
"clean": "rimraf dist .turbo *.tsbuildinfo"
|
|
62
|
+
}
|
|
63
|
+
}
|