@qui-cli/env 4.0.1 → 5.0.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 CHANGED
@@ -2,6 +2,32 @@
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
+ ## [5.0.0](https://github.com/battis/qui-cli/compare/env/4.1.0...env/5.0.0) (2025-11-07)
6
+
7
+
8
+ ### ⚠ BREAKING CHANGES
9
+
10
+ * expose env-1password as a separate plugin
11
+ * separate 1Password without requiring knowledge of module structure
12
+ * no longer accepting 1Password service account token as environment variable
13
+
14
+ ### Features
15
+
16
+ * expose env-1password as a separate plugin ([90ecb79](https://github.com/battis/qui-cli/commit/90ecb7900b85ade45113fc5a5999f2a2ff87f580))
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * no longer accepting 1Password service account token as environment variable ([db7ae6a](https://github.com/battis/qui-cli/commit/db7ae6ac615c51a9966f201c85a8388bf4ebd76a))
22
+ * separate 1Password without requiring knowledge of module structure ([c21fe44](https://github.com/battis/qui-cli/commit/c21fe44d7d72d5d6bc4d092cb869164117378ab2))
23
+
24
+ ## [4.1.0](https://github.com/battis/qui-cli/compare/env/4.0.1...env/4.1.0) (2025-11-04)
25
+
26
+
27
+ ### Features
28
+
29
+ * standardize on modules labeling the options that they add to the usage man page ([eba86cd](https://github.com/battis/qui-cli/commit/eba86cd8a22a93aa6b6f19a3979272d87fe59274))
30
+
5
31
  ## [4.0.1](https://github.com/battis/qui-cli/compare/env/4.0.0...env/4.0.1) (2025-08-04)
6
32
 
7
33
 
package/README.md CHANGED
@@ -31,15 +31,7 @@ Any plugin that depends on this plugin can assume that the `.env` file environem
31
31
 
32
32
  ### 1Password integration
33
33
 
34
- If desired, this package has an implementation that 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.
35
-
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. Inject the Service Account Token into the environment with the name `OP_SERVICE_ACCOUNT_TOKEN`.
40
- 5. Run!
41
-
42
- See [dev-1password-env](https://github.com/battis/qui-cli/tree/main/examples/dev-1password-env#readme) for an example using the 1Password implementation of this package.
34
+ For 1Password integration supporting secret references in `.env`, use [@qui-cli/env-1password](https://npmjs.com/package/@qui-cli/env-1password) in place of this package.
43
35
 
44
36
  ## Configuration
45
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qui-cli/env",
3
- "version": "4.0.1",
3
+ "version": "5.0.0",
4
4
  "description": "@qui-cli Plugin: Standardized environment configuration",
5
5
  "homepage": "https://github.com/battis/qui-cli/tree/main/packages/env#readme",
6
6
  "repository": {
@@ -19,15 +19,15 @@
19
19
  "dependencies": {
20
20
  "@1password/sdk": "^0.3.1",
21
21
  "@battis/import-package-json": "^0.1.7",
22
- "dotenv": "^17.2.1"
22
+ "dotenv": "^17.2.3"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@tsconfig/node20": "^20.1.6",
26
- "@types/node": "^24.1.0",
27
- "commit-and-tag-version": "^12.5.2",
26
+ "@types/node": "^24.10.0",
27
+ "commit-and-tag-version": "^12.6.0",
28
28
  "del-cli": "^6.0.0",
29
29
  "npm-run-all": "^4.1.5",
30
- "typescript": "^5.9.2",
30
+ "typescript": "^5.9.3",
31
31
  "@qui-cli/plugin": "3.0.0",
32
32
  "@qui-cli/root": "3.0.1"
33
33
  },
package/1Password.d.ts DELETED
@@ -1,2 +0,0 @@
1
- import * as OP from './dist/1Password.js';
2
- export { OP };
package/1Password.js DELETED
@@ -1,6 +0,0 @@
1
- import { register } from '@qui-cli/plugin';
2
- import * as OP from './dist/1Password.js';
3
-
4
- await register(OP);
5
-
6
- export { OP };
@@ -1,15 +0,0 @@
1
- import * as Plugin from '@qui-cli/plugin';
2
- import * as Env from './Env.js';
3
- export type Configuration = Plugin.Configuration & Env.Configuration & {
4
- serviceAccountToken?: string;
5
- };
6
- export declare const name = "env";
7
- export declare function configure(config?: Configuration): Promise<void>;
8
- export declare function options(): Plugin.Options;
9
- export declare function init({ values }: Plugin.ExpectedArguments<typeof options>): Promise<void>;
10
- export declare function parse(file?: string): Promise<import("dotenv").DotenvParseOutput>;
11
- export declare function get({ key, file }: Env.GetOptions): Promise<string>;
12
- export declare function exists({ key, file }: Parameters<(typeof Env)['exists']>[0]): Promise<boolean>;
13
- /** Requires a service account with write privileges */
14
- export declare function set({ key, value, file, ...rest }: Env.SetOptions): Promise<void>;
15
- export declare const remove: typeof Env.remove;
package/dist/1Password.js DELETED
@@ -1,129 +0,0 @@
1
- import { createClient } from '@1password/sdk';
2
- import { importLocal } from '@battis/import-package-json';
3
- import path from 'node:path';
4
- import * as Env from './Env.js';
5
- const OP_SERVICE_ACCOUNT_TOKEN = 'OP_SERVICE_ACCOUNT_TOKEN';
6
- export const name = Env.name;
7
- let client = undefined;
8
- export async function configure(config = {}) {
9
- const { serviceAccountToken, load = true, ...rest } = config;
10
- await Env.configure({ ...rest, load: false });
11
- const auth = serviceAccountToken || process.env[OP_SERVICE_ACCOUNT_TOKEN];
12
- if (auth) {
13
- const pkg = await importLocal(path.join(import.meta.dirname, '../package.json'));
14
- client = await createClient({
15
- auth,
16
- integrationName: pkg.name.replace(/^(\/|@)/, '').replace(/[/@]+/g, '-'),
17
- integrationVersion: pkg.version
18
- });
19
- if (load) {
20
- await parse();
21
- }
22
- }
23
- else if (load) {
24
- await Env.parse();
25
- }
26
- }
27
- export function options() {
28
- return {
29
- opt: {
30
- serviceAccountToken: {
31
- description: `1Password service account token (defaults to ${OP_SERVICE_ACCOUNT_TOKEN}} environment variable, if present)`
32
- }
33
- }
34
- };
35
- }
36
- export async function init({ values }) {
37
- await configure(values);
38
- parse();
39
- }
40
- function isSecretReference(value) {
41
- return (value &&
42
- value !== null &&
43
- typeof value === 'string' &&
44
- /^op:\/\//.test(value));
45
- }
46
- function secretReferences(parsed) {
47
- return Object.fromEntries(Object.entries(parsed).filter((entry) => isSecretReference(entry[1])));
48
- }
49
- export async function parse(file) {
50
- const parsed = await Env.parse(file);
51
- if (client) {
52
- for (const key in secretReferences(parsed)) {
53
- parsed[key] = await client.secrets.resolve(parsed[key]);
54
- process.env[key] = parsed[key];
55
- }
56
- }
57
- else if (Object.keys(secretReferences(parsed)).length) {
58
- throw new Error('Attempt to parse .env file containing secret reference without a 1Password client');
59
- }
60
- return parsed;
61
- }
62
- export async function get({ key, file }) {
63
- return (await parse(file))[key];
64
- }
65
- export async function exists({ key, file }) {
66
- return Env.exists({ key, file });
67
- }
68
- function secretFrom(secretReference) {
69
- const [vault, item, section, field] = secretReference
70
- .replace('op://', '')
71
- .split('/');
72
- return { vault, item, section, field };
73
- }
74
- async function itemFrom(secret) {
75
- if (client) {
76
- const vault = (await client.vaults.list())
77
- .filter((vault) => vault.title == secret.vault)
78
- .shift();
79
- if (vault) {
80
- const item = (await client.items.list(vault.id))
81
- .filter((item) => item.title === secret.item || item.id === secret.item)
82
- .shift();
83
- if (item) {
84
- return await client.items.get(vault.id, item.id);
85
- }
86
- }
87
- }
88
- return undefined;
89
- }
90
- /** Requires a service account with write privileges */
91
- export async function set({ key, value, file, ...rest }) {
92
- const prev = await Env.get({ key, file });
93
- if (prev && isSecretReference(prev)) {
94
- if (client) {
95
- const secret = secretFrom(prev);
96
- const item = await itemFrom(secret);
97
- if (item) {
98
- const updated = {
99
- ...item,
100
- fields: item.fields.map((field) => {
101
- if (field.title === secret.field &&
102
- ((/^Section_.+/.test(secret.section) &&
103
- field.sectionId == secret.section.replace(/^Section_/, '')) ||
104
- field.sectionId ==
105
- item.sections
106
- .filter((section) => section.title === secret.section)
107
- .shift()?.id)) {
108
- return { ...field, value };
109
- }
110
- return field;
111
- })
112
- };
113
- await client.items.put(updated);
114
- }
115
- else {
116
- throw new Error('1Password item could not be found');
117
- }
118
- }
119
- else {
120
- throw new Error('Attempt to update .env file value that is currently a secret reference without a 1Password client');
121
- }
122
- }
123
- else {
124
- // FIXME handle creating a new secret reference
125
- // Issue URL: https://github.com/battis/qui-cli/issues/63
126
- return await Env.set({ key, value, file, ...rest });
127
- }
128
- }
129
- export const remove = Env.remove;