@qui-cli/env-1password 1.2.7 → 1.2.9

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 CHANGED
@@ -2,6 +2,15 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
4
4
 
5
+ ## [1.2.9](https://github.com/battis/qui-cli/compare/env-1password/1.2.8...env-1password/1.2.9) (2026-01-19)
6
+
7
+ ## [1.2.8](https://github.com/battis/qui-cli/compare/env-1password/1.2.7...env-1password/1.2.8) (2026-01-18)
8
+
9
+
10
+ ### Bug Fixes
11
+
12
+ * compile against Node.js v24 ([7b06b4f](https://github.com/battis/qui-cli/commit/7b06b4f4ac4f9688719041ab8b1d837b3a0ee214))
13
+
5
14
  ## [1.2.7](https://github.com/battis/qui-cli/compare/env-1password/1.2.6...env-1password/1.2.7) (2026-01-17)
6
15
 
7
16
 
package/README.md CHANGED
@@ -1,20 +1,24 @@
1
1
  # @qui-cli/env-1password
2
2
 
3
- @qui-cli Plugin: Standardized environment configuration
3
+ @qui-cli Plugin: Standardized environment configuration with 1Password support
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/@qui-cli%2Fenv-1password.svg)](https://npmjs.com/package/@qui-cli/env-1password)
6
6
  [![Module type: ESM](https://img.shields.io/badge/module%20type-esm-brightgreen)](https://nodejs.org/api/esm.html)
7
7
 
8
+ ## Note
9
+
10
+ This plugin is identical to [@qui-cli/env](https://npmjs.com/package/@qui-cli/env), with the exception that it includes that package's optional peer dependency [@1password/sdk](https://www.npmjs.com/package/@1password/sdk).
11
+
8
12
  ## Install
9
13
 
10
14
  ```sh
11
- npm install @qui-cli/env-1password
15
+ npm install @qui-cli/env
12
16
  ```
13
17
 
14
18
  ## Usage
15
19
 
16
20
  ```ts
17
- import { Env } from '@qui-cli/env-1password';
21
+ import { Env } from '@qui-cli/env';
18
22
 
19
23
  // configure desired environment path
20
24
  Env.configure({ path: '../../.env' });
@@ -31,14 +35,19 @@ Any plugin that depends on this plugin can assume that the `.env` file environem
31
35
 
32
36
  ### 1Password integration
33
37
 
34
- This package integrates with [@1password/sdk](https://www.npmjs.com/package/@1password/sdk) to inject values from 1Password vaults into the environment for use by the script.
38
+ For full integration, also install the [1Password CLI](https://developer.1password.com/docs/cli/) which will allow you to look up a [1Password service account](https://developer.1password.com/docs/service-accounts/security/) token by identifier.
35
39
 
36
- 1. Follow [the 1Password CLI directions to create a service account](https://developer.1password.com/docs/service-accounts/get-started/).
37
- 2. Install [@1password/sdk](https://www.npmjs.com/package/@1password/sdk) as a peer of `@qui-cli/env`.
38
- 3. Update environment variables to be [secret references](https://developer.1password.com/docs/cli/secret-references)
39
- 4. Run!
40
+ The configuration options `opToken`, `opItem`, and `opAccount` may all be passed as command-line options. For example:
40
41
 
41
- See [dev-env-1password](https://github.com/battis/qui-cli/tree/main/examples/dev-env-1password#readme) for an example using the 1Password implementation of this package.
42
+ ```sh
43
+ example --opToken "$(op item get ServiceAccountToken)"
44
+ ```
45
+
46
+ If the [1Password CLI tool](https://developer.1password.com/docs/cli) is installed, then `opItem` and `opAccount` can be used:
47
+
48
+ ```sh
49
+ example --opAccount example.1password.com --opitem "My Token Identifier"
50
+ ```
42
51
 
43
52
  ## Configuration
44
53
 
@@ -62,40 +71,34 @@ Whether or not to load the `.env` file into `process.env` immediately. Defaults
62
71
 
63
72
  Path to desired `.env` file relative to `root`. Defaults to `'.env'`;
64
73
 
65
- ### `opToken`
74
+ ### 1Password configuration
66
75
 
67
- 1Password service account token
76
+ If 1Password secret references are stored in the environment, a 1Password service account token is required to access the secret values.
68
77
 
69
- ### `opItem`
78
+ #### `opToken`
70
79
 
71
- Name or ID of the 1Password API Credential item storing the 1Password service account token
80
+ 1Password service account token; will use environment variable `OP_TOKEN` if present
72
81
 
73
- ### `opAccount`
82
+ #### `opItem`
74
83
 
75
- 1Password account to use (if signed into multiple)
84
+ Name or ID of the 1Password API Credential item storing the 1Password service account token; will use environment variable `OP_ITEM` if present. Requires the [1Password CLI tool](https://developer.1password.com/docs/cli).
76
85
 
77
- ## Options
78
-
79
- The configuration options `opToken`, `opItem`, and `opAccount` may all be passed as command-line options. For example:
86
+ #### `opAccount`
80
87
 
81
- ```sh
82
- example --opToken "$(op item get ServiceAccountToken)"
83
- ```
88
+ 1Password account to use (if signed into multiple); will use environment variable `OP_ACCOUNT` if present
84
89
 
85
- If the [1Password CLI tool](https://developer.1password.com/docs/cli) is installed, then `opItem` and `opAccount` can be used:
90
+ ## Options
86
91
 
87
- ```sh
88
- example --opItem ServiceAccountToken
89
- ```
92
+ When the optional peer dependency [@1password/sdk](https://www.npmjs.com/package/@1password/sdk) is installed, `Env` exposes `opAccount`, `opItem`, and `opToken` as command-line options.
90
93
 
91
94
  ## Initialization
92
95
 
93
- `Env` loads the specified `.env` file into `process.env`. If 1Password secret references are found in the environment, those references are loaded into the `process.env` from the 1Password vault (if a service account token is provided).
96
+ `Env` loads the specified `.env` file into `process.env`.
94
97
 
95
98
  ## API
96
99
 
97
100
  ```ts
98
- import { Env } from '@qui-cli/env-1password';
101
+ import { Env } from '@qui-cli/env';
99
102
  ```
100
103
 
101
104
  ### `Env.parse(file?)`
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
- import * as Env from './1Password.js';
2
- export { Env };
1
+ export * from '@qui-cli/env';
package/dist/index.js CHANGED
@@ -1,4 +1 @@
1
- import { register } from '@qui-cli/plugin';
2
- import * as Env from './1Password.js';
3
- export { Env };
4
- await register(Env);
1
+ export * from '@qui-cli/env';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@qui-cli/env-1password",
3
- "version": "1.2.7",
4
- "description": "@qui-cli Plugin: Standardized environment configuration",
3
+ "version": "1.2.9",
4
+ "description": "@qui-cli Plugin: Standardized environment configuration with 1Password support",
5
5
  "homepage": "https://github.com/battis/qui-cli/tree/main/packages/env-1password#readme",
6
6
  "repository": {
7
7
  "type": "git",
@@ -18,28 +18,20 @@
18
18
  "types": "./dist/index.d.ts",
19
19
  "dependencies": {
20
20
  "@1password/sdk": "^0.3.1",
21
- "@battis/import-package-json": "^0.1.7",
22
- "dotenv": "^17.2.3",
23
- "@qui-cli/colors": "3.2.2"
21
+ "@battis/import-package-json": "^0.1.7"
24
22
  },
25
23
  "devDependencies": {
26
- "@tsconfig/node20": "^20.1.8",
27
- "@types/node": "^24.10.4",
24
+ "@tsconfig/node24": "^24.0.4",
25
+ "@types/node": "^24.10.9",
28
26
  "commit-and-tag-version": "^12.6.1",
29
- "del-cli": "^6.0.0",
27
+ "del-cli": "^7.0.0",
30
28
  "npm-run-all": "^4.1.5",
31
29
  "typescript": "^5.9.3",
32
- "@qui-cli/log": "4.0.2",
33
- "@qui-cli/plugin": "4.0.0",
34
- "@qui-cli/root": "3.0.6",
35
- "@qui-cli/shell": "3.1.1",
36
- "@qui-cli/env": "5.0.3"
30
+ "@qui-cli/shell": "3.1.2",
31
+ "@qui-cli/env": "5.1.0"
37
32
  },
38
33
  "peerDependencies": {
39
34
  "@qui-cli/env": ">=5",
40
- "@qui-cli/log": ">=3",
41
- "@qui-cli/plugin": ">=3",
42
- "@qui-cli/root": ">=3",
43
35
  "@qui-cli/shell": ">=3"
44
36
  },
45
37
  "target": "node",
@@ -1,31 +0,0 @@
1
- import { Env } from '@qui-cli/env';
2
- import * as Plugin from '@qui-cli/plugin';
3
- export type Configuration = Plugin.Configuration & Env.Configuration & {
4
- /**
5
- * 1Password service account token; will use the environment variable
6
- * OP_TOKEN if present
7
- */
8
- opToken?: string;
9
- /**
10
- * Name or ID of the 1Password API Credential item storing the 1Password
11
- * service account token; will use environment variable OP_ITEM if present
12
- */
13
- opItem?: string;
14
- /**
15
- * 1Password account to use (if signed into multiple); will use environment
16
- * variable OP_ACCOUNT if present
17
- */
18
- opAccount?: string;
19
- /** @deprecated Use {@link opToken} */
20
- serviceAccountToken?: string;
21
- };
22
- export declare const name = "env-1password";
23
- export declare function configure(proposal?: Configuration): Promise<void>;
24
- export declare function options(): Plugin.Options;
25
- export declare function init({ values }: Plugin.ExpectedArguments<typeof options>): Promise<void>;
26
- export declare function parse(file?: string): Promise<import("dotenv").DotenvParseOutput>;
27
- export declare function get({ key, file }: Env.GetOptions): Promise<string>;
28
- export declare function exists({ key, file }: Parameters<(typeof Env)['exists']>[0]): Promise<boolean>;
29
- /** Requires a service account with write privileges */
30
- export declare function set({ key, value, file, ...rest }: Env.SetOptions): Promise<void>;
31
- export declare const remove: typeof Env.remove;
package/dist/1Password.js DELETED
@@ -1,191 +0,0 @@
1
- import { createClient } from '@1password/sdk';
2
- import { importLocal } from '@battis/import-package-json';
3
- import { Colors } from '@qui-cli/colors';
4
- import { Env } from '@qui-cli/env';
5
- import { Log } from '@qui-cli/log';
6
- import { Shell } from '@qui-cli/shell';
7
- import path from 'node:path';
8
- export const name = 'env-1password';
9
- let client = undefined;
10
- const config = {};
11
- export async function configure(proposal = {}) {
12
- for (const key in proposal) {
13
- if (proposal[key] !== undefined) {
14
- config[key] = proposal[key];
15
- }
16
- }
17
- config.opToken = config.opToken || config.serviceAccountToken;
18
- await Env.configure({ ...config, load: false });
19
- if (config.opItem && !config.opToken) {
20
- const silent = Shell.isSilent();
21
- const showCommands = Shell.commandsShown();
22
- const logging = Shell.isLogging();
23
- Shell.configure({ silent: true, showCommands: false, logging: false });
24
- const { stdout, stderr } = Shell.exec(`op item get ${config.opAccount ? `--account "${config.opAccount}" ` : ''}--reveal --fields credential "${config.opItem}"`);
25
- if (stdout.length) {
26
- config.opToken = stdout.trim();
27
- }
28
- else {
29
- Log.fatal(stderr);
30
- process.exit(1);
31
- }
32
- Shell.configure({ silent, showCommands, logging });
33
- }
34
- if (config.opToken) {
35
- const pkg = await importLocal(path.join(import.meta.dirname, '../package.json'));
36
- client = await createClient({
37
- auth: config.opToken,
38
- integrationName: pkg.name.replace(/^(\/|@)/, '').replace(/[/@]+/g, '-'),
39
- integrationVersion: pkg.version
40
- });
41
- if (config.load) {
42
- await parse();
43
- }
44
- }
45
- else if (config.load) {
46
- await Env.parse();
47
- }
48
- }
49
- export function options() {
50
- return {
51
- man: [
52
- {
53
- level: 1,
54
- text: '1Password environment integration'
55
- },
56
- {
57
- text: 'Store 1Password secret references in your environment, rather than the actual secrets.'
58
- },
59
- {
60
- text: `If 1Password secret references are stored in the environment, a 1Password service account token is required to access the secret values, which will be loaded into ${Colors.varName('process.env')}. The service account token can be passed directly as the ${Colors.optionArg('--opToken')} argument (e.g. ${Colors.command(`example --opToken "$(${Colors.keyword('op')} item get myToken)"`, Colors.keyword)}) or, if the 1Password CLI tool is also installed, by simply passing the name or ID of the API Credential in your 1Password vault that holds the service account token (e.g. ${Colors.command(`example --opItem myToken`, Colors.keyword)}). If you are signed into multiple 1Password account, use the ${Colors.optionArg('--opAccount')} argument to specify the account containing the token.`
61
- },
62
- { text: Colors.url('https://developer.1password.com/docs/cli') }
63
- ],
64
- opt: {
65
- opAccount: {
66
- description: `1Password account to use (if signed into multiple); will use environment variable ${Colors.varName('OP_ACCOUNT')} if present`,
67
- hint: 'example.1password.com',
68
- default: config.opAccount
69
- },
70
- opItem: {
71
- description: `Name or ID of the 1Password API Credential item storing the 1Password service account token; will use environment variable ${Colors.varName('OP_ITEM')} if present`,
72
- hint: '1Password unique identifier',
73
- default: config.opItem
74
- },
75
- opToken: {
76
- description: `1Password service account token; will use environment variable ${Colors.varName('OP_TOKEN')} if present`,
77
- hint: 'token value',
78
- secret: true,
79
- default: config.opToken || config.serviceAccountToken
80
- },
81
- serviceAccountToken: {
82
- description: `1Password service account token (deprecated, use ${Colors.optionArg('--opToken')})`,
83
- hint: 'token value',
84
- secret: true,
85
- default: config.serviceAccountToken
86
- }
87
- }
88
- };
89
- }
90
- export async function init({ values }) {
91
- const { opAccount = process.env.OP_ACCOUNT, opItem = process.env.OP_ITEM, opToken = process.env.OP_TOKEN } = values;
92
- await configure({ opAccount, opItem, opToken, ...values });
93
- parse();
94
- }
95
- function isSecretReference(value) {
96
- return (value &&
97
- value !== null &&
98
- typeof value === 'string' &&
99
- /^op:\/\//.test(value));
100
- }
101
- function secretReferences(parsed) {
102
- return Object.fromEntries(Object.entries(parsed).filter((entry) => isSecretReference(entry[1])));
103
- }
104
- // FIXME parse is not loading 1Password secrets into process.env
105
- // Issue URL: https://github.com/battis/qui-cli/issues/81
106
- export async function parse(file) {
107
- const parsed = await Env.parse(file);
108
- if (client) {
109
- for (const key in secretReferences(parsed)) {
110
- parsed[key] = await client.secrets.resolve(parsed[key]);
111
- process.env[key] = parsed[key];
112
- }
113
- }
114
- else if (Object.keys(secretReferences(parsed)).length) {
115
- throw new Error('Attempt to parse .env file containing secret reference without a 1Password client');
116
- }
117
- return parsed;
118
- }
119
- export async function get({ key, file }) {
120
- return (await parse(file))[key];
121
- }
122
- export async function exists({ key, file }) {
123
- return Env.exists({ key, file });
124
- }
125
- function secretFrom(secretReference) {
126
- // eslint-disable-next-line prefer-const
127
- let [vault, item, section, field] = secretReference
128
- .replace(/^op:\/\//, '')
129
- .split('/');
130
- if (field === undefined) {
131
- field = section;
132
- section = '';
133
- }
134
- return { vault, item, section, field };
135
- }
136
- async function itemFrom(secret) {
137
- if (client) {
138
- const vault = (await client.vaults.list())
139
- .filter((vault) => vault.title == secret.vault)
140
- .shift();
141
- if (vault) {
142
- const item = (await client.items.list(vault.id))
143
- .filter((item) => item.title === secret.item || item.id === secret.item)
144
- .shift();
145
- if (item) {
146
- return await client.items.get(vault.id, item.id);
147
- }
148
- }
149
- }
150
- return undefined;
151
- }
152
- /** Requires a service account with write privileges */
153
- export async function set({ key, value, file, ...rest }) {
154
- const prev = await Env.get({ key, file });
155
- if (prev && isSecretReference(prev)) {
156
- if (client) {
157
- const secret = secretFrom(prev);
158
- const item = await itemFrom(secret);
159
- if (item) {
160
- const updated = {
161
- ...item,
162
- fields: item.fields.map((field) => {
163
- const section = item.sections.find((s) => s.id === field.sectionId);
164
- if (secret.field === field.title || secret.field === field.id) {
165
- if ((!secret.section &&
166
- (field.sectionId === '' || field.sectionId === 'add more')) ||
167
- secret.section === section?.title ||
168
- secret.section === section?.id) {
169
- return { ...field, value };
170
- }
171
- }
172
- return field;
173
- })
174
- };
175
- await client.items.put(updated);
176
- }
177
- else {
178
- throw new Error('1Password item could not be found');
179
- }
180
- }
181
- else {
182
- throw new Error('Attempt to update .env file value that is currently a secret reference without a 1Password client');
183
- }
184
- }
185
- else {
186
- // FIXME handle creating a new secret reference
187
- // Issue URL: https://github.com/battis/qui-cli/issues/63
188
- return await Env.set({ key, value, file, ...rest });
189
- }
190
- }
191
- export const remove = Env.remove;