@qui-cli/env 5.0.3 → 5.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +30 -2
- package/dist/1Password/1Password.d.ts +12 -0
- package/dist/1Password/1Password.js +157 -0
- package/dist/1Password/Configuration.d.ts +18 -0
- package/dist/1Password/Configuration.js +1 -0
- package/dist/1Password/index.d.ts +3 -0
- package/dist/1Password/index.js +3 -0
- package/dist/1Password/isSecretReference.d.ts +1 -0
- package/dist/1Password/isSecretReference.js +6 -0
- package/dist/Env.d.ts +11 -4
- package/dist/Env.js +88 -34
- package/package.json +23 -10
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
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.1.0](https://github.com/battis/qui-cli/compare/env/5.0.4...env/5.1.0) (2026-01-19)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* support 1Password secret references if @1password/sdk (and 1Password) installed ([ed4570c](https://github.com/battis/qui-cli/commit/ed4570cd5cf19ae34fc03f6b88b01f0ebf72e72d))
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* detect missing 1Password CLI ([3c306ba](https://github.com/battis/qui-cli/commit/3c306ba7e37394948666f96b1ad728e8abc65a4e)), closes [#81](https://github.com/battis/qui-cli/issues/81)
|
|
16
|
+
|
|
17
|
+
## [5.0.4](https://github.com/battis/qui-cli/compare/env/5.0.3...env/5.0.4) (2026-01-18)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* compile against Node.js v24 ([7b06b4f](https://github.com/battis/qui-cli/commit/7b06b4f4ac4f9688719041ab8b1d837b3a0ee214))
|
|
23
|
+
|
|
5
24
|
## [5.0.3](https://github.com/battis/qui-cli/compare/env/5.0.2...env/5.0.3) (2026-01-02)
|
|
6
25
|
|
|
7
26
|
|
package/README.md
CHANGED
|
@@ -31,7 +31,19 @@ Any plugin that depends on this plugin can assume that the `.env` file environem
|
|
|
31
31
|
|
|
32
32
|
### 1Password integration
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
To access 1Password secret references stored in the environment, install the optional peer dependency [@1password/sdk](https://www.npmjs.com/package/@1password/sdk). 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
|
+
|
|
36
|
+
The configuration options `opToken`, `opItem`, and `opAccount` may all be passed as command-line options. For example:
|
|
37
|
+
|
|
38
|
+
```sh
|
|
39
|
+
example --opToken "$(op item get ServiceAccountToken)"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
If the [1Password CLI tool](https://developer.1password.com/docs/cli) is installed, then `opItem` and `opAccount` can be used:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
example --opAccount example.1password.com --opitem "My Token Identifier"
|
|
46
|
+
```
|
|
35
47
|
|
|
36
48
|
## Configuration
|
|
37
49
|
|
|
@@ -55,9 +67,25 @@ Whether or not to load the `.env` file into `process.env` immediately. Defaults
|
|
|
55
67
|
|
|
56
68
|
Path to desired `.env` file relative to `root`. Defaults to `'.env'`;
|
|
57
69
|
|
|
70
|
+
### 1Password configuration
|
|
71
|
+
|
|
72
|
+
If 1Password secret references are stored in the environment, a 1Password service account token is required to access the secret values.
|
|
73
|
+
|
|
74
|
+
#### `opToken`
|
|
75
|
+
|
|
76
|
+
1Password service account token; will use environment variable `OP_TOKEN` if present
|
|
77
|
+
|
|
78
|
+
#### `opItem`
|
|
79
|
+
|
|
80
|
+
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).
|
|
81
|
+
|
|
82
|
+
#### `opAccount`
|
|
83
|
+
|
|
84
|
+
1Password account to use (if signed into multiple); will use environment variable `OP_ACCOUNT` if present
|
|
85
|
+
|
|
58
86
|
## Options
|
|
59
87
|
|
|
60
|
-
`Env`
|
|
88
|
+
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.
|
|
61
89
|
|
|
62
90
|
## Initialization
|
|
63
91
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as Plugin from '@qui-cli/plugin';
|
|
2
|
+
import { OPConfiguration } from './Configuration.js';
|
|
3
|
+
export declare const name = "1password support";
|
|
4
|
+
export declare function configure(proposal?: OPConfiguration): Promise<void>;
|
|
5
|
+
export declare function options(): Plugin.Options;
|
|
6
|
+
export declare function init({ values }: Plugin.ExpectedArguments<typeof options>): Promise<void>;
|
|
7
|
+
export declare function get(ref: string): Promise<string>;
|
|
8
|
+
/** Requires a service account with write privileges */
|
|
9
|
+
export declare function set({ ref, value }: {
|
|
10
|
+
ref: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { createClient } from '@1password/sdk';
|
|
2
|
+
import { importLocal } from '@battis/import-package-json';
|
|
3
|
+
import { Colors } from '@qui-cli/colors';
|
|
4
|
+
import { Log } from '@qui-cli/log';
|
|
5
|
+
import { Shell } from '@qui-cli/shell';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
export const name = '1password support';
|
|
9
|
+
const config = {};
|
|
10
|
+
let _client = undefined;
|
|
11
|
+
async function getClient() {
|
|
12
|
+
if (!_client) {
|
|
13
|
+
const spinner = ora('Loading 1Password').start();
|
|
14
|
+
if (config.opItem && !config.opToken) {
|
|
15
|
+
const silent = Shell.isSilent();
|
|
16
|
+
const showCommands = Shell.commandsShown();
|
|
17
|
+
const logging = Shell.isLogging();
|
|
18
|
+
Shell.configure({ silent: true, showCommands: false, logging: false });
|
|
19
|
+
if (/(\d+\.)+\d/.test(Shell.exec('op -v').stdout)) {
|
|
20
|
+
const { stdout, stderr } = Shell.exec(`op item get ${config.opAccount ? `--account "${config.opAccount}" ` : ''}--reveal --fields credential "${config.opItem}"`);
|
|
21
|
+
if (stdout.length) {
|
|
22
|
+
config.opToken = stdout.trim();
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
Log.fatal(stderr);
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
throw new Error(`Looking up a 1Password service account token by item identifier requires the 1Password CLI (${Colors.url('https://developer.1password.com/docs/cli')}).`);
|
|
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
|
|
39
|
+
.name.replace(/^(\/|@)/, '')
|
|
40
|
+
.replace(/[/@]+/g, '-'),
|
|
41
|
+
integrationVersion: pkg.version
|
|
42
|
+
});
|
|
43
|
+
spinner.succeed('1Password loaded');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
const message = 'A 1Password service account token was not provided.';
|
|
47
|
+
spinner.fail(message);
|
|
48
|
+
throw new Error('A 1Password service account token was not provided.');
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return _client;
|
|
52
|
+
}
|
|
53
|
+
export async function configure(proposal = {}) {
|
|
54
|
+
for (const key in proposal) {
|
|
55
|
+
if (proposal[key] !== undefined) {
|
|
56
|
+
config[key] = proposal[key];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
export function options() {
|
|
61
|
+
return {
|
|
62
|
+
man: [
|
|
63
|
+
{
|
|
64
|
+
level: 1,
|
|
65
|
+
text: '1Password environment integration'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
text: `If 1Password secret references are stored in the environment, a ` +
|
|
69
|
+
`1Password service account token is required to access the secret ` +
|
|
70
|
+
`values.`
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
opt: {
|
|
74
|
+
opAccount: {
|
|
75
|
+
description: `1Password account to use (if signed into multiple); will use ` +
|
|
76
|
+
`environment variable ${Colors.varName('OP_ACCOUNT')} if present`,
|
|
77
|
+
hint: 'example.1password.com',
|
|
78
|
+
default: config.opAccount
|
|
79
|
+
},
|
|
80
|
+
opItem: {
|
|
81
|
+
description: `Name or ID of the 1Password API Credential item storing the ` +
|
|
82
|
+
`1Password service account token; will use environment variable ` +
|
|
83
|
+
`${Colors.varName('OP_ITEM')} if present. Requires the 1Password ` +
|
|
84
|
+
`CLI tool (${Colors.url('https://developer.1password.com/docs/cli')})`,
|
|
85
|
+
hint: '1Password unique identifier',
|
|
86
|
+
default: config.opItem
|
|
87
|
+
},
|
|
88
|
+
opToken: {
|
|
89
|
+
description: `1Password service account token; will use environment variable ` +
|
|
90
|
+
`${Colors.varName('OP_TOKEN')} if present`,
|
|
91
|
+
hint: 'token value',
|
|
92
|
+
secret: true,
|
|
93
|
+
default: config.opToken
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
export async function init({ values }) {
|
|
99
|
+
const { opAccount = process.env.OP_ACCOUNT, opItem = process.env.OP_ITEM, opToken = process.env.OP_TOKEN } = values;
|
|
100
|
+
await configure({ ...values, opAccount, opItem, opToken });
|
|
101
|
+
}
|
|
102
|
+
export async function get(ref) {
|
|
103
|
+
const client = await getClient();
|
|
104
|
+
return await client.secrets.resolve(ref);
|
|
105
|
+
}
|
|
106
|
+
function explodeSecretReference(secretReference) {
|
|
107
|
+
// eslint-disable-next-line prefer-const
|
|
108
|
+
let [vault, item, section, field] = secretReference
|
|
109
|
+
.replace(/^op:\/\//, '')
|
|
110
|
+
.split('/');
|
|
111
|
+
if (field === undefined) {
|
|
112
|
+
field = section;
|
|
113
|
+
section = '';
|
|
114
|
+
}
|
|
115
|
+
return { vault, item, section, field };
|
|
116
|
+
}
|
|
117
|
+
async function itemFrom(parts) {
|
|
118
|
+
const client = await getClient();
|
|
119
|
+
const vault = (await client.vaults.list())
|
|
120
|
+
.filter((vault) => vault.title == parts.vault)
|
|
121
|
+
.shift();
|
|
122
|
+
if (vault) {
|
|
123
|
+
const item = (await client.items.list(vault.id))
|
|
124
|
+
.filter((item) => item.title === parts.item || item.id === parts.item)
|
|
125
|
+
.shift();
|
|
126
|
+
if (item) {
|
|
127
|
+
return await client.items.get(vault.id, item.id);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/** Requires a service account with write privileges */
|
|
132
|
+
export async function set({ ref, value }) {
|
|
133
|
+
const client = await getClient();
|
|
134
|
+
const parts = explodeSecretReference(ref);
|
|
135
|
+
const item = await itemFrom(parts);
|
|
136
|
+
if (item) {
|
|
137
|
+
const updated = {
|
|
138
|
+
...item,
|
|
139
|
+
fields: item.fields.map((field) => {
|
|
140
|
+
const section = item.sections.find((s) => s.id === field.sectionId);
|
|
141
|
+
if (parts.field === field.title || parts.field === field.id) {
|
|
142
|
+
if ((!parts.section &&
|
|
143
|
+
(field.sectionId === '' || field.sectionId === 'add more')) ||
|
|
144
|
+
parts.section === section?.title ||
|
|
145
|
+
parts.section === section?.id) {
|
|
146
|
+
return { ...field, value };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return field;
|
|
150
|
+
})
|
|
151
|
+
};
|
|
152
|
+
await client.items.put(updated);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
throw new Error('1Password item could not be found');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import * as Plugin from '@qui-cli/plugin';
|
|
2
|
+
export type OPConfiguration = Plugin.Configuration & {
|
|
3
|
+
/**
|
|
4
|
+
* 1Password service account token; will use the environment variable OP_TOKEN
|
|
5
|
+
* if present
|
|
6
|
+
*/
|
|
7
|
+
opToken?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Name or ID of the 1Password API Credential item storing the 1Password
|
|
10
|
+
* service account token; will use environment variable OP_ITEM if present
|
|
11
|
+
*/
|
|
12
|
+
opItem?: string;
|
|
13
|
+
/**
|
|
14
|
+
* 1Password account to use (if signed into multiple); will use environment
|
|
15
|
+
* variable OP_ACCOUNT if present
|
|
16
|
+
*/
|
|
17
|
+
opAccount?: string;
|
|
18
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isSecretReference(value: unknown): unknown;
|
package/dist/Env.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as Plugin from '@qui-cli/plugin';
|
|
2
2
|
import dotenv from 'dotenv';
|
|
3
|
+
import { OPConfiguration } from './1Password/Configuration.js';
|
|
3
4
|
export type Configuration = Plugin.Configuration & {
|
|
4
5
|
/**
|
|
5
6
|
* Optional root for calculating relative paths to `.env` files. If undefined,
|
|
@@ -14,12 +15,13 @@ export type Configuration = Plugin.Configuration & {
|
|
|
14
15
|
load?: boolean;
|
|
15
16
|
/** Path to desired `.env` file relative to `root`. Defaults to `'.env'`; */
|
|
16
17
|
path?: string;
|
|
17
|
-
};
|
|
18
|
+
} & OPConfiguration;
|
|
18
19
|
export declare const name = "env";
|
|
19
|
-
export declare function configure(
|
|
20
|
-
export declare function
|
|
20
|
+
export declare function configure(proposal?: Configuration): Promise<void>;
|
|
21
|
+
export declare function options(): Promise<Plugin.Options>;
|
|
22
|
+
export declare function init(args: Plugin.ExpectedArguments<typeof options>): Promise<void>;
|
|
21
23
|
export type ParsedResult = dotenv.DotenvParseOutput;
|
|
22
|
-
export declare function parse(file?: string): Promise<ParsedResult>;
|
|
24
|
+
export declare function parse(file?: string | undefined): Promise<ParsedResult>;
|
|
23
25
|
export type GetOptions = {
|
|
24
26
|
key: string;
|
|
25
27
|
file?: string;
|
|
@@ -27,10 +29,15 @@ export type GetOptions = {
|
|
|
27
29
|
export declare function get({ key, file }: GetOptions): Promise<string | undefined>;
|
|
28
30
|
export declare function exists({ key, file }: GetOptions): Promise<boolean>;
|
|
29
31
|
export type SetOptions = {
|
|
32
|
+
/** Name of environment variable to set */
|
|
30
33
|
key: string;
|
|
34
|
+
/** Value to set */
|
|
31
35
|
value: string;
|
|
36
|
+
/** Path to the .env file */
|
|
32
37
|
file?: string;
|
|
38
|
+
/** A comment included in the .env file above the variable */
|
|
33
39
|
comment?: string;
|
|
40
|
+
/** Only set key=value if key is not already set */
|
|
34
41
|
ifNotExists?: boolean;
|
|
35
42
|
};
|
|
36
43
|
export declare function set({ key, value, file, comment, ifNotExists }: SetOptions): Promise<void>;
|
package/dist/Env.js
CHANGED
|
@@ -1,25 +1,59 @@
|
|
|
1
|
-
import * as Plugin from '@qui-cli/plugin';
|
|
2
1
|
import { Root } from '@qui-cli/root';
|
|
3
2
|
import dotenv from 'dotenv';
|
|
4
3
|
import fs from 'node:fs';
|
|
5
4
|
import path from 'node:path';
|
|
5
|
+
import { isSecretReference } from './1Password/isSecretReference.js';
|
|
6
|
+
let OP = undefined;
|
|
7
|
+
try {
|
|
8
|
+
await import('@1password/sdk');
|
|
9
|
+
OP = await import('./1Password/index.js');
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
11
|
+
}
|
|
12
|
+
catch (_) {
|
|
13
|
+
// @1password/sdk not present
|
|
14
|
+
}
|
|
6
15
|
export const name = 'env';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
const config = {
|
|
17
|
+
load: true,
|
|
18
|
+
path: '.env'
|
|
19
|
+
};
|
|
20
|
+
export async function configure(proposal = {}) {
|
|
21
|
+
for (const key in proposal) {
|
|
22
|
+
if (proposal[key] !== undefined) {
|
|
23
|
+
config[key] = proposal[key];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (OP?.configure) {
|
|
27
|
+
OP.configure({
|
|
28
|
+
opAccount: config.opAccount,
|
|
29
|
+
opItem: config.opItem,
|
|
30
|
+
opToken: config.opToken
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (config.load) {
|
|
15
34
|
await parse();
|
|
16
35
|
}
|
|
17
36
|
}
|
|
18
|
-
export function
|
|
37
|
+
export async function options() {
|
|
38
|
+
if (OP?.options) {
|
|
39
|
+
return await OP.options();
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export async function init(args) {
|
|
46
|
+
if (OP?.init) {
|
|
47
|
+
await OP.init(args);
|
|
48
|
+
}
|
|
49
|
+
await configure(args.values);
|
|
19
50
|
parse();
|
|
20
51
|
}
|
|
21
|
-
|
|
22
|
-
|
|
52
|
+
function toFilePath(file = '.env') {
|
|
53
|
+
return path.resolve(config.root || Root.path(), file);
|
|
54
|
+
}
|
|
55
|
+
export async function parse(file = config.path) {
|
|
56
|
+
const filePath = toFilePath(file);
|
|
23
57
|
if (fs.existsSync(filePath)) {
|
|
24
58
|
const env = dotenv.config({ path: filePath, quiet: true });
|
|
25
59
|
if (env.error) {
|
|
@@ -29,40 +63,60 @@ export async function parse(file = pathToEnv) {
|
|
|
29
63
|
}
|
|
30
64
|
return {};
|
|
31
65
|
}
|
|
32
|
-
export async function get({ key, file =
|
|
33
|
-
if (fs.existsSync(
|
|
34
|
-
|
|
66
|
+
export async function get({ key, file = config.path || '.env' }) {
|
|
67
|
+
if (fs.existsSync(toFilePath(file))) {
|
|
68
|
+
const env = await parse(file);
|
|
69
|
+
if (isSecretReference(env[key])) {
|
|
70
|
+
if (OP?.get) {
|
|
71
|
+
return OP?.get(env[key]);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
throw new Error(`Attempt to read environment variable ${key} that is a 1Password secret reference without @1password/sdk installed.`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return env[key];
|
|
35
78
|
}
|
|
36
79
|
return undefined;
|
|
37
80
|
}
|
|
38
|
-
export async function exists({ key, file =
|
|
39
|
-
if (fs.existsSync(
|
|
81
|
+
export async function exists({ key, file = config.path }) {
|
|
82
|
+
if (fs.existsSync(toFilePath(file))) {
|
|
40
83
|
return !!(await parse(file))[key];
|
|
41
84
|
}
|
|
42
85
|
return false;
|
|
43
86
|
}
|
|
44
|
-
export async function set({ key, value, file =
|
|
45
|
-
const filePath =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (pattern.test(env)) {
|
|
56
|
-
env = env.replace(pattern, `${key}=${value}`);
|
|
87
|
+
export async function set({ key, value, file = config.path, comment, ifNotExists = false }) {
|
|
88
|
+
const filePath = toFilePath(file);
|
|
89
|
+
const { [key]: prev } = dotenv.config({ path: filePath, quiet: true }).parsed || {};
|
|
90
|
+
if (ifNotExists === false || !prev) {
|
|
91
|
+
if (prev && isSecretReference(prev)) {
|
|
92
|
+
if (OP?.set) {
|
|
93
|
+
OP.set({ ref: prev, value });
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
throw new Error(`Attmept to update environment variable ${key} that is a 1Password secret reference without installing @1password/sdk`);
|
|
97
|
+
}
|
|
57
98
|
}
|
|
58
99
|
else {
|
|
59
|
-
env =
|
|
100
|
+
let env = '';
|
|
101
|
+
if (fs.existsSync(filePath)) {
|
|
102
|
+
env = fs.readFileSync(filePath).toString();
|
|
103
|
+
}
|
|
104
|
+
const pattern = new RegExp(`^${key}=.*$`, 'm');
|
|
105
|
+
if (/[\s=]/.test(value)) {
|
|
106
|
+
value = `"${value}"`;
|
|
107
|
+
}
|
|
108
|
+
if (pattern.test(env)) {
|
|
109
|
+
env = env.replace(pattern, `${key}=${value}`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
env = `${env.trim()}\n${comment ? `\n# ${comment}\n` : ''}${key}=${value}\n`;
|
|
113
|
+
}
|
|
114
|
+
fs.writeFileSync(filePath, env);
|
|
60
115
|
}
|
|
61
|
-
fs.writeFileSync(filePath, env);
|
|
62
116
|
}
|
|
63
117
|
}
|
|
64
|
-
export async function remove({ key, file =
|
|
65
|
-
const filePath = path.resolve(root || Root.path(), file);
|
|
118
|
+
export async function remove({ key, file = config.path || '.env', comment }) {
|
|
119
|
+
const filePath = path.resolve(config.root || Root.path(), file);
|
|
66
120
|
if (fs.existsSync(filePath)) {
|
|
67
121
|
const env = fs.readFileSync(filePath).toString();
|
|
68
122
|
const pattern = new RegExp(`${key}=.*\\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@qui-cli/env",
|
|
3
|
-
"version": "5.0
|
|
3
|
+
"version": "5.1.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": {
|
|
@@ -17,23 +17,36 @@
|
|
|
17
17
|
"main": "./dist/index.js",
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@1password/sdk": "^0.3.1",
|
|
21
20
|
"@battis/import-package-json": "^0.1.7",
|
|
22
|
-
"dotenv": "^17.2.3"
|
|
21
|
+
"dotenv": "^17.2.3",
|
|
22
|
+
"ora": "^9.0.0"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"@
|
|
25
|
+
"@1password/sdk": "^0.3.1",
|
|
26
|
+
"@tsconfig/node24": "^24.0.4",
|
|
27
|
+
"@types/node": "^24.10.9",
|
|
27
28
|
"commit-and-tag-version": "^12.6.1",
|
|
28
|
-
"del-cli": "^
|
|
29
|
+
"del-cli": "^7.0.0",
|
|
29
30
|
"npm-run-all": "^4.1.5",
|
|
30
31
|
"typescript": "^5.9.3",
|
|
31
|
-
"@qui-cli/
|
|
32
|
-
"@qui-cli/
|
|
32
|
+
"@qui-cli/log": "4.0.3",
|
|
33
|
+
"@qui-cli/plugin": "4.1.0",
|
|
34
|
+
"@qui-cli/colors": "3.2.3",
|
|
35
|
+
"@qui-cli/root": "3.1.0",
|
|
36
|
+
"@qui-cli/shell": "3.1.2"
|
|
33
37
|
},
|
|
34
38
|
"peerDependencies": {
|
|
39
|
+
"@1password/sdk": "0.3.x",
|
|
40
|
+
"@qui-cli/colors": ">=3",
|
|
41
|
+
"@qui-cli/log": ">=3",
|
|
35
42
|
"@qui-cli/plugin": ">=3",
|
|
36
|
-
"@qui-cli/root": ">=3"
|
|
43
|
+
"@qui-cli/root": ">=3",
|
|
44
|
+
"@qui-cli/shell": ">=3"
|
|
45
|
+
},
|
|
46
|
+
"peerDependenciesMeta": {
|
|
47
|
+
"@1password/sdk": {
|
|
48
|
+
"optional": true
|
|
49
|
+
}
|
|
37
50
|
},
|
|
38
51
|
"target": "node",
|
|
39
52
|
"scripts": {
|
|
@@ -41,6 +54,6 @@
|
|
|
41
54
|
"build": "run-s build:*",
|
|
42
55
|
"build:clean": "run-s clean",
|
|
43
56
|
"build:compile": "tsc",
|
|
44
|
-
"
|
|
57
|
+
"version": "commit-and-tag-version"
|
|
45
58
|
}
|
|
46
59
|
}
|