@theholocron/holocron-plugin-1password 2.0.0-alpha.0 → 2.0.0-alpha.5

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/README.md CHANGED
@@ -1,22 +1,73 @@
1
+ <!-- editorconfig-checker-disable-file -->
2
+
1
3
  # `@theholocron/holocron-plugin-1password`
2
4
 
3
- 1Password plugin for [Holocron](../cli). Implements the **required**
4
- `vault` capability via shell-out to the `op` CLI
5
- (https://developer.1password.com/docs/cli).
5
+ 1Password plugin for [Holocron](../cli). Implements the `vault`
6
+ capability via shell-out to the `op` CLI
7
+ (<https://developer.1password.com/docs/cli>). Also exports
8
+ `verifyToken` + `AUTH_HINT` for use by `holocron auth`.
9
+
10
+ ## One of several vault providers
11
+
12
+ `vault` is a REQUIRED capability, but 1Password is one of several
13
+ providers you can pick — the capability/provider model is designed
14
+ so you can swap by editing one config line. Peer plugins:
15
+
16
+ - **[`@theholocron/holocron-plugin-doppler`](../holocron-plugin-doppler)**
17
+ — REST-transport, Doppler-CLI-managed auth via the OS keychain
18
+ (`doppler login` → `holocron auth set doppler …`). This repo's
19
+ own default since `2.0.0-alpha.4`.
20
+ - **`@theholocron/holocron-plugin-infisical`** — planned (see [#97](https://github.com/theholocron/holocron/issues/97)),
21
+ will use the same REST + keyring shape.
22
+
23
+ ### When to choose 1Password
24
+
25
+ Reach for this plugin if:
26
+
27
+ - Your existing personal / team secrets already live in 1Password
28
+ and adding another vault provider is real friction.
29
+ - You value 1Password's biometric-first UX for laptop workflows
30
+ over the "REST + keyring" ergonomic set that the other plugins
31
+ offer.
32
+ - You're comfortable running the `op` CLI on every machine that
33
+ needs to reach your secrets — including CI, where you'd set
34
+ `OP_SERVICE_ACCOUNT_TOKEN` instead of the biometric flow.
35
+
36
+ Reach for one of the REST plugins (Doppler / Infisical) instead
37
+ when you want:
38
+
39
+ - Zero desktop-app dependency (Doppler-CLI on the laptop still
40
+ needed; nothing on CI beyond a bearer token).
41
+ - 100% REST transport across every capability call (this plugin
42
+ shells out per operation).
43
+ - A vault whose API you can reach without unlocking anything.
44
+
45
+ Both patterns are fully supported — switch by editing
46
+ `holocron.config.json`.
47
+
48
+ ## Install
49
+
50
+ ```bash
51
+ pnpm add -D @theholocron/holocron-plugin-1password@alpha
52
+ ```
53
+
54
+ Requires the `op` binary on PATH (see [Prerequisite](#prerequisite)).
6
55
 
7
56
  ## Why shell-out, not REST
8
57
 
9
58
  1Password's "REST API" is the **Connect server** — a Docker container
10
- you have to run yourself (or pay for the cloud-hosted version). For a
11
- solo / small-team workflow, that's overkill. The `op` CLI is what every
12
- developer already has installed and uses for hand-debugging anyway, so:
59
+ you have to run yourself (or pay for the cloud-hosted version). For
60
+ a solo / small-team workflow, that's overkill. The `op` CLI is what
61
+ every developer already has installed and uses for hand-debugging
62
+ anyway, so:
13
63
 
14
- - **Local dev:** developer's signed-in `op` CLI (biometric unlock via
15
- the desktop app)
16
- - **CI:** `OP_SERVICE_ACCOUNT_TOKEN` env var; `op` auto-detects it
17
- - **Either way:** same binary, same commands, same code path
64
+ - **Local dev**: developer's signed-in `op` CLI (biometric unlock
65
+ via the desktop app).
66
+ - **CI**: `OP_SERVICE_ACCOUNT_TOKEN` env var; `op` auto-detects it.
67
+ - **Either way**: same binary, same commands, same code path.
18
68
 
19
- The plugin's job is just to drive the CLI; auth handling is the CLI's.
69
+ The plugin's job is just to drive the CLI; auth handling is the
70
+ CLI's.
20
71
 
21
72
  ## Prerequisite
22
73
 
@@ -30,40 +81,75 @@ brew install 1password-cli # macOS
30
81
  The plugin throws a clear error at construction time if `op` isn't
31
82
  found.
32
83
 
84
+ ## Auth
85
+
86
+ This plugin does NOT store a bearer token — the `op` CLI manages
87
+ its own auth via the 1Password desktop app (biometric unlock on
88
+ laptops) or `OP_SERVICE_ACCOUNT_TOKEN` on CI. `holocron auth set
89
+ 1password` will accept a token, but since the plugin doesn't read
90
+ the keyring, storing one has no runtime effect. The `AUTH_HINT`
91
+ export makes this explicit in `holocron auth` output.
92
+
93
+ `holocron auth check 1password` runs `op whoami --format=json` to
94
+ confirm you're signed in — a useful sanity check independent of
95
+ holocron.
96
+
33
97
  ## Config
34
98
 
35
99
  ```jsonc
36
100
  {
37
- "providers": {
38
- "vault": ["1password", {
39
- "vault": "rando", // 1P vault name
40
- "account": "ABCDEFGHIJKLMNOPQRSTUVWXYZ" // optional: 1P account UUID
41
- }]
42
- }
101
+ "providers": {
102
+ "vault": [
103
+ "1password",
104
+ {
105
+ "vault": "rando", // 1P vault name
106
+ "account": "ABCDEFGHIJKLMNOPQRSTUVWXYZ", // optional: 1P account UUID
107
+ },
108
+ ],
109
+ },
43
110
  }
44
111
  ```
45
112
 
46
113
  - `vault` (required) — the 1Password vault name items live in.
47
- - `account` (optional) — 1P account UUID. Passes `--account <UUID>` on
48
- every `op` call so the integration targets a specific account even
49
- when the developer has multiple signed in (e.g., work + personal).
50
- Find via `op account list`.
114
+ - `account` (optional) — 1P account UUID. Passes `--account <UUID>`
115
+ on every `op` call so the integration targets a specific account
116
+ even when the developer has multiple signed in (e.g., work +
117
+ personal). Find via `op account list`.
51
118
 
52
119
  ## What's implemented
53
120
 
54
- | Method | What it does |
55
- | ---------------------- | ------------------------------------------------------------------------------ |
56
- | `read(reference)` | `op read <reference>` — resolves `op://Vault/Item/field`. `--no-newline`. |
57
- | `write(reference, v)` | Probe + `op item edit` if exists, else `op item create --category=API Credential`. |
58
- | `list()` | `op item list --vault=<vault> --format=json` — names of items in the vault. |
59
- | `environments()` | `op environment list --format=json` — names of 1P Environments. |
60
- | `readEnvironment(id)` | `op environment read <id>` — parses KEY=VALUE lines into a record. |
61
- | `whoami()` (via doctor)| `op whoami --format=json` — tolerates the JSON-shape drift across CLI versions and auth modes. |
121
+ | Method | What it does |
122
+ | --------------------- | ---------------------------------------------------------------------------------- |
123
+ | `read(reference)` | `op read <reference>` — resolves `op://Vault/Item/field`. `--no-newline`. |
124
+ | `write(reference, v)` | Probe + `op item edit` if exists, else `op item create --category=API Credential`. |
125
+ | `list()` | `op item list --vault=<vault> --format=json` — names of items in the vault. |
126
+ | `environments()` | `op environment list --format=json` — names of 1P Environments. |
127
+ | `readEnvironment(id)` | `op environment read <id>` — parses `KEY=VALUE` lines into a record. |
128
+
129
+ Plugin-level exports (not capability methods, per the auth-bootstrap
130
+ convention):
131
+
132
+ | Export | Purpose |
133
+ | ------------- | ----------------------------------------------------------------------------------------------------------- |
134
+ | `verifyToken` | `op whoami --format=json` — token arg ignored (see [Auth](#auth)). Returns `ok: true` when signed in. |
135
+ | `AUTH_HINT` | Explains the `op signin` / `OP_SERVICE_ACCOUNT_TOKEN` model to operators — 1P has no bearer token to store. |
136
+
137
+ ### Not implemented (deliberately)
138
+
139
+ `ensureProject` / `ensureEnvironment` — 1Password's data model
140
+ doesn't have projects with sub-configs the way Doppler / Infisical
141
+ do. The vault + item hierarchy is created via the 1P UI or `op item
142
+ create`, not via `holocron setup`. The methods are simply omitted;
143
+ `runSetup` skips them cleanly (see the [`Vault`
144
+ interface](../cli/src/capabilities/index.ts)).
62
145
 
63
146
  ## Status
64
147
 
65
- **v0.0.0 first port.** Ports `rando-id/rando.id`
66
- `adapters/op-cli.ts`. The stdio shape (`['inherit', 'pipe', 'pipe']`)
67
- is critical: it gives `op` a TTY signal so it can fire the desktop
148
+ **`v2.0.0-alpha.1`** (or later check [releases](https://github.com/theholocron/holocron/releases)).
149
+ Published on npm under the `alpha` dist-tag. APIs may still shift
150
+ before stable v2.0.0.
151
+
152
+ Implementation note: the stdio shape (`['inherit', 'pipe', 'pipe']`)
153
+ is critical — it gives `op` a TTY signal so it can fire the desktop
68
154
  biometric unlock dialog when running locally. CI runs see no TTY and
69
155
  fall back to whatever auth mode the env var configures.
package/dist/index.d.mts CHANGED
@@ -60,6 +60,22 @@ declare class OpVault implements Vault {
60
60
  readEnvironment(environmentId: string): Promise<Record<string, string>>;
61
61
  }
62
62
  //#endregion
63
+ //#region src/verify-token.d.ts
64
+ interface VerifyTokenSuccess {
65
+ ok: true;
66
+ subject: string;
67
+ }
68
+ interface VerifyTokenFailure {
69
+ ok: false;
70
+ message: string;
71
+ }
72
+ type VerifyTokenResult = VerifyTokenSuccess | VerifyTokenFailure;
73
+ interface VerifyTokenOptions {
74
+ spawn?: typeof spawnSync;
75
+ binary?: string;
76
+ }
77
+ declare function verifyToken(_token: string, opts?: VerifyTokenOptions): Promise<VerifyTokenResult>;
78
+ //#endregion
63
79
  //#region src/index.d.ts
64
80
  interface OpPluginOptions extends VerifyInput {
65
81
  /** 1P vault name items live in. Required. */
@@ -79,5 +95,11 @@ declare function createPlugin(options: OpPluginOptions): {
79
95
  vault: () => Vault;
80
96
  };
81
97
  };
98
+ /**
99
+ * One-line hint printed by `holocron auth set 1password` — 1P doesn't
100
+ * store a token in the holocron keyring; the `op` CLI manages its own
101
+ * auth. Directs the operator to the two paths that actually work.
102
+ */
103
+ declare const AUTH_HINT: string;
82
104
  //#endregion
83
- export { AuthError, OpPluginOptions, OpShell, OpVault, PluginContext, VerifyInput, createContext, createPlugin, vault, verifyOpInstalled };
105
+ export { AUTH_HINT, AuthError, OpPluginOptions, OpShell, OpVault, PluginContext, VerifyInput, type VerifyTokenFailure, type VerifyTokenResult, type VerifyTokenSuccess, createContext, createPlugin, vault, verifyOpInstalled, verifyToken };
package/dist/index.mjs CHANGED
@@ -211,6 +211,53 @@ var OpShell = class {
211
211
  }
212
212
  };
213
213
  //#endregion
214
+ //#region src/verify-token.ts
215
+ /**
216
+ * `verifyToken` — plugin-level export used by `holocron auth check`.
217
+ *
218
+ * 1Password doesn't use a bearer token like the REST-transport plugins
219
+ * do — the `op` CLI manages its own auth via desktop-app biometric on
220
+ * laptops and `OP_SERVICE_ACCOUNT_TOKEN` env var in CI. So the token
221
+ * argument is intentionally IGNORED here. What we actually check is:
222
+ * is `op` installed AND signed in (i.e., can it answer `whoami`).
223
+ *
224
+ * If someone tries `holocron auth set 1password <token>`, the token
225
+ * will be stored in the keyring but never read by this plugin — the
226
+ * `AUTH_HINT` below explains that up front.
227
+ */
228
+ async function verifyToken(_token, opts = {}) {
229
+ const spawnImpl = opts.spawn ?? spawnSync;
230
+ const binary = opts.binary ?? "op";
231
+ const result = spawnImpl(binary, ["whoami", "--format=json"], {
232
+ encoding: "utf-8",
233
+ stdio: [
234
+ "ignore",
235
+ "pipe",
236
+ "pipe"
237
+ ]
238
+ });
239
+ if (result.error) return {
240
+ ok: false,
241
+ message: `1Password CLI (\`${binary}\`) not found on PATH. Install via \`brew install 1password-cli\`.`
242
+ };
243
+ if (result.status !== 0) return {
244
+ ok: false,
245
+ message: `\`${binary} whoami\` failed: ${(result.stderr ?? "").trim() || `exit ${result.status ?? "?"}`}`
246
+ };
247
+ try {
248
+ const parsed = JSON.parse(result.stdout ?? "{}");
249
+ return {
250
+ ok: true,
251
+ subject: `op: ${parsed.email ?? parsed.url ?? parsed.user_uuid ?? "signed in"}`
252
+ };
253
+ } catch {
254
+ return {
255
+ ok: true,
256
+ subject: "op: signed in"
257
+ };
258
+ }
259
+ }
260
+ //#endregion
214
261
  //#region src/index.ts
215
262
  function createContext(options) {
216
263
  if (!options.vault) throw new Error("@theholocron/holocron-plugin-1password requires `vault` in options");
@@ -237,5 +284,11 @@ function createPlugin(options) {
237
284
  capabilities: { vault: () => vault(ctx) }
238
285
  };
239
286
  }
287
+ /**
288
+ * One-line hint printed by `holocron auth set 1password` — 1P doesn't
289
+ * store a token in the holocron keyring; the `op` CLI manages its own
290
+ * auth. Directs the operator to the two paths that actually work.
291
+ */
292
+ const AUTH_HINT = "1Password uses the `op` CLI for auth — no bearer token to store. On laptop: `op signin` (or the desktop app's biometric flow). In CI: set `OP_SERVICE_ACCOUNT_TOKEN` in the workflow env.";
240
293
  //#endregion
241
- export { AuthError, OpShell, OpVault, createContext, createPlugin, vault, verifyOpInstalled };
294
+ export { AUTH_HINT, AuthError, OpShell, OpVault, createContext, createPlugin, vault, verifyOpInstalled, verifyToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theholocron/holocron-plugin-1password",
3
- "version": "2.0.0-alpha.0",
3
+ "version": "2.0.0-alpha.5",
4
4
  "description": "Holocron plugin for 1Password. Implements the vault capability via shell-out to the `op` CLI (https://developer.1password.com/docs/cli).",
5
5
  "homepage": "https://github.com/theholocron/holocron/tree/main/packages/holocron-plugin-1password#readme",
6
6
  "bugs": "https://github.com/theholocron/holocron/issues",
@@ -21,7 +21,7 @@
21
21
  }
22
22
  },
23
23
  "peerDependencies": {
24
- "@theholocron/cli": "2.0.0-alpha.0"
24
+ "@theholocron/cli": "2.0.0-alpha.5"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@theholocron/tsconfig": "^4.1.0",
@@ -32,7 +32,8 @@
32
32
  "typescript": "^5.9.3",
33
33
  "vitest": "^3.2.6",
34
34
  "tsdown": "^0.22.3",
35
- "@theholocron/cli": "2.0.0-alpha.0"
35
+ "tsx": "^4.22.4",
36
+ "@theholocron/cli": "2.0.0-alpha.5"
36
37
  },
37
38
  "publishConfig": {
38
39
  "access": "public"
@@ -47,7 +48,8 @@
47
48
  "typecheck": "tsc --noEmit",
48
49
  "test": "vitest run",
49
50
  "test:watch": "vitest",
50
- "test:coverage": "vitest run --coverage"
51
+ "test:coverage": "vitest run --coverage",
52
+ "validate": "tsx scripts/validate.mjs"
51
53
  },
52
54
  "types": "./dist/index.d.mts"
53
55
  }