@tolgamorf/env2op-cli 0.1.5 → 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/dist/cli.js CHANGED
@@ -1,187 +1,160 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from "node:module";
2
3
  var __create = Object.create;
4
+ var __getProtoOf = Object.getPrototypeOf;
3
5
  var __defProp = Object.defineProperty;
4
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __commonJS = (cb, mod) => function __require() {
9
- return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
8
+ var __toESM = (mod, isNodeMode, target) => {
9
+ target = mod != null ? __create(__getProtoOf(mod)) : {};
10
+ const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
+ for (let key of __getOwnPropNames(mod))
12
+ if (!__hasOwnProp.call(to, key))
13
+ __defProp(to, key, {
14
+ get: () => mod[key],
15
+ enumerable: true
16
+ });
17
17
  return to;
18
18
  };
19
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
20
- // If the importer is in node compatibility mode or this is not an ESM
21
- // file that has been converted to a CommonJS file using a Babel-
22
- // compatible transform (i.e. "__esModule" has not been set), then set
23
- // "default" to the CommonJS "module.exports" for node compatibility.
24
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
25
- mod
26
- ));
19
+ var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
27
20
 
28
21
  // package.json
29
- var require_package = __commonJS({
30
- "package.json"(exports, module) {
31
- module.exports = {
32
- name: "@tolgamorf/env2op-cli",
33
- version: "0.1.5",
34
- description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
35
- type: "module",
36
- main: "dist/index.js",
37
- module: "dist/index.js",
38
- types: "dist/index.d.ts",
39
- exports: {
40
- ".": {
41
- import: "./dist/index.js",
42
- types: "./dist/index.d.ts"
43
- }
44
- },
45
- bin: {
46
- env2op: "dist/cli.js",
47
- op2env: "dist/op2env-cli.js"
48
- },
49
- files: [
50
- "dist",
51
- "LICENSE",
52
- "README.md"
53
- ],
54
- scripts: {
55
- dev: "bun run src/cli.ts",
56
- build: "tsup",
57
- test: "bun test",
58
- "test:watch": "bun test --watch",
59
- "test:coverage": "bun test --coverage",
60
- typecheck: "tsc --noEmit",
61
- lint: "bunx biome check .",
62
- "lint:fix": "bunx biome check . --write",
63
- format: "bunx biome format --write .",
64
- "format:check": "bunx biome format .",
65
- prepublishOnly: "bun run build",
66
- release: "bun run scripts/release.ts"
67
- },
68
- keywords: [
69
- "env",
70
- "1password",
71
- "op",
72
- "cli",
73
- "secrets",
74
- "environment-variables",
75
- "dotenv",
76
- "secure-notes",
77
- "bun",
78
- "op-inject",
79
- "op-run",
80
- "template"
81
- ],
82
- author: {
83
- name: "Tolga O.",
84
- url: "https://github.com/tolgamorf"
85
- },
86
- license: "MIT",
87
- repository: {
88
- type: "git",
89
- url: "git+https://github.com/tolgamorf/env2op-cli.git"
90
- },
91
- bugs: {
92
- url: "https://github.com/tolgamorf/env2op-cli/issues"
93
- },
94
- homepage: "https://github.com/tolgamorf/env2op-cli#readme",
95
- engines: {
96
- node: ">=18.0.0"
97
- },
98
- dependencies: {
99
- "@clack/prompts": "^0.11.0",
100
- picocolors: "^1.1.1"
101
- },
102
- devDependencies: {
103
- "@biomejs/biome": "^2.3.10",
104
- "@tsconfig/bun": "^1.0.10",
105
- "@types/bun": "^1.3.5",
106
- tsup: "^8.5.1",
107
- typescript: "^5.9.3"
22
+ var require_package = __commonJS((exports, module) => {
23
+ module.exports = {
24
+ name: "@tolgamorf/env2op-cli",
25
+ version: "0.2.1",
26
+ description: "Convert .env files to 1Password Secure Notes and generate templates for op inject/run",
27
+ type: "module",
28
+ main: "dist/index.js",
29
+ module: "dist/index.js",
30
+ types: "dist/index.d.ts",
31
+ exports: {
32
+ ".": {
33
+ import: "./dist/index.js",
34
+ types: "./dist/index.d.ts"
108
35
  }
109
- };
110
- }
36
+ },
37
+ bin: {
38
+ env2op: "dist/cli.js",
39
+ op2env: "dist/op2env-cli.js"
40
+ },
41
+ files: [
42
+ "dist",
43
+ "LICENSE",
44
+ "README.md"
45
+ ],
46
+ scripts: {
47
+ dev: "bun run src/cli.ts",
48
+ build: "bunup",
49
+ test: "bun test",
50
+ "test:watch": "bun test --watch",
51
+ "test:coverage": "bun test --coverage",
52
+ typecheck: "tsc --noEmit",
53
+ lint: "bunx biome check .",
54
+ "lint:fix": "bunx biome check . --write",
55
+ format: "bunx biome format --write .",
56
+ "format:check": "bunx biome format .",
57
+ prepublishOnly: "bun run build",
58
+ release: "bun run scripts/release.ts"
59
+ },
60
+ keywords: [
61
+ "env",
62
+ "1password",
63
+ "op",
64
+ "cli",
65
+ "secrets",
66
+ "environment-variables",
67
+ "dotenv",
68
+ "secure-notes",
69
+ "bun",
70
+ "op-inject",
71
+ "op-run",
72
+ "template"
73
+ ],
74
+ author: {
75
+ name: "Tolga O.",
76
+ url: "https://github.com/tolgamorf"
77
+ },
78
+ license: "MIT",
79
+ repository: {
80
+ type: "git",
81
+ url: "git+https://github.com/tolgamorf/env2op-cli.git"
82
+ },
83
+ bugs: {
84
+ url: "https://github.com/tolgamorf/env2op-cli/issues"
85
+ },
86
+ homepage: "https://github.com/tolgamorf/env2op-cli#readme",
87
+ engines: {
88
+ node: ">=18.0.0"
89
+ },
90
+ dependencies: {
91
+ "@clack/prompts": "^0.11.0",
92
+ picocolors: "^1.1.1"
93
+ },
94
+ devDependencies: {
95
+ "@biomejs/biome": "^2.3.10",
96
+ "@tsconfig/bun": "^1.0.10",
97
+ "@types/bun": "^1.3.5",
98
+ bunup: "^0.16.17",
99
+ typescript: "^5.9.3"
100
+ }
101
+ };
111
102
  });
112
103
 
113
104
  // src/cli.ts
114
- import pc2 from "picocolors";
105
+ import pc3 from "picocolors";
115
106
 
116
107
  // src/commands/convert.ts
117
- import { basename, dirname, join } from "path";
108
+ import { basename, dirname, join } from "node:path";
118
109
  import * as p2 from "@clack/prompts";
119
110
 
120
111
  // src/core/env-parser.ts
121
- import { existsSync, readFileSync } from "fs";
112
+ import { readFile } from "node:fs/promises";
122
113
 
123
114
  // src/utils/errors.ts
124
- var Env2OpError = class extends Error {
115
+ class Env2OpError extends Error {
116
+ code;
117
+ suggestion;
125
118
  constructor(message, code, suggestion) {
126
119
  super(message);
127
120
  this.code = code;
128
121
  this.suggestion = suggestion;
129
122
  this.name = "Env2OpError";
130
123
  }
131
- };
124
+ }
132
125
  var ErrorCodes = {
133
126
  ENV_FILE_NOT_FOUND: "ENV_FILE_NOT_FOUND",
134
127
  ENV_FILE_EMPTY: "ENV_FILE_EMPTY",
135
128
  OP_CLI_NOT_INSTALLED: "OP_CLI_NOT_INSTALLED",
136
129
  OP_NOT_SIGNED_IN: "OP_NOT_SIGNED_IN",
130
+ OP_SIGNIN_FAILED: "OP_SIGNIN_FAILED",
137
131
  VAULT_NOT_FOUND: "VAULT_NOT_FOUND",
138
132
  VAULT_CREATE_FAILED: "VAULT_CREATE_FAILED",
139
133
  ITEM_EXISTS: "ITEM_EXISTS",
140
134
  ITEM_CREATE_FAILED: "ITEM_CREATE_FAILED",
135
+ ITEM_EDIT_FAILED: "ITEM_EDIT_FAILED",
141
136
  PARSE_ERROR: "PARSE_ERROR",
142
137
  TEMPLATE_NOT_FOUND: "TEMPLATE_NOT_FOUND",
143
138
  INJECT_FAILED: "INJECT_FAILED"
144
139
  };
145
140
  var errors = {
146
- envFileNotFound: (path) => new Env2OpError(
147
- `File not found: ${path}`,
148
- ErrorCodes.ENV_FILE_NOT_FOUND,
149
- "Check that the file path is correct"
150
- ),
151
- envFileEmpty: (path) => new Env2OpError(
152
- `No valid environment variables found in ${path}`,
153
- ErrorCodes.ENV_FILE_EMPTY,
154
- "Ensure the file contains KEY=value pairs"
155
- ),
156
- opCliNotInstalled: () => new Env2OpError(
157
- "1Password CLI (op) is not installed",
158
- ErrorCodes.OP_CLI_NOT_INSTALLED,
159
- "Install it from https://1password.com/downloads/command-line/"
160
- ),
161
- opNotSignedIn: () => new Env2OpError(
162
- "Not signed in to 1Password CLI",
163
- ErrorCodes.OP_NOT_SIGNED_IN,
164
- 'Run "op signin" to authenticate'
165
- ),
166
- vaultNotFound: (vault2) => new Env2OpError(
167
- `Vault not found: ${vault2}`,
168
- ErrorCodes.VAULT_NOT_FOUND,
169
- 'Run "op vault list" to see available vaults'
170
- ),
141
+ envFileNotFound: (path) => new Env2OpError(`File not found: ${path}`, ErrorCodes.ENV_FILE_NOT_FOUND, "Check that the file path is correct"),
142
+ envFileEmpty: (path) => new Env2OpError(`No valid environment variables found in ${path}`, ErrorCodes.ENV_FILE_EMPTY, "Ensure the file contains KEY=value pairs"),
143
+ opCliNotInstalled: () => new Env2OpError("1Password CLI (op) is not installed", ErrorCodes.OP_CLI_NOT_INSTALLED, "Install it from https://1password.com/downloads/command-line/"),
144
+ opNotSignedIn: () => new Env2OpError("Not signed in to 1Password CLI", ErrorCodes.OP_NOT_SIGNED_IN, 'Run "op signin" to authenticate'),
145
+ vaultNotFound: (vault) => new Env2OpError(`Vault not found: ${vault}`, ErrorCodes.VAULT_NOT_FOUND, 'Run "op vault list" to see available vaults'),
171
146
  vaultCreateFailed: (message) => new Env2OpError(`Failed to create vault: ${message}`, ErrorCodes.VAULT_CREATE_FAILED),
172
- itemExists: (title, vault2) => new Env2OpError(
173
- `Item "${title}" already exists in vault "${vault2}"`,
174
- ErrorCodes.ITEM_EXISTS,
175
- "Use default behavior (overwrites) or choose a different item name"
176
- ),
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"),
177
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),
178
150
  parseError: (line, message) => new Env2OpError(`Parse error at line ${line}: ${message}`, ErrorCodes.PARSE_ERROR)
179
151
  };
180
152
 
181
153
  // src/core/env-parser.ts
182
154
  var HEADER_SEPARATOR = "# ===========================================================================";
183
155
  function stripHeaders(content) {
184
- const lines = content.split("\n");
156
+ const lines = content.split(`
157
+ `);
185
158
  const result = [];
186
159
  let inHeader = false;
187
160
  for (const line of lines) {
@@ -201,7 +174,8 @@ function stripHeaders(content) {
201
174
  while (result.length > 0 && result[0]?.trim() === "") {
202
175
  result.shift();
203
176
  }
204
- return result.join("\n");
177
+ return result.join(`
178
+ `);
205
179
  }
206
180
  function parseValue(raw) {
207
181
  const trimmed = raw.trim();
@@ -220,18 +194,27 @@ function parseValue(raw) {
220
194
  const parts = trimmed.split(/\s+#/);
221
195
  return (parts[0] ?? trimmed).trim();
222
196
  }
223
- function parseEnvFile(filePath) {
224
- if (!existsSync(filePath)) {
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 {
225
208
  throw errors.envFileNotFound(filePath);
226
209
  }
227
- const rawContent = readFileSync(filePath, "utf-8");
228
- const content = stripHeaders(rawContent);
229
- const rawLines = content.split("\n");
210
+ const content = stripHeaders(stripBom(rawContent));
211
+ const rawLines = content.split(`
212
+ `);
230
213
  const variables = [];
231
214
  const lines = [];
232
215
  const parseErrors = [];
233
216
  let currentComment = "";
234
- for (let i = 0; i < rawLines.length; i++) {
217
+ for (let i = 0;i < rawLines.length; i++) {
235
218
  const line = rawLines[i] ?? "";
236
219
  const trimmed = line.trim();
237
220
  const lineNumber = i + 1;
@@ -253,7 +236,7 @@ function parseEnvFile(filePath) {
253
236
  variables.push({
254
237
  key,
255
238
  value,
256
- comment: currentComment || void 0,
239
+ comment: currentComment || undefined,
257
240
  line: lineNumber
258
241
  });
259
242
  lines.push({ type: "variable", key, value });
@@ -271,101 +254,181 @@ function validateParseResult(result, filePath) {
271
254
  }
272
255
 
273
256
  // src/utils/shell.ts
274
- import { spawn } from "child_process";
275
- async function exec(command, args2 = []) {
276
- return new Promise((resolve, reject) => {
277
- const proc = spawn(command, args2, {
278
- shell: false,
279
- stdio: ["pipe", "pipe", "pipe"]
257
+ import { spawn } from "node:child_process";
258
+ import pc from "picocolors";
259
+ function quoteArg(arg) {
260
+ if (/[ [\]'"\\=]/.test(arg)) {
261
+ return `'${arg.replace(/'/g, "'\\''")}'`;
262
+ }
263
+ return arg;
264
+ }
265
+ async function exec(command, args = [], options = {}) {
266
+ const { verbose = false } = options;
267
+ const fullCommand = `${command} ${args.map(quoteArg).join(" ")}`;
268
+ if (verbose) {
269
+ console.log(pc.dim(`$ ${fullCommand}`));
270
+ }
271
+ return new Promise((resolve) => {
272
+ const proc = spawn(command, args, {
273
+ stdio: ["ignore", "pipe", "pipe"]
280
274
  });
281
- let stdout = "";
282
- let stderr = "";
275
+ const stdoutChunks = [];
276
+ const stderrChunks = [];
283
277
  proc.stdout?.on("data", (data) => {
284
- stdout += data.toString();
278
+ const text = Buffer.isBuffer(data) ? data.toString() : String(data);
279
+ stdoutChunks.push(text);
280
+ if (verbose) {
281
+ process.stdout.write(text);
282
+ }
285
283
  });
286
284
  proc.stderr?.on("data", (data) => {
287
- stderr += data.toString();
285
+ const text = Buffer.isBuffer(data) ? data.toString() : String(data);
286
+ stderrChunks.push(text);
287
+ if (verbose) {
288
+ process.stderr.write(text);
289
+ }
288
290
  });
289
- proc.on("error", (error) => {
290
- reject(error);
291
+ proc.on("close", (code) => {
292
+ resolve({
293
+ stdout: stdoutChunks.join(""),
294
+ stderr: stderrChunks.join(""),
295
+ exitCode: code ?? 1
296
+ });
297
+ });
298
+ proc.on("error", (err) => {
299
+ stderrChunks.push(err.message);
300
+ resolve({
301
+ stdout: stdoutChunks.join(""),
302
+ stderr: stderrChunks.join(""),
303
+ exitCode: 1
304
+ });
305
+ });
306
+ });
307
+ }
308
+ async function execWithStdin(command, args = [], options) {
309
+ const { stdin: stdinContent, verbose = false } = options;
310
+ if (verbose) {
311
+ const fullCommand = `${command} ${args.map(quoteArg).join(" ")}`;
312
+ console.log(pc.dim(`$ echo '...' | ${fullCommand}`));
313
+ }
314
+ return new Promise((resolve) => {
315
+ const proc = spawn(command, args, {
316
+ stdio: ["pipe", "pipe", "pipe"]
317
+ });
318
+ const stdoutChunks = [];
319
+ const stderrChunks = [];
320
+ proc.stdin?.write(stdinContent);
321
+ proc.stdin?.end();
322
+ proc.stdout?.on("data", (data) => {
323
+ const text = Buffer.isBuffer(data) ? data.toString() : String(data);
324
+ stdoutChunks.push(text);
325
+ if (verbose)
326
+ process.stdout.write(text);
327
+ });
328
+ proc.stderr?.on("data", (data) => {
329
+ const text = Buffer.isBuffer(data) ? data.toString() : String(data);
330
+ stderrChunks.push(text);
331
+ if (verbose)
332
+ process.stderr.write(text);
291
333
  });
292
334
  proc.on("close", (code) => {
293
335
  resolve({
294
- stdout,
295
- stderr,
296
- exitCode: code ?? 0
336
+ stdout: stdoutChunks.join(""),
337
+ stderr: stderrChunks.join(""),
338
+ exitCode: code ?? 1
339
+ });
340
+ });
341
+ proc.on("error", (err) => {
342
+ stderrChunks.push(err.message);
343
+ resolve({
344
+ stdout: stdoutChunks.join(""),
345
+ stderr: stderrChunks.join(""),
346
+ exitCode: 1
297
347
  });
298
348
  });
299
349
  });
300
350
  }
301
- async function execOrThrow(command, args2 = []) {
302
- const result = await exec(command, args2);
351
+
352
+ // src/core/onepassword.ts
353
+ async function checkOpCli(options = {}) {
354
+ const result = await exec("op", ["--version"], options);
355
+ return result.exitCode === 0;
356
+ }
357
+ async function checkSignedIn(options = {}) {
358
+ const result = await exec("op", ["whoami", "--format", "json"], options);
359
+ return result.exitCode === 0;
360
+ }
361
+ async function signIn(options = {}) {
362
+ const result = await exec("op", ["signin"], options);
363
+ return result.exitCode === 0;
364
+ }
365
+ async function itemExists(vault, title, options = {}) {
366
+ const result = await exec("op", ["item", "list", "--vault", vault, "--format", "json"], options);
303
367
  if (result.exitCode !== 0) {
304
- const error = new Error(result.stderr || `Command failed with exit code ${result.exitCode}`);
305
- error.stderr = result.stderr;
306
- error.stdout = result.stdout;
307
- error.exitCode = result.exitCode;
308
- throw error;
368
+ return null;
369
+ }
370
+ try {
371
+ const items = JSON.parse(result.stdout);
372
+ const item = items.find((item2) => item2.title === title);
373
+ return item?.id ?? null;
374
+ } catch {
375
+ return null;
309
376
  }
310
- return result;
311
- }
312
- async function execJson(command, args2 = []) {
313
- const result = await execOrThrow(command, args2);
314
- return JSON.parse(result.stdout);
315
377
  }
316
- async function execQuiet(command, args2 = []) {
378
+ async function vaultExists(vault, options = {}) {
379
+ const result = await exec("op", ["vault", "list", "--format", "json"], options);
380
+ if (result.exitCode !== 0) {
381
+ return false;
382
+ }
317
383
  try {
318
- const result = await exec(command, args2);
319
- return result.exitCode === 0;
384
+ const vaults = JSON.parse(result.stdout);
385
+ return vaults.some((v) => v.name === vault);
320
386
  } catch {
321
387
  return false;
322
388
  }
323
389
  }
324
-
325
- // src/core/onepassword.ts
326
- async function checkOpCli() {
327
- return execQuiet("op", ["--version"]);
328
- }
329
- async function checkSignedIn() {
330
- return execQuiet("op", ["account", "get"]);
331
- }
332
- async function itemExists(vault2, title) {
333
- return execQuiet("op", ["item", "get", title, "--vault", vault2]);
334
- }
335
- async function deleteItem(vault2, title) {
390
+ async function createVault(name, options = {}) {
336
391
  try {
337
- await exec("op", ["item", "delete", title, "--vault", vault2]);
338
- } catch (_error) {
392
+ await exec("op", ["vault", "create", name], options);
393
+ } catch (error) {
394
+ const message = error instanceof Error ? error.message : String(error);
395
+ throw errors.vaultCreateFailed(message);
339
396
  }
340
397
  }
341
- async function createSecureNote(options2) {
342
- const { vault: vault2, title, fields, secret } = options2;
343
- const fieldType = secret ? "password" : "text";
344
- const fieldArgs = fields.map(({ key, value }) => `${key}[${fieldType}]=${value}`);
398
+ function buildItemTemplate(title, vault, fields, secret) {
399
+ const fieldType = secret ? "CONCEALED" : "STRING";
400
+ return {
401
+ title,
402
+ vault: { name: vault },
403
+ category: "SECURE_NOTE",
404
+ fields: fields.map(({ key, value }) => ({
405
+ type: fieldType,
406
+ label: key,
407
+ value
408
+ }))
409
+ };
410
+ }
411
+ async function createSecureNote(options) {
412
+ const { vault, title, fields, secret, verbose } = options;
413
+ const template = buildItemTemplate(title, vault, fields, secret);
414
+ const json = JSON.stringify(template);
345
415
  try {
346
- const args2 = [
347
- "item",
348
- "create",
349
- "--category=Secure Note",
350
- `--vault=${vault2}`,
351
- `--title=${title}`,
352
- "--format=json",
353
- ...fieldArgs
354
- ];
355
- const result = await execJson("op", args2);
416
+ const result = await execWithStdin("op", ["item", "create", "--format", "json"], { stdin: json, verbose });
417
+ if (result.exitCode !== 0) {
418
+ throw new Error(result.stderr || "Failed to create item");
419
+ }
420
+ const item = JSON.parse(result.stdout);
356
421
  const fieldIds = {};
357
- if (Array.isArray(result.fields)) {
358
- for (const field of result.fields) {
359
- if (field.label && field.id) {
360
- fieldIds[field.label] = field.id;
361
- }
422
+ for (const field of item.fields ?? []) {
423
+ if (field.label && field.id) {
424
+ fieldIds[field.label] = field.id;
362
425
  }
363
426
  }
364
427
  return {
365
- id: result.id,
366
- title: result.title,
367
- vault: result.vault?.name ?? vault2,
368
- vaultId: result.vault?.id ?? "",
428
+ id: item.id,
429
+ title: item.title,
430
+ vault: item.vault?.name ?? vault,
431
+ vaultId: item.vault?.id ?? "",
369
432
  fieldIds
370
433
  };
371
434
  } catch (error) {
@@ -373,21 +436,41 @@ async function createSecureNote(options2) {
373
436
  throw errors.itemCreateFailed(message);
374
437
  }
375
438
  }
376
- async function vaultExists(vault2) {
377
- return execQuiet("op", ["vault", "get", vault2]);
378
- }
379
- async function createVault(name) {
439
+ async function editSecureNote(options) {
440
+ const { vault, title, fields, secret, verbose, itemId } = options;
441
+ const template = buildItemTemplate(title, vault, fields, secret);
442
+ const json = JSON.stringify(template);
380
443
  try {
381
- await exec("op", ["vault", "create", name]);
444
+ const result = await execWithStdin("op", ["item", "edit", itemId, "--format", "json"], {
445
+ stdin: json,
446
+ verbose
447
+ });
448
+ if (result.exitCode !== 0) {
449
+ throw new Error(result.stderr || "Failed to edit item");
450
+ }
451
+ const item = JSON.parse(result.stdout);
452
+ const fieldIds = {};
453
+ for (const field of item.fields ?? []) {
454
+ if (field.label && field.id) {
455
+ fieldIds[field.label] = field.id;
456
+ }
457
+ }
458
+ return {
459
+ id: item.id,
460
+ title: item.title,
461
+ vault: item.vault?.name ?? vault,
462
+ vaultId: item.vault?.id ?? "",
463
+ fieldIds
464
+ };
382
465
  } catch (error) {
383
466
  const message = error instanceof Error ? error.message : String(error);
384
- throw errors.vaultCreateFailed(message);
467
+ throw errors.itemEditFailed(message);
385
468
  }
386
469
  }
387
470
 
388
471
  // src/core/template-generator.ts
389
- var import_package = __toESM(require_package(), 1);
390
- import { writeFileSync } from "fs";
472
+ var import__package = __toESM(require_package(), 1);
473
+ import { writeFileSync } from "node:fs";
391
474
  var SEPARATOR = "# ===========================================================================";
392
475
  function deriveEnvFileName(templateFileName) {
393
476
  if (templateFileName.endsWith(".tpl")) {
@@ -395,17 +478,20 @@ function deriveEnvFileName(templateFileName) {
395
478
  }
396
479
  return templateFileName;
397
480
  }
481
+ function deriveTemplateFileName(envFileName) {
482
+ return `${envFileName}.tpl`;
483
+ }
398
484
  function formatTimestamp() {
399
- return (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, " UTC");
485
+ return new Date().toISOString().replace("T", " ").replace(/\.\d{3}Z$/, " UTC");
400
486
  }
401
487
  function generateTemplateHeader(templateFileName) {
402
488
  const envFileName = deriveEnvFileName(templateFileName);
403
489
  return [
404
490
  SEPARATOR,
405
- `# ${templateFileName} \u2014 1Password Secret References`,
491
+ `# ${templateFileName} 1Password Secret References`,
406
492
  "#",
407
493
  "# This template contains references to secrets stored in 1Password.",
408
- "# The actual values are not stored here \u2014 only secret references.",
494
+ "# The actual values are not stored here only secret references.",
409
495
  "#",
410
496
  `# To generate ${envFileName} with real values:`,
411
497
  `# op2env ${templateFileName}`,
@@ -414,14 +500,33 @@ function generateTemplateHeader(templateFileName) {
414
500
  `# op run --env-file ${templateFileName} -- npm start`,
415
501
  "#",
416
502
  `# Pushed: ${formatTimestamp()}`,
417
- `# Generated by env2op v${import_package.default.version}`,
503
+ `# Generated by env2op v${import__package.default.version}`,
418
504
  "# https://github.com/tolgamorf/env2op-cli",
419
505
  SEPARATOR,
420
506
  ""
421
507
  ];
422
508
  }
423
- function generateTemplateContent(options2, templateFileName) {
424
- const { vaultId, itemId, lines: envLines, fieldIds } = options2;
509
+ function generateEnvHeader(envFileName) {
510
+ const templateFileName = deriveTemplateFileName(envFileName);
511
+ return [
512
+ SEPARATOR,
513
+ `# ${envFileName} — Environment Variables`,
514
+ "#",
515
+ "# WARNING: This file contains sensitive values. Do not commit to git!",
516
+ "#",
517
+ `# To push updates to 1Password and generate ${templateFileName}:`,
518
+ `# env2op ${envFileName} <vault> "<item_name>"`,
519
+ "#",
520
+ `# Pulled: ${formatTimestamp()}`,
521
+ `# Generated by op2env v${import__package.default.version}`,
522
+ "# https://github.com/tolgamorf/env2op-cli",
523
+ SEPARATOR,
524
+ "",
525
+ ""
526
+ ];
527
+ }
528
+ function generateTemplateContent(options, templateFileName) {
529
+ const { vaultId, itemId, lines: envLines, fieldIds } = options;
425
530
  const outputLines = generateTemplateHeader(templateFileName);
426
531
  for (const line of envLines) {
427
532
  switch (line.type) {
@@ -438,129 +543,80 @@ function generateTemplateContent(options2, templateFileName) {
438
543
  }
439
544
  }
440
545
  }
441
- return `${outputLines.join("\n")}
546
+ return `${outputLines.join(`
547
+ `)}
442
548
  `;
443
549
  }
444
550
  function writeTemplate(content, outputPath) {
445
551
  writeFileSync(outputPath, content, "utf-8");
446
552
  }
447
553
  function generateUsageInstructions(templatePath) {
448
- return ["", "Usage:", ` op2env ${templatePath}`, ` op run --env-file ${templatePath} -- npm start`].join("\n");
554
+ return ["Usage:", ` op2env ${templatePath}`, ` op run --env-file ${templatePath} -- npm start`].join(`
555
+ `);
449
556
  }
450
557
 
451
558
  // src/utils/logger.ts
452
559
  import * as p from "@clack/prompts";
453
- import pc from "picocolors";
560
+ import pc2 from "picocolors";
454
561
  var symbols = {
455
- success: pc.green("\u2713"),
456
- error: pc.red("\u2717"),
457
- warning: pc.yellow("\u26A0"),
458
- info: pc.blue("\u2139"),
459
- arrow: pc.cyan("\u2192"),
460
- bullet: pc.dim("\u2022")
562
+ success: pc2.green(""),
563
+ error: pc2.red(""),
564
+ warning: pc2.yellow(""),
565
+ info: pc2.blue(""),
566
+ arrow: pc2.cyan(""),
567
+ bullet: pc2.dim("")
461
568
  };
462
569
  var logger = {
463
- /**
464
- * Display CLI intro banner
465
- */
466
570
  intro(name, version, dryRun = false) {
467
- const label = dryRun ? pc.bgYellow(pc.black(` ${name} v${version} [DRY RUN] `)) : pc.bgCyan(pc.black(` ${name} v${version} `));
571
+ const label = dryRun ? pc2.bgYellow(pc2.black(` ${name} v${version} [DRY RUN] `)) : pc2.bgCyan(pc2.black(` ${name} v${version} `));
468
572
  p.intro(label);
469
573
  },
470
- /**
471
- * Display section header
472
- */
473
574
  section(title) {
474
575
  console.log(`
475
- ${pc.bold(pc.underline(title))}`);
576
+ ${pc2.bold(pc2.underline(title))}`);
476
577
  },
477
- /**
478
- * Success message
479
- */
480
578
  success(message) {
481
579
  p.log.success(message);
482
580
  },
483
- /**
484
- * Error message
485
- */
486
581
  error(message) {
487
582
  p.log.error(message);
488
583
  },
489
- /**
490
- * Warning message
491
- */
492
584
  warn(message) {
493
585
  p.log.warn(message);
494
586
  },
495
- /**
496
- * Info message
497
- */
498
587
  info(message) {
499
588
  p.log.info(message);
500
589
  },
501
- /**
502
- * Step in a process
503
- */
504
590
  step(message) {
505
591
  p.log.step(message);
506
592
  },
507
- /**
508
- * Message (neutral)
509
- */
510
593
  message(message) {
511
594
  p.log.message(message);
512
595
  },
513
- /**
514
- * Display key-value pair
515
- */
516
596
  keyValue(key, value, indent = 2) {
517
- console.log(`${" ".repeat(indent)}${pc.dim(key)}: ${pc.cyan(value)}`);
597
+ console.log(`${" ".repeat(indent)}${pc2.dim(key)}: ${pc2.cyan(value)}`);
518
598
  },
519
- /**
520
- * Display list item
521
- */
522
599
  listItem(item, indent = 2) {
523
600
  console.log(`${" ".repeat(indent)}${symbols.bullet} ${item}`);
524
601
  },
525
- /**
526
- * Display arrow item
527
- */
528
602
  arrowItem(item, indent = 2) {
529
603
  console.log(`${" ".repeat(indent)}${symbols.arrow} ${item}`);
530
604
  },
531
- /**
532
- * Display dry run indicator
533
- */
534
605
  dryRun(message) {
535
- console.log(`${pc.yellow("[DRY RUN]")} ${message}`);
606
+ console.log(`${pc2.yellow("[DRY RUN]")} ${message}`);
536
607
  },
537
- /**
538
- * Create a spinner for async operations
539
- */
540
608
  spinner() {
541
609
  return p.spinner();
542
610
  },
543
- /**
544
- * Display outro message
545
- */
546
611
  outro(message) {
547
- p.outro(pc.green(message));
612
+ p.outro(pc2.green(message));
548
613
  },
549
- /**
550
- * Display cancellation message
551
- */
552
614
  cancel(message) {
553
615
  p.cancel(message);
554
616
  },
555
- /**
556
- * Display a note block
557
- */
558
617
  note(message, title) {
559
618
  p.note(message, title);
560
619
  },
561
- /**
562
- * Format a field list for display
563
- */
564
620
  formatFields(fields, max = 3) {
565
621
  if (fields.length <= max) {
566
622
  return fields.join(", ");
@@ -569,113 +625,147 @@ ${pc.bold(pc.underline(title))}`);
569
625
  }
570
626
  };
571
627
 
628
+ // src/utils/timing.ts
629
+ import { setTimeout } from "node:timers/promises";
630
+ var MIN_SPINNER_TIME = 500;
631
+ async function withMinTime(promise, minTime = MIN_SPINNER_TIME) {
632
+ const [result] = await Promise.all([promise, setTimeout(minTime)]);
633
+ return result;
634
+ }
635
+
572
636
  // src/commands/convert.ts
573
- async function runConvert(options2) {
574
- const { envFile: envFile2, vault: vault2, itemName: itemName2, output, dryRun, secret, force } = options2;
575
- const pkg3 = await Promise.resolve().then(() => __toESM(require_package(), 1));
576
- logger.intro("env2op", pkg3.version, dryRun);
637
+ async function runConvert(options) {
638
+ const { envFile, vault, itemName, output, dryRun, secret, force, verbose } = options;
639
+ const pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
640
+ logger.intro("env2op", pkg2.version, dryRun);
577
641
  try {
578
- const parseResult = parseEnvFile(envFile2);
579
- validateParseResult(parseResult, envFile2);
642
+ const parseResult = await parseEnvFile(envFile);
643
+ validateParseResult(parseResult, envFile);
580
644
  const { variables, lines } = parseResult;
581
- logger.success(`Parsed ${basename(envFile2)}`);
582
- logger.message(`Found ${variables.length} environment variable${variables.length === 1 ? "" : "s"}`);
645
+ const varCount = variables.length;
646
+ logger.success(`Parsed ${basename(envFile)} found ${varCount} variable${varCount === 1 ? "" : "s"}`);
583
647
  for (const error of parseResult.errors) {
584
648
  logger.warn(error);
585
649
  }
586
650
  let itemResult = null;
587
651
  if (dryRun) {
588
- logger.warn("Would create Secure Note");
589
- logger.keyValue("Vault", vault2);
590
- logger.keyValue("Title", itemName2);
652
+ logger.step("Checking 1Password CLI...");
653
+ logger.warn("Would check if 1Password CLI is installed and authenticated");
654
+ logger.step(`Checking for vault "${vault}"...`);
655
+ logger.warn(`Would check if vault "${vault}" exists`);
656
+ logger.step(`Checking for item "${itemName}"...`);
657
+ logger.warn(`Would check if item "${itemName}" exists`);
658
+ logger.step("Pushing environment variables...");
659
+ logger.warn("Would push to 1Password:");
660
+ logger.keyValue("Vault", vault);
661
+ logger.keyValue("Title", itemName);
591
662
  logger.keyValue("Type", secret ? "password (hidden)" : "text (visible)");
592
663
  logger.keyValue("Fields", logger.formatFields(variables.map((v) => v.key)));
593
664
  } else {
594
- const opInstalled = await checkOpCli();
665
+ const authSpinner = p2.spinner();
666
+ authSpinner.start("Checking 1Password CLI...");
667
+ const opInstalled = await checkOpCli({ verbose });
595
668
  if (!opInstalled) {
596
- throw new Env2OpError(
597
- "1Password CLI (op) is not installed",
598
- "OP_CLI_NOT_INSTALLED",
599
- "Install from https://1password.com/downloads/command-line/"
600
- );
669
+ authSpinner.stop("1Password CLI not found");
670
+ throw new Env2OpError("1Password CLI (op) is not installed", "OP_CLI_NOT_INSTALLED", "Install from https://1password.com/downloads/command-line/");
601
671
  }
602
- const signedIn = await checkSignedIn();
672
+ let signedIn = await checkSignedIn({ verbose });
603
673
  if (!signedIn) {
604
- throw new Env2OpError(
605
- "Not signed in to 1Password CLI",
606
- "OP_NOT_SIGNED_IN",
607
- 'Run "op signin" to authenticate'
608
- );
674
+ authSpinner.message("Signing in to 1Password...");
675
+ const signInSuccess = await signIn({ verbose });
676
+ if (!signInSuccess) {
677
+ authSpinner.stop();
678
+ throw new Env2OpError("Failed to sign in to 1Password CLI", "OP_SIGNIN_FAILED", 'Try running "op signin" manually');
679
+ }
680
+ signedIn = await checkSignedIn({ verbose });
681
+ if (!signedIn) {
682
+ authSpinner.stop();
683
+ throw new Env2OpError("Not signed in to 1Password CLI", "OP_NOT_SIGNED_IN", 'Run "op signin" to authenticate');
684
+ }
609
685
  }
610
- const vaultFound = await vaultExists(vault2);
686
+ authSpinner.stop("1Password CLI ready");
687
+ const vaultSpinner = p2.spinner();
688
+ vaultSpinner.start(`Checking for vault "${vault}"...`);
689
+ const vaultFound = await withMinTime(vaultExists(vault, { verbose }));
611
690
  if (vaultFound) {
612
- logger.success(`Vault "${vault2}" found`);
691
+ vaultSpinner.stop(`Vault "${vault}" found`);
613
692
  } else {
614
693
  if (force) {
615
- logger.warn(`Vault "${vault2}" not found, creating...`);
616
- await createVault(vault2);
617
- logger.success(`Created vault "${vault2}"`);
694
+ vaultSpinner.message(`Vault "${vault}" not found, creating...`);
695
+ await createVault(vault, { verbose });
696
+ vaultSpinner.stop(`Created vault "${vault}"`);
618
697
  } else {
698
+ vaultSpinner.stop(`Vault "${vault}" not found`);
619
699
  const shouldCreate = await p2.confirm({
620
- message: `Vault "${vault2}" does not exist. Create it?`
700
+ message: `Vault "${vault}" does not exist. Create it?`
621
701
  });
622
702
  if (p2.isCancel(shouldCreate) || !shouldCreate) {
623
703
  logger.cancel("Operation cancelled");
624
704
  logger.info('Run "op vault list" to see available vaults');
625
705
  process.exit(0);
626
706
  }
627
- const spinner3 = logger.spinner();
628
- spinner3.start(`Creating vault "${vault2}"...`);
629
- await createVault(vault2);
630
- spinner3.stop(`Created vault "${vault2}"`);
707
+ const createSpinner = p2.spinner();
708
+ createSpinner.start(`Creating vault "${vault}"...`);
709
+ await createVault(vault, { verbose });
710
+ createSpinner.stop(`Created vault "${vault}"`);
631
711
  }
632
712
  }
633
- const exists = await itemExists(vault2, itemName2);
634
- if (exists) {
635
- if (force) {
636
- logger.warn(`Item "${itemName2}" already exists, overwriting...`);
637
- await deleteItem(vault2, itemName2);
638
- } else {
713
+ const itemSpinner = p2.spinner();
714
+ itemSpinner.start(`Checking for item "${itemName}"...`);
715
+ const existingItemId = await withMinTime(itemExists(vault, itemName, { verbose }));
716
+ if (existingItemId) {
717
+ itemSpinner.stop(`Item "${itemName}" found`);
718
+ if (!force) {
639
719
  const shouldOverwrite = await p2.confirm({
640
- message: `Item "${itemName2}" already exists in vault "${vault2}". Overwrite?`
720
+ message: `Item "${itemName}" already exists in vault "${vault}". Update it?`
641
721
  });
642
722
  if (p2.isCancel(shouldOverwrite) || !shouldOverwrite) {
643
723
  logger.cancel("Operation cancelled");
644
724
  process.exit(0);
645
725
  }
646
- await deleteItem(vault2, itemName2);
647
726
  }
727
+ } else {
728
+ itemSpinner.stop(`Item "${itemName}" not found`);
648
729
  }
649
- const spinner2 = logger.spinner();
650
- spinner2.start("Creating 1Password Secure Note...");
730
+ const pushSpinner = p2.spinner();
731
+ pushSpinner.start("Pushing environment variables...");
651
732
  try {
652
- itemResult = await createSecureNote({
653
- vault: vault2,
654
- title: itemName2,
655
- fields: variables,
656
- secret
657
- });
658
- spinner2.stop(`Created "${itemResult.title}" in vault "${itemResult.vault}"`);
733
+ if (existingItemId) {
734
+ itemResult = await editSecureNote({
735
+ vault,
736
+ title: itemName,
737
+ fields: variables,
738
+ secret,
739
+ verbose,
740
+ itemId: existingItemId
741
+ });
742
+ } else {
743
+ itemResult = await createSecureNote({
744
+ vault,
745
+ title: itemName,
746
+ fields: variables,
747
+ secret,
748
+ verbose
749
+ });
750
+ }
751
+ pushSpinner.stop(existingItemId ? `Updated "${itemResult.title}" in vault "${itemResult.vault}"` : `Created "${itemResult.title}" in vault "${itemResult.vault}"`);
659
752
  } catch (error) {
660
- spinner2.stop("Failed to create Secure Note");
753
+ pushSpinner.stop();
661
754
  throw error;
662
755
  }
663
756
  }
664
- const templatePath = output ?? join(dirname(envFile2), `${basename(envFile2)}.tpl`);
757
+ const templatePath = output ?? join(dirname(envFile), `${basename(envFile)}.tpl`);
665
758
  const templateFileName = basename(templatePath);
666
759
  if (dryRun) {
667
760
  logger.warn(`Would generate template: ${templatePath}`);
668
761
  } else if (itemResult) {
669
- const templateContent = generateTemplateContent(
670
- {
671
- vaultId: itemResult.vaultId,
672
- itemId: itemResult.id,
673
- variables,
674
- lines,
675
- fieldIds: itemResult.fieldIds
676
- },
677
- templateFileName
678
- );
762
+ const templateContent = generateTemplateContent({
763
+ vaultId: itemResult.vaultId,
764
+ itemId: itemResult.id,
765
+ variables,
766
+ lines,
767
+ fieldIds: itemResult.fieldIds
768
+ }, templateFileName);
679
769
  writeTemplate(templateContent, templatePath);
680
770
  logger.success(`Generated template: ${templatePath}`);
681
771
  }
@@ -701,10 +791,10 @@ async function runConvert(options2) {
701
791
  // src/cli.ts
702
792
  var pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
703
793
  var args = process.argv.slice(2);
704
- var flags = /* @__PURE__ */ new Set();
794
+ var flags = new Set;
705
795
  var positional = [];
706
796
  var options = {};
707
- for (let i = 0; i < args.length; i++) {
797
+ for (let i = 0;i < args.length; i++) {
708
798
  const arg = args[i];
709
799
  if (arg === "-o" || arg === "--output") {
710
800
  const next = args[i + 1];
@@ -744,67 +834,73 @@ await runConvert({
744
834
  output: options.output,
745
835
  dryRun: flags.has("dry-run"),
746
836
  secret: flags.has("secret"),
747
- force: flags.has("f") || flags.has("force")
837
+ force: flags.has("f") || flags.has("force"),
838
+ verbose: flags.has("verbose")
748
839
  });
749
840
  function showHelp() {
750
- const name = pc2.bold(pc2.cyan("env2op"));
751
- const version = pc2.dim(`v${pkg2.version}`);
841
+ const name = pc3.bold(pc3.cyan("env2op"));
842
+ const version = pc3.dim(`v${pkg2.version}`);
752
843
  console.log(`
753
844
  ${name} ${version}
754
845
  ${pkg2.description}
755
846
 
756
- ${pc2.bold("USAGE")}
757
- ${pc2.cyan("$")} env2op ${pc2.yellow("<env_file>")} ${pc2.yellow("<vault>")} ${pc2.yellow("<item_name>")} ${pc2.dim("[options]")}
847
+ ${pc3.bold("USAGE")}
848
+ ${pc3.cyan("$")} env2op ${pc3.yellow("<env_file>")} ${pc3.yellow("<vault>")} ${pc3.yellow("<item_name>")} ${pc3.dim("[options]")}
758
849
 
759
- ${pc2.bold("ARGUMENTS")}
760
- ${pc2.yellow("env_file")} Path to .env file
761
- ${pc2.yellow("vault")} 1Password vault name
762
- ${pc2.yellow("item_name")} Name for the Secure Note in 1Password
850
+ ${pc3.bold("ARGUMENTS")}
851
+ ${pc3.yellow("env_file")} Path to .env file
852
+ ${pc3.yellow("vault")} 1Password vault name
853
+ ${pc3.yellow("item_name")} Name for the Secure Note in 1Password
763
854
 
764
- ${pc2.bold("OPTIONS")}
765
- ${pc2.cyan("-o, --output")} Output template path (default: <env_file>.tpl)
766
- ${pc2.cyan("-f, --force")} Skip confirmation prompts
767
- ${pc2.cyan("--dry-run")} Preview actions without executing
768
- ${pc2.cyan("--secret")} Store all fields as password type (hidden)
769
- ${pc2.cyan("-h, --help")} Show this help message
770
- ${pc2.cyan("-v, --version")} Show version
855
+ ${pc3.bold("OPTIONS")}
856
+ ${pc3.cyan("-o, --output")} Output template path (default: <env_file>.tpl)
857
+ ${pc3.cyan("-f, --force")} Skip confirmation prompts
858
+ ${pc3.cyan("--dry-run")} Preview actions without executing
859
+ ${pc3.cyan("--secret")} Store all fields as password type (hidden)
860
+ ${pc3.cyan("--verbose")} Show op CLI output
861
+ ${pc3.cyan("-h, --help")} Show this help message
862
+ ${pc3.cyan("-v, --version")} Show version
771
863
 
772
- ${pc2.bold("EXAMPLES")}
773
- ${pc2.dim("# Basic usage")}
774
- ${pc2.cyan("$")} env2op .env.production Personal "MyApp - Production"
864
+ ${pc3.bold("EXAMPLES")}
865
+ ${pc3.dim("# Basic usage")}
866
+ ${pc3.cyan("$")} env2op .env.production Personal "MyApp - Production"
775
867
 
776
- ${pc2.dim("# Custom output path")}
777
- ${pc2.cyan("$")} env2op .env Personal "MyApp" -o secrets.tpl
868
+ ${pc3.dim("# Custom output path")}
869
+ ${pc3.cyan("$")} env2op .env Personal "MyApp" -o secrets.tpl
778
870
 
779
- ${pc2.dim("# Preview without making changes")}
780
- ${pc2.cyan("$")} env2op .env Personal "MyApp" --dry-run
871
+ ${pc3.dim("# Preview without making changes")}
872
+ ${pc3.cyan("$")} env2op .env Personal "MyApp" --dry-run
781
873
 
782
- ${pc2.dim("# Store as hidden password fields")}
783
- ${pc2.cyan("$")} env2op .env Personal "MyApp" --secret
874
+ ${pc3.dim("# Store as hidden password fields")}
875
+ ${pc3.cyan("$")} env2op .env Personal "MyApp" --secret
784
876
 
785
- ${pc2.dim("# Skip confirmation prompts (for CI/scripts)")}
786
- ${pc2.cyan("$")} env2op .env Personal "MyApp" -f
877
+ ${pc3.dim("# Skip confirmation prompts (for CI/scripts)")}
878
+ ${pc3.cyan("$")} env2op .env Personal "MyApp" -f
787
879
 
788
- ${pc2.bold("DOCUMENTATION")}
789
- ${pc2.dim("https://github.com/tolgamorf/env2op-cli")}
880
+ ${pc3.bold("DOCUMENTATION")}
881
+ ${pc3.dim("https://github.com/tolgamorf/env2op-cli")}
790
882
  `);
791
883
  }
792
884
  function showMissingArgsError(provided) {
793
885
  const missing = [];
794
- if (provided.length < 1) missing.push("env_file");
795
- if (provided.length < 2) missing.push("vault");
796
- if (provided.length < 3) missing.push("item_name");
886
+ if (provided.length < 1)
887
+ missing.push("env_file");
888
+ if (provided.length < 2)
889
+ missing.push("vault");
890
+ if (provided.length < 3)
891
+ missing.push("item_name");
797
892
  console.log(`
798
- ${pc2.red(pc2.bold("Error:"))} Missing required arguments
893
+ ${pc3.red(pc3.bold("Error:"))} Missing required arguments
799
894
 
800
- ${pc2.bold("Usage:")} env2op ${pc2.yellow("<env_file>")} ${pc2.yellow("<vault>")} ${pc2.yellow("<item_name>")} ${pc2.dim("[options]")}
895
+ ${pc3.bold("Usage:")} env2op ${pc3.yellow("<env_file>")} ${pc3.yellow("<vault>")} ${pc3.yellow("<item_name>")} ${pc3.dim("[options]")}
801
896
 
802
- ${pc2.bold("Missing:")}
803
- ${missing.map((arg) => ` ${pc2.red("\u2022")} ${pc2.yellow(arg)}`).join("\n")}
897
+ ${pc3.bold("Missing:")}
898
+ ${missing.map((arg) => ` ${pc3.red("")} ${pc3.yellow(arg)}`).join(`
899
+ `)}
804
900
 
805
- ${pc2.bold("Example:")}
806
- ${pc2.cyan("$")} env2op .env.production Personal "MyApp - Production"
901
+ ${pc3.bold("Example:")}
902
+ ${pc3.cyan("$")} env2op .env.production Personal "MyApp - Production"
807
903
 
808
- Run ${pc2.cyan("env2op --help")} for more information.
904
+ Run ${pc3.cyan("env2op --help")} for more information.
809
905
  `);
810
906
  }