@tolgamorf/env2op-cli 0.2.0 → 0.2.1
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 +19 -3
- package/dist/cli.js +54 -32
- package/dist/index.d.ts +3 -1
- package/dist/index.js +48 -28
- package/dist/op2env-cli.js +56 -34
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# env2op
|
|
1
|
+
# env2op & op2env
|
|
2
2
|
|
|
3
3
|
Push `.env` files to 1Password and pull them back with two simple commands.
|
|
4
4
|
|
|
@@ -19,7 +19,9 @@ Or in a single command:
|
|
|
19
19
|
brew install tolgamorf/tap/env2op-cli
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
###
|
|
22
|
+
### Package Managers (All Operating Systems)
|
|
23
|
+
|
|
24
|
+
#### Global installation
|
|
23
25
|
|
|
24
26
|
```bash
|
|
25
27
|
# Using bun
|
|
@@ -28,10 +30,24 @@ bun add -g @tolgamorf/env2op-cli
|
|
|
28
30
|
# Using npm
|
|
29
31
|
npm install -g @tolgamorf/env2op-cli
|
|
30
32
|
|
|
31
|
-
#
|
|
33
|
+
# Using pnpm
|
|
34
|
+
pnpm add -g @tolgamorf/env2op-cli
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Running directly
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Using bun
|
|
32
41
|
bunx @tolgamorf/env2op-cli .env Personal "MyApp"
|
|
42
|
+
|
|
43
|
+
# Using npm
|
|
44
|
+
npx @tolgamorf/env2op-cli .env Personal "MyApp"
|
|
45
|
+
|
|
46
|
+
# Using pnpm
|
|
47
|
+
pnpm dlx @tolgamorf/env2op-cli .env Personal "MyApp"
|
|
33
48
|
```
|
|
34
49
|
|
|
50
|
+
|
|
35
51
|
## Prerequisites
|
|
36
52
|
|
|
37
53
|
- [1Password CLI](https://1password.com/downloads/command-line/) installed and signed in
|
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
|
|
|
22
22
|
var require_package = __commonJS((exports, module) => {
|
|
23
23
|
module.exports = {
|
|
24
24
|
name: "@tolgamorf/env2op-cli",
|
|
25
|
-
version: "0.2.
|
|
25
|
+
version: "0.2.1",
|
|
26
26
|
description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
|
|
27
27
|
type: "module",
|
|
28
28
|
main: "dist/index.js",
|
|
@@ -106,11 +106,10 @@ import pc3 from "picocolors";
|
|
|
106
106
|
|
|
107
107
|
// src/commands/convert.ts
|
|
108
108
|
import { basename, dirname, join } from "node:path";
|
|
109
|
-
import { setTimeout } from "node:timers/promises";
|
|
110
109
|
import * as p2 from "@clack/prompts";
|
|
111
110
|
|
|
112
111
|
// src/core/env-parser.ts
|
|
113
|
-
import {
|
|
112
|
+
import { readFile } from "node:fs/promises";
|
|
114
113
|
|
|
115
114
|
// src/utils/errors.ts
|
|
116
115
|
class Env2OpError extends Error {
|
|
@@ -133,6 +132,7 @@ var ErrorCodes = {
|
|
|
133
132
|
VAULT_CREATE_FAILED: "VAULT_CREATE_FAILED",
|
|
134
133
|
ITEM_EXISTS: "ITEM_EXISTS",
|
|
135
134
|
ITEM_CREATE_FAILED: "ITEM_CREATE_FAILED",
|
|
135
|
+
ITEM_EDIT_FAILED: "ITEM_EDIT_FAILED",
|
|
136
136
|
PARSE_ERROR: "PARSE_ERROR",
|
|
137
137
|
TEMPLATE_NOT_FOUND: "TEMPLATE_NOT_FOUND",
|
|
138
138
|
INJECT_FAILED: "INJECT_FAILED"
|
|
@@ -146,6 +146,7 @@ var errors = {
|
|
|
146
146
|
vaultCreateFailed: (message) => new Env2OpError(`Failed to create vault: ${message}`, ErrorCodes.VAULT_CREATE_FAILED),
|
|
147
147
|
itemExists: (title, vault) => new Env2OpError(`Item "${title}" already exists in vault "${vault}"`, ErrorCodes.ITEM_EXISTS, "Use default behavior (overwrites) or choose a different item name"),
|
|
148
148
|
itemCreateFailed: (message) => new Env2OpError(`Failed to create 1Password item: ${message}`, ErrorCodes.ITEM_CREATE_FAILED),
|
|
149
|
+
itemEditFailed: (message) => new Env2OpError(`Failed to edit 1Password item: ${message}`, ErrorCodes.ITEM_EDIT_FAILED),
|
|
149
150
|
parseError: (line, message) => new Env2OpError(`Parse error at line ${line}: ${message}`, ErrorCodes.PARSE_ERROR)
|
|
150
151
|
};
|
|
151
152
|
|
|
@@ -193,12 +194,20 @@ function parseValue(raw) {
|
|
|
193
194
|
const parts = trimmed.split(/\s+#/);
|
|
194
195
|
return (parts[0] ?? trimmed).trim();
|
|
195
196
|
}
|
|
196
|
-
function
|
|
197
|
-
if (
|
|
197
|
+
function stripBom(content) {
|
|
198
|
+
if (content.charCodeAt(0) === 65279) {
|
|
199
|
+
return content.slice(1);
|
|
200
|
+
}
|
|
201
|
+
return content;
|
|
202
|
+
}
|
|
203
|
+
async function parseEnvFile(filePath) {
|
|
204
|
+
let rawContent;
|
|
205
|
+
try {
|
|
206
|
+
rawContent = await readFile(filePath, "utf-8");
|
|
207
|
+
} catch {
|
|
198
208
|
throw errors.envFileNotFound(filePath);
|
|
199
209
|
}
|
|
200
|
-
const
|
|
201
|
-
const content = stripHeaders(rawContent);
|
|
210
|
+
const content = stripHeaders(stripBom(rawContent));
|
|
202
211
|
const rawLines = content.split(`
|
|
203
212
|
`);
|
|
204
213
|
const variables = [];
|
|
@@ -263,33 +272,34 @@ async function exec(command, args = [], options = {}) {
|
|
|
263
272
|
const proc = spawn(command, args, {
|
|
264
273
|
stdio: ["ignore", "pipe", "pipe"]
|
|
265
274
|
});
|
|
266
|
-
|
|
267
|
-
|
|
275
|
+
const stdoutChunks = [];
|
|
276
|
+
const stderrChunks = [];
|
|
268
277
|
proc.stdout?.on("data", (data) => {
|
|
269
|
-
const text = data.toString();
|
|
270
|
-
|
|
278
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
279
|
+
stdoutChunks.push(text);
|
|
271
280
|
if (verbose) {
|
|
272
281
|
process.stdout.write(text);
|
|
273
282
|
}
|
|
274
283
|
});
|
|
275
284
|
proc.stderr?.on("data", (data) => {
|
|
276
|
-
const text = data.toString();
|
|
277
|
-
|
|
285
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
286
|
+
stderrChunks.push(text);
|
|
278
287
|
if (verbose) {
|
|
279
288
|
process.stderr.write(text);
|
|
280
289
|
}
|
|
281
290
|
});
|
|
282
291
|
proc.on("close", (code) => {
|
|
283
292
|
resolve({
|
|
284
|
-
stdout,
|
|
285
|
-
stderr,
|
|
286
|
-
exitCode: code ??
|
|
293
|
+
stdout: stdoutChunks.join(""),
|
|
294
|
+
stderr: stderrChunks.join(""),
|
|
295
|
+
exitCode: code ?? 1
|
|
287
296
|
});
|
|
288
297
|
});
|
|
289
|
-
proc.on("error", () => {
|
|
298
|
+
proc.on("error", (err) => {
|
|
299
|
+
stderrChunks.push(err.message);
|
|
290
300
|
resolve({
|
|
291
|
-
stdout,
|
|
292
|
-
stderr,
|
|
301
|
+
stdout: stdoutChunks.join(""),
|
|
302
|
+
stderr: stderrChunks.join(""),
|
|
293
303
|
exitCode: 1
|
|
294
304
|
});
|
|
295
305
|
});
|
|
@@ -305,27 +315,36 @@ async function execWithStdin(command, args = [], options) {
|
|
|
305
315
|
const proc = spawn(command, args, {
|
|
306
316
|
stdio: ["pipe", "pipe", "pipe"]
|
|
307
317
|
});
|
|
308
|
-
|
|
309
|
-
|
|
318
|
+
const stdoutChunks = [];
|
|
319
|
+
const stderrChunks = [];
|
|
310
320
|
proc.stdin?.write(stdinContent);
|
|
311
321
|
proc.stdin?.end();
|
|
312
322
|
proc.stdout?.on("data", (data) => {
|
|
313
|
-
const text = data.toString();
|
|
314
|
-
|
|
323
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
324
|
+
stdoutChunks.push(text);
|
|
315
325
|
if (verbose)
|
|
316
326
|
process.stdout.write(text);
|
|
317
327
|
});
|
|
318
328
|
proc.stderr?.on("data", (data) => {
|
|
319
|
-
const text = data.toString();
|
|
320
|
-
|
|
329
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
330
|
+
stderrChunks.push(text);
|
|
321
331
|
if (verbose)
|
|
322
332
|
process.stderr.write(text);
|
|
323
333
|
});
|
|
324
334
|
proc.on("close", (code) => {
|
|
325
|
-
resolve({
|
|
335
|
+
resolve({
|
|
336
|
+
stdout: stdoutChunks.join(""),
|
|
337
|
+
stderr: stderrChunks.join(""),
|
|
338
|
+
exitCode: code ?? 1
|
|
339
|
+
});
|
|
326
340
|
});
|
|
327
|
-
proc.on("error", () => {
|
|
328
|
-
|
|
341
|
+
proc.on("error", (err) => {
|
|
342
|
+
stderrChunks.push(err.message);
|
|
343
|
+
resolve({
|
|
344
|
+
stdout: stdoutChunks.join(""),
|
|
345
|
+
stderr: stderrChunks.join(""),
|
|
346
|
+
exitCode: 1
|
|
347
|
+
});
|
|
329
348
|
});
|
|
330
349
|
});
|
|
331
350
|
}
|
|
@@ -445,7 +464,7 @@ async function editSecureNote(options) {
|
|
|
445
464
|
};
|
|
446
465
|
} catch (error) {
|
|
447
466
|
const message = error instanceof Error ? error.message : String(error);
|
|
448
|
-
throw errors.
|
|
467
|
+
throw errors.itemEditFailed(message);
|
|
449
468
|
}
|
|
450
469
|
}
|
|
451
470
|
|
|
@@ -606,18 +625,21 @@ ${pc2.bold(pc2.underline(title))}`);
|
|
|
606
625
|
}
|
|
607
626
|
};
|
|
608
627
|
|
|
609
|
-
// src/
|
|
628
|
+
// src/utils/timing.ts
|
|
629
|
+
import { setTimeout } from "node:timers/promises";
|
|
610
630
|
var MIN_SPINNER_TIME = 500;
|
|
611
631
|
async function withMinTime(promise, minTime = MIN_SPINNER_TIME) {
|
|
612
632
|
const [result] = await Promise.all([promise, setTimeout(minTime)]);
|
|
613
633
|
return result;
|
|
614
634
|
}
|
|
635
|
+
|
|
636
|
+
// src/commands/convert.ts
|
|
615
637
|
async function runConvert(options) {
|
|
616
638
|
const { envFile, vault, itemName, output, dryRun, secret, force, verbose } = options;
|
|
617
639
|
const pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
|
|
618
640
|
logger.intro("env2op", pkg2.version, dryRun);
|
|
619
641
|
try {
|
|
620
|
-
const parseResult = parseEnvFile(envFile);
|
|
642
|
+
const parseResult = await parseEnvFile(envFile);
|
|
621
643
|
validateParseResult(parseResult, envFile);
|
|
622
644
|
const { variables, lines } = parseResult;
|
|
623
645
|
const varCount = variables.length;
|
|
@@ -728,7 +750,7 @@ async function runConvert(options) {
|
|
|
728
750
|
}
|
|
729
751
|
pushSpinner.stop(existingItemId ? `Updated "${itemResult.title}" in vault "${itemResult.vault}"` : `Created "${itemResult.title}" in vault "${itemResult.vault}"`);
|
|
730
752
|
} catch (error) {
|
|
731
|
-
pushSpinner.stop(
|
|
753
|
+
pushSpinner.stop();
|
|
732
754
|
throw error;
|
|
733
755
|
}
|
|
734
756
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -113,7 +113,7 @@ interface TemplateOptions {
|
|
|
113
113
|
* @returns ParseResult containing variables and any errors
|
|
114
114
|
* @throws Env2OpError if file not found
|
|
115
115
|
*/
|
|
116
|
-
declare function parseEnvFile(filePath: string): ParseResult
|
|
116
|
+
declare function parseEnvFile(filePath: string): Promise<ParseResult>;
|
|
117
117
|
/**
|
|
118
118
|
* Validate that the parsed result has variables
|
|
119
119
|
*
|
|
@@ -198,6 +198,7 @@ declare const ErrorCodes: {
|
|
|
198
198
|
readonly VAULT_CREATE_FAILED: "VAULT_CREATE_FAILED";
|
|
199
199
|
readonly ITEM_EXISTS: "ITEM_EXISTS";
|
|
200
200
|
readonly ITEM_CREATE_FAILED: "ITEM_CREATE_FAILED";
|
|
201
|
+
readonly ITEM_EDIT_FAILED: "ITEM_EDIT_FAILED";
|
|
201
202
|
readonly PARSE_ERROR: "PARSE_ERROR";
|
|
202
203
|
readonly TEMPLATE_NOT_FOUND: "TEMPLATE_NOT_FOUND";
|
|
203
204
|
readonly INJECT_FAILED: "INJECT_FAILED";
|
|
@@ -215,6 +216,7 @@ declare const errors: {
|
|
|
215
216
|
vaultCreateFailed: (message: string) => Env2OpError;
|
|
216
217
|
itemExists: (title: string, vault: string) => Env2OpError;
|
|
217
218
|
itemCreateFailed: (message: string) => Env2OpError;
|
|
219
|
+
itemEditFailed: (message: string) => Env2OpError;
|
|
218
220
|
parseError: (line: number, message: string) => Env2OpError;
|
|
219
221
|
};
|
|
220
222
|
export { writeTemplate, vaultExists, validateParseResult, signIn, parseEnvFile, itemExists2 as itemExists, generateUsageInstructions, generateTemplateContent, errors, editSecureNote, createVault, createSecureNote, checkSignedIn, checkOpCli, TemplateOptions, ParseResult, ErrorCodes, EnvVariable, EnvLine, Env2OpError, CreateItemResult, CreateItemOptions, ConvertOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/core/env-parser.ts
|
|
2
|
-
import {
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
3
|
|
|
4
4
|
// src/utils/errors.ts
|
|
5
5
|
class Env2OpError extends Error {
|
|
@@ -22,6 +22,7 @@ var ErrorCodes = {
|
|
|
22
22
|
VAULT_CREATE_FAILED: "VAULT_CREATE_FAILED",
|
|
23
23
|
ITEM_EXISTS: "ITEM_EXISTS",
|
|
24
24
|
ITEM_CREATE_FAILED: "ITEM_CREATE_FAILED",
|
|
25
|
+
ITEM_EDIT_FAILED: "ITEM_EDIT_FAILED",
|
|
25
26
|
PARSE_ERROR: "PARSE_ERROR",
|
|
26
27
|
TEMPLATE_NOT_FOUND: "TEMPLATE_NOT_FOUND",
|
|
27
28
|
INJECT_FAILED: "INJECT_FAILED"
|
|
@@ -35,6 +36,7 @@ var errors = {
|
|
|
35
36
|
vaultCreateFailed: (message) => new Env2OpError(`Failed to create vault: ${message}`, ErrorCodes.VAULT_CREATE_FAILED),
|
|
36
37
|
itemExists: (title, vault) => new Env2OpError(`Item "${title}" already exists in vault "${vault}"`, ErrorCodes.ITEM_EXISTS, "Use default behavior (overwrites) or choose a different item name"),
|
|
37
38
|
itemCreateFailed: (message) => new Env2OpError(`Failed to create 1Password item: ${message}`, ErrorCodes.ITEM_CREATE_FAILED),
|
|
39
|
+
itemEditFailed: (message) => new Env2OpError(`Failed to edit 1Password item: ${message}`, ErrorCodes.ITEM_EDIT_FAILED),
|
|
38
40
|
parseError: (line, message) => new Env2OpError(`Parse error at line ${line}: ${message}`, ErrorCodes.PARSE_ERROR)
|
|
39
41
|
};
|
|
40
42
|
|
|
@@ -82,12 +84,20 @@ function parseValue(raw) {
|
|
|
82
84
|
const parts = trimmed.split(/\s+#/);
|
|
83
85
|
return (parts[0] ?? trimmed).trim();
|
|
84
86
|
}
|
|
85
|
-
function
|
|
86
|
-
if (
|
|
87
|
+
function stripBom(content) {
|
|
88
|
+
if (content.charCodeAt(0) === 65279) {
|
|
89
|
+
return content.slice(1);
|
|
90
|
+
}
|
|
91
|
+
return content;
|
|
92
|
+
}
|
|
93
|
+
async function parseEnvFile(filePath) {
|
|
94
|
+
let rawContent;
|
|
95
|
+
try {
|
|
96
|
+
rawContent = await readFile(filePath, "utf-8");
|
|
97
|
+
} catch {
|
|
87
98
|
throw errors.envFileNotFound(filePath);
|
|
88
99
|
}
|
|
89
|
-
const
|
|
90
|
-
const content = stripHeaders(rawContent);
|
|
100
|
+
const content = stripHeaders(stripBom(rawContent));
|
|
91
101
|
const rawLines = content.split(`
|
|
92
102
|
`);
|
|
93
103
|
const variables = [];
|
|
@@ -151,33 +161,34 @@ async function exec(command, args = [], options = {}) {
|
|
|
151
161
|
const proc = spawn(command, args, {
|
|
152
162
|
stdio: ["ignore", "pipe", "pipe"]
|
|
153
163
|
});
|
|
154
|
-
|
|
155
|
-
|
|
164
|
+
const stdoutChunks = [];
|
|
165
|
+
const stderrChunks = [];
|
|
156
166
|
proc.stdout?.on("data", (data) => {
|
|
157
|
-
const text = data.toString();
|
|
158
|
-
|
|
167
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
168
|
+
stdoutChunks.push(text);
|
|
159
169
|
if (verbose) {
|
|
160
170
|
process.stdout.write(text);
|
|
161
171
|
}
|
|
162
172
|
});
|
|
163
173
|
proc.stderr?.on("data", (data) => {
|
|
164
|
-
const text = data.toString();
|
|
165
|
-
|
|
174
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
175
|
+
stderrChunks.push(text);
|
|
166
176
|
if (verbose) {
|
|
167
177
|
process.stderr.write(text);
|
|
168
178
|
}
|
|
169
179
|
});
|
|
170
180
|
proc.on("close", (code) => {
|
|
171
181
|
resolve({
|
|
172
|
-
stdout,
|
|
173
|
-
stderr,
|
|
174
|
-
exitCode: code ??
|
|
182
|
+
stdout: stdoutChunks.join(""),
|
|
183
|
+
stderr: stderrChunks.join(""),
|
|
184
|
+
exitCode: code ?? 1
|
|
175
185
|
});
|
|
176
186
|
});
|
|
177
|
-
proc.on("error", () => {
|
|
187
|
+
proc.on("error", (err) => {
|
|
188
|
+
stderrChunks.push(err.message);
|
|
178
189
|
resolve({
|
|
179
|
-
stdout,
|
|
180
|
-
stderr,
|
|
190
|
+
stdout: stdoutChunks.join(""),
|
|
191
|
+
stderr: stderrChunks.join(""),
|
|
181
192
|
exitCode: 1
|
|
182
193
|
});
|
|
183
194
|
});
|
|
@@ -193,27 +204,36 @@ async function execWithStdin(command, args = [], options) {
|
|
|
193
204
|
const proc = spawn(command, args, {
|
|
194
205
|
stdio: ["pipe", "pipe", "pipe"]
|
|
195
206
|
});
|
|
196
|
-
|
|
197
|
-
|
|
207
|
+
const stdoutChunks = [];
|
|
208
|
+
const stderrChunks = [];
|
|
198
209
|
proc.stdin?.write(stdinContent);
|
|
199
210
|
proc.stdin?.end();
|
|
200
211
|
proc.stdout?.on("data", (data) => {
|
|
201
|
-
const text = data.toString();
|
|
202
|
-
|
|
212
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
213
|
+
stdoutChunks.push(text);
|
|
203
214
|
if (verbose)
|
|
204
215
|
process.stdout.write(text);
|
|
205
216
|
});
|
|
206
217
|
proc.stderr?.on("data", (data) => {
|
|
207
|
-
const text = data.toString();
|
|
208
|
-
|
|
218
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
219
|
+
stderrChunks.push(text);
|
|
209
220
|
if (verbose)
|
|
210
221
|
process.stderr.write(text);
|
|
211
222
|
});
|
|
212
223
|
proc.on("close", (code) => {
|
|
213
|
-
resolve({
|
|
224
|
+
resolve({
|
|
225
|
+
stdout: stdoutChunks.join(""),
|
|
226
|
+
stderr: stderrChunks.join(""),
|
|
227
|
+
exitCode: code ?? 1
|
|
228
|
+
});
|
|
214
229
|
});
|
|
215
|
-
proc.on("error", () => {
|
|
216
|
-
|
|
230
|
+
proc.on("error", (err) => {
|
|
231
|
+
stderrChunks.push(err.message);
|
|
232
|
+
resolve({
|
|
233
|
+
stdout: stdoutChunks.join(""),
|
|
234
|
+
stderr: stderrChunks.join(""),
|
|
235
|
+
exitCode: 1
|
|
236
|
+
});
|
|
217
237
|
});
|
|
218
238
|
});
|
|
219
239
|
}
|
|
@@ -333,7 +353,7 @@ async function editSecureNote(options) {
|
|
|
333
353
|
};
|
|
334
354
|
} catch (error) {
|
|
335
355
|
const message = error instanceof Error ? error.message : String(error);
|
|
336
|
-
throw errors.
|
|
356
|
+
throw errors.itemEditFailed(message);
|
|
337
357
|
}
|
|
338
358
|
}
|
|
339
359
|
// src/core/template-generator.ts
|
|
@@ -341,7 +361,7 @@ import { writeFileSync } from "node:fs";
|
|
|
341
361
|
// package.json
|
|
342
362
|
var package_default = {
|
|
343
363
|
name: "@tolgamorf/env2op-cli",
|
|
344
|
-
version: "0.2.
|
|
364
|
+
version: "0.2.1",
|
|
345
365
|
description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
|
|
346
366
|
type: "module",
|
|
347
367
|
main: "dist/index.js",
|
package/dist/op2env-cli.js
CHANGED
|
@@ -22,7 +22,7 @@ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports,
|
|
|
22
22
|
var require_package = __commonJS((exports, module) => {
|
|
23
23
|
module.exports = {
|
|
24
24
|
name: "@tolgamorf/env2op-cli",
|
|
25
|
-
version: "0.2.
|
|
25
|
+
version: "0.2.1",
|
|
26
26
|
description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
|
|
27
27
|
type: "module",
|
|
28
28
|
main: "dist/index.js",
|
|
@@ -105,13 +105,12 @@ var require_package = __commonJS((exports, module) => {
|
|
|
105
105
|
import pc3 from "picocolors";
|
|
106
106
|
|
|
107
107
|
// src/commands/inject.ts
|
|
108
|
-
import { existsSync
|
|
108
|
+
import { existsSync, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
109
109
|
import { basename } from "node:path";
|
|
110
|
-
import { setTimeout } from "node:timers/promises";
|
|
111
110
|
import * as p2 from "@clack/prompts";
|
|
112
111
|
|
|
113
112
|
// src/core/env-parser.ts
|
|
114
|
-
import {
|
|
113
|
+
import { readFile } from "node:fs/promises";
|
|
115
114
|
|
|
116
115
|
// src/utils/errors.ts
|
|
117
116
|
class Env2OpError extends Error {
|
|
@@ -134,6 +133,7 @@ var ErrorCodes = {
|
|
|
134
133
|
VAULT_CREATE_FAILED: "VAULT_CREATE_FAILED",
|
|
135
134
|
ITEM_EXISTS: "ITEM_EXISTS",
|
|
136
135
|
ITEM_CREATE_FAILED: "ITEM_CREATE_FAILED",
|
|
136
|
+
ITEM_EDIT_FAILED: "ITEM_EDIT_FAILED",
|
|
137
137
|
PARSE_ERROR: "PARSE_ERROR",
|
|
138
138
|
TEMPLATE_NOT_FOUND: "TEMPLATE_NOT_FOUND",
|
|
139
139
|
INJECT_FAILED: "INJECT_FAILED"
|
|
@@ -147,6 +147,7 @@ var errors = {
|
|
|
147
147
|
vaultCreateFailed: (message) => new Env2OpError(`Failed to create vault: ${message}`, ErrorCodes.VAULT_CREATE_FAILED),
|
|
148
148
|
itemExists: (title, vault) => new Env2OpError(`Item "${title}" already exists in vault "${vault}"`, ErrorCodes.ITEM_EXISTS, "Use default behavior (overwrites) or choose a different item name"),
|
|
149
149
|
itemCreateFailed: (message) => new Env2OpError(`Failed to create 1Password item: ${message}`, ErrorCodes.ITEM_CREATE_FAILED),
|
|
150
|
+
itemEditFailed: (message) => new Env2OpError(`Failed to edit 1Password item: ${message}`, ErrorCodes.ITEM_EDIT_FAILED),
|
|
150
151
|
parseError: (line, message) => new Env2OpError(`Parse error at line ${line}: ${message}`, ErrorCodes.PARSE_ERROR)
|
|
151
152
|
};
|
|
152
153
|
|
|
@@ -194,12 +195,20 @@ function parseValue(raw) {
|
|
|
194
195
|
const parts = trimmed.split(/\s+#/);
|
|
195
196
|
return (parts[0] ?? trimmed).trim();
|
|
196
197
|
}
|
|
197
|
-
function
|
|
198
|
-
if (
|
|
198
|
+
function stripBom(content) {
|
|
199
|
+
if (content.charCodeAt(0) === 65279) {
|
|
200
|
+
return content.slice(1);
|
|
201
|
+
}
|
|
202
|
+
return content;
|
|
203
|
+
}
|
|
204
|
+
async function parseEnvFile(filePath) {
|
|
205
|
+
let rawContent;
|
|
206
|
+
try {
|
|
207
|
+
rawContent = await readFile(filePath, "utf-8");
|
|
208
|
+
} catch {
|
|
199
209
|
throw errors.envFileNotFound(filePath);
|
|
200
210
|
}
|
|
201
|
-
const
|
|
202
|
-
const content = stripHeaders(rawContent);
|
|
211
|
+
const content = stripHeaders(stripBom(rawContent));
|
|
203
212
|
const rawLines = content.split(`
|
|
204
213
|
`);
|
|
205
214
|
const variables = [];
|
|
@@ -264,33 +273,34 @@ async function exec(command, args = [], options = {}) {
|
|
|
264
273
|
const proc = spawn(command, args, {
|
|
265
274
|
stdio: ["ignore", "pipe", "pipe"]
|
|
266
275
|
});
|
|
267
|
-
|
|
268
|
-
|
|
276
|
+
const stdoutChunks = [];
|
|
277
|
+
const stderrChunks = [];
|
|
269
278
|
proc.stdout?.on("data", (data) => {
|
|
270
|
-
const text = data.toString();
|
|
271
|
-
|
|
279
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
280
|
+
stdoutChunks.push(text);
|
|
272
281
|
if (verbose) {
|
|
273
282
|
process.stdout.write(text);
|
|
274
283
|
}
|
|
275
284
|
});
|
|
276
285
|
proc.stderr?.on("data", (data) => {
|
|
277
|
-
const text = data.toString();
|
|
278
|
-
|
|
286
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
287
|
+
stderrChunks.push(text);
|
|
279
288
|
if (verbose) {
|
|
280
289
|
process.stderr.write(text);
|
|
281
290
|
}
|
|
282
291
|
});
|
|
283
292
|
proc.on("close", (code) => {
|
|
284
293
|
resolve({
|
|
285
|
-
stdout,
|
|
286
|
-
stderr,
|
|
287
|
-
exitCode: code ??
|
|
294
|
+
stdout: stdoutChunks.join(""),
|
|
295
|
+
stderr: stderrChunks.join(""),
|
|
296
|
+
exitCode: code ?? 1
|
|
288
297
|
});
|
|
289
298
|
});
|
|
290
|
-
proc.on("error", () => {
|
|
299
|
+
proc.on("error", (err) => {
|
|
300
|
+
stderrChunks.push(err.message);
|
|
291
301
|
resolve({
|
|
292
|
-
stdout,
|
|
293
|
-
stderr,
|
|
302
|
+
stdout: stdoutChunks.join(""),
|
|
303
|
+
stderr: stderrChunks.join(""),
|
|
294
304
|
exitCode: 1
|
|
295
305
|
});
|
|
296
306
|
});
|
|
@@ -306,27 +316,36 @@ async function execWithStdin(command, args = [], options) {
|
|
|
306
316
|
const proc = spawn(command, args, {
|
|
307
317
|
stdio: ["pipe", "pipe", "pipe"]
|
|
308
318
|
});
|
|
309
|
-
|
|
310
|
-
|
|
319
|
+
const stdoutChunks = [];
|
|
320
|
+
const stderrChunks = [];
|
|
311
321
|
proc.stdin?.write(stdinContent);
|
|
312
322
|
proc.stdin?.end();
|
|
313
323
|
proc.stdout?.on("data", (data) => {
|
|
314
|
-
const text = data.toString();
|
|
315
|
-
|
|
324
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
325
|
+
stdoutChunks.push(text);
|
|
316
326
|
if (verbose)
|
|
317
327
|
process.stdout.write(text);
|
|
318
328
|
});
|
|
319
329
|
proc.stderr?.on("data", (data) => {
|
|
320
|
-
const text = data.toString();
|
|
321
|
-
|
|
330
|
+
const text = Buffer.isBuffer(data) ? data.toString() : String(data);
|
|
331
|
+
stderrChunks.push(text);
|
|
322
332
|
if (verbose)
|
|
323
333
|
process.stderr.write(text);
|
|
324
334
|
});
|
|
325
335
|
proc.on("close", (code) => {
|
|
326
|
-
resolve({
|
|
336
|
+
resolve({
|
|
337
|
+
stdout: stdoutChunks.join(""),
|
|
338
|
+
stderr: stderrChunks.join(""),
|
|
339
|
+
exitCode: code ?? 1
|
|
340
|
+
});
|
|
327
341
|
});
|
|
328
|
-
proc.on("error", () => {
|
|
329
|
-
|
|
342
|
+
proc.on("error", (err) => {
|
|
343
|
+
stderrChunks.push(err.message);
|
|
344
|
+
resolve({
|
|
345
|
+
stdout: stdoutChunks.join(""),
|
|
346
|
+
stderr: stderrChunks.join(""),
|
|
347
|
+
exitCode: 1
|
|
348
|
+
});
|
|
330
349
|
});
|
|
331
350
|
});
|
|
332
351
|
}
|
|
@@ -446,7 +465,7 @@ async function editSecureNote(options) {
|
|
|
446
465
|
};
|
|
447
466
|
} catch (error) {
|
|
448
467
|
const message = error instanceof Error ? error.message : String(error);
|
|
449
|
-
throw errors.
|
|
468
|
+
throw errors.itemEditFailed(message);
|
|
450
469
|
}
|
|
451
470
|
}
|
|
452
471
|
|
|
@@ -607,12 +626,15 @@ ${pc2.bold(pc2.underline(title))}`);
|
|
|
607
626
|
}
|
|
608
627
|
};
|
|
609
628
|
|
|
610
|
-
// src/
|
|
629
|
+
// src/utils/timing.ts
|
|
630
|
+
import { setTimeout } from "node:timers/promises";
|
|
611
631
|
var MIN_SPINNER_TIME = 500;
|
|
612
632
|
async function withMinTime(promise, minTime = MIN_SPINNER_TIME) {
|
|
613
633
|
const [result] = await Promise.all([promise, setTimeout(minTime)]);
|
|
614
634
|
return result;
|
|
615
635
|
}
|
|
636
|
+
|
|
637
|
+
// src/commands/inject.ts
|
|
616
638
|
function deriveOutputPath(templatePath) {
|
|
617
639
|
if (templatePath.endsWith(".tpl")) {
|
|
618
640
|
return templatePath.slice(0, -4);
|
|
@@ -625,7 +647,7 @@ async function runInject(options) {
|
|
|
625
647
|
const pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
|
|
626
648
|
logger.intro("op2env", pkg2.version, dryRun);
|
|
627
649
|
try {
|
|
628
|
-
if (!
|
|
650
|
+
if (!existsSync(templateFile)) {
|
|
629
651
|
throw new Env2OpError(`Template file not found: ${templateFile}`, "TEMPLATE_NOT_FOUND", "Ensure the file exists and the path is correct");
|
|
630
652
|
}
|
|
631
653
|
logger.success(`Found template: ${basename(templateFile)}`);
|
|
@@ -653,7 +675,7 @@ async function runInject(options) {
|
|
|
653
675
|
}
|
|
654
676
|
authSpinner.stop("1Password CLI ready");
|
|
655
677
|
}
|
|
656
|
-
const outputExists =
|
|
678
|
+
const outputExists = existsSync(outputPath);
|
|
657
679
|
if (dryRun) {
|
|
658
680
|
if (outputExists) {
|
|
659
681
|
logger.warn(`Would overwrite: ${outputPath}`);
|
|
@@ -679,7 +701,7 @@ async function runInject(options) {
|
|
|
679
701
|
if (result.exitCode !== 0) {
|
|
680
702
|
throw new Error(result.stderr);
|
|
681
703
|
}
|
|
682
|
-
const rawContent =
|
|
704
|
+
const rawContent = readFileSync(outputPath, "utf-8");
|
|
683
705
|
const envContent = stripHeaders(rawContent);
|
|
684
706
|
const header = generateEnvHeader(basename(outputPath)).join(`
|
|
685
707
|
`);
|