@tolgamorf/env2op-cli 0.2.2 → 0.2.3

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.
@@ -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.2",
25
+ version: "0.2.3",
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,12 +105,11 @@ var require_package = __commonJS((exports, module) => {
105
105
  import pc5 from "picocolors";
106
106
 
107
107
  // src/commands/inject.ts
108
- import { existsSync, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
108
+ import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
109
109
  import { basename } from "node:path";
110
- import * as p2 from "@clack/prompts";
111
110
 
112
- // src/core/env-parser.ts
113
- import { readFile } from "node:fs/promises";
111
+ // src/core/auth.ts
112
+ import * as p from "@clack/prompts";
114
113
 
115
114
  // src/utils/errors.ts
116
115
  class Env2OpError extends Error {
@@ -143,117 +142,17 @@ var errors = {
143
142
  envFileEmpty: (path) => new Env2OpError(`No valid environment variables found in ${path}`, ErrorCodes.ENV_FILE_EMPTY, "Ensure the file contains KEY=value pairs"),
144
143
  opCliNotInstalled: () => new Env2OpError("1Password CLI (op) is not installed", ErrorCodes.OP_CLI_NOT_INSTALLED, "Install it from https://1password.com/downloads/command-line/"),
145
144
  opNotSignedIn: () => new Env2OpError("Not signed in to 1Password CLI", ErrorCodes.OP_NOT_SIGNED_IN, 'Run "op signin" to authenticate'),
145
+ opSigninFailed: () => new Env2OpError("Failed to sign in to 1Password CLI", ErrorCodes.OP_SIGNIN_FAILED, 'Try running "op signin" manually'),
146
146
  vaultNotFound: (vault) => new Env2OpError(`Vault not found: ${vault}`, ErrorCodes.VAULT_NOT_FOUND, 'Run "op vault list" to see available vaults'),
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
150
  itemEditFailed: (message) => new Env2OpError(`Failed to edit 1Password item: ${message}`, ErrorCodes.ITEM_EDIT_FAILED),
151
- parseError: (line, message) => new Env2OpError(`Parse error at line ${line}: ${message}`, ErrorCodes.PARSE_ERROR)
151
+ parseError: (line, message) => new Env2OpError(`Parse error at line ${line}: ${message}`, ErrorCodes.PARSE_ERROR),
152
+ templateNotFound: (path) => new Env2OpError(`Template file not found: ${path}`, ErrorCodes.TEMPLATE_NOT_FOUND, "Ensure the file exists and the path is correct"),
153
+ injectFailed: (message) => new Env2OpError("Failed to pull secrets from 1Password", ErrorCodes.INJECT_FAILED, message)
152
154
  };
153
155
 
154
- // src/core/env-parser.ts
155
- var HEADER_SEPARATOR = "# ===========================================================================";
156
- function stripHeaders(content) {
157
- const lines = content.split(`
158
- `);
159
- const result = [];
160
- let inHeader = false;
161
- for (const line of lines) {
162
- const trimmed = line.trim();
163
- if (trimmed === HEADER_SEPARATOR) {
164
- if (!inHeader) {
165
- inHeader = true;
166
- } else {
167
- inHeader = false;
168
- }
169
- continue;
170
- }
171
- if (!inHeader) {
172
- result.push(line);
173
- }
174
- }
175
- while (result.length > 0 && result[0]?.trim() === "") {
176
- result.shift();
177
- }
178
- return result.join(`
179
- `);
180
- }
181
- function parseValue(raw) {
182
- const trimmed = raw.trim();
183
- if (trimmed.startsWith('"')) {
184
- const endQuote = trimmed.indexOf('"', 1);
185
- if (endQuote !== -1) {
186
- return trimmed.slice(1, endQuote);
187
- }
188
- }
189
- if (trimmed.startsWith("'")) {
190
- const endQuote = trimmed.indexOf("'", 1);
191
- if (endQuote !== -1) {
192
- return trimmed.slice(1, endQuote);
193
- }
194
- }
195
- const parts = trimmed.split(/\s+#/);
196
- return (parts[0] ?? trimmed).trim();
197
- }
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 {
209
- throw errors.envFileNotFound(filePath);
210
- }
211
- const content = stripHeaders(stripBom(rawContent));
212
- const rawLines = content.split(`
213
- `);
214
- const variables = [];
215
- const lines = [];
216
- const parseErrors = [];
217
- let currentComment = "";
218
- for (let i = 0;i < rawLines.length; i++) {
219
- const line = rawLines[i] ?? "";
220
- const trimmed = line.trim();
221
- const lineNumber = i + 1;
222
- if (!trimmed) {
223
- lines.push({ type: "empty" });
224
- currentComment = "";
225
- continue;
226
- }
227
- if (trimmed.startsWith("#")) {
228
- lines.push({ type: "comment", content: line });
229
- currentComment = trimmed.slice(1).trim();
230
- continue;
231
- }
232
- const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
233
- if (match?.[1]) {
234
- const key = match[1];
235
- const rawValue = match[2] ?? "";
236
- const value = parseValue(rawValue);
237
- variables.push({
238
- key,
239
- value,
240
- comment: currentComment || undefined,
241
- line: lineNumber
242
- });
243
- lines.push({ type: "variable", key, value });
244
- currentComment = "";
245
- } else if (trimmed.includes("=")) {
246
- parseErrors.push(`Line ${lineNumber}: Invalid variable name`);
247
- }
248
- }
249
- return { variables, lines, errors: parseErrors };
250
- }
251
- function validateParseResult(result, filePath) {
252
- if (result.variables.length === 0) {
253
- throw errors.envFileEmpty(filePath);
254
- }
255
- }
256
-
257
156
  // src/utils/shell.ts
258
157
  import { spawn } from "node:child_process";
259
158
  import pc from "picocolors";
@@ -263,16 +162,8 @@ function quoteArg(arg) {
263
162
  }
264
163
  return arg;
265
164
  }
266
- async function exec(command, args = [], options = {}) {
267
- const { verbose = false } = options;
268
- const fullCommand = `${command} ${args.map(quoteArg).join(" ")}`;
269
- if (verbose) {
270
- console.log(pc.dim(`$ ${fullCommand}`));
271
- }
165
+ function collectOutput(proc, verbose) {
272
166
  return new Promise((resolve) => {
273
- const proc = spawn(command, args, {
274
- stdio: ["ignore", "pipe", "pipe"]
275
- });
276
167
  const stdoutChunks = [];
277
168
  const stderrChunks = [];
278
169
  proc.stdout?.on("data", (data) => {
@@ -306,48 +197,29 @@ async function exec(command, args = [], options = {}) {
306
197
  });
307
198
  });
308
199
  }
200
+ async function exec(command, args = [], options = {}) {
201
+ const { verbose = false } = options;
202
+ const fullCommand = `${command} ${args.map(quoteArg).join(" ")}`;
203
+ if (verbose) {
204
+ console.log(pc.dim(`$ ${fullCommand}`));
205
+ }
206
+ const proc = spawn(command, args, {
207
+ stdio: ["ignore", "pipe", "pipe"]
208
+ });
209
+ return collectOutput(proc, verbose);
210
+ }
309
211
  async function execWithStdin(command, args = [], options) {
310
212
  const { stdin: stdinContent, verbose = false } = options;
311
213
  if (verbose) {
312
214
  const fullCommand = `${command} ${args.map(quoteArg).join(" ")}`;
313
215
  console.log(pc.dim(`$ echo '...' | ${fullCommand}`));
314
216
  }
315
- return new Promise((resolve) => {
316
- const proc = spawn(command, args, {
317
- stdio: ["pipe", "pipe", "pipe"]
318
- });
319
- const stdoutChunks = [];
320
- const stderrChunks = [];
321
- proc.stdin?.write(stdinContent);
322
- proc.stdin?.end();
323
- proc.stdout?.on("data", (data) => {
324
- const text = Buffer.isBuffer(data) ? data.toString() : String(data);
325
- stdoutChunks.push(text);
326
- if (verbose)
327
- process.stdout.write(text);
328
- });
329
- proc.stderr?.on("data", (data) => {
330
- const text = Buffer.isBuffer(data) ? data.toString() : String(data);
331
- stderrChunks.push(text);
332
- if (verbose)
333
- process.stderr.write(text);
334
- });
335
- proc.on("close", (code) => {
336
- resolve({
337
- stdout: stdoutChunks.join(""),
338
- stderr: stderrChunks.join(""),
339
- exitCode: code ?? 1
340
- });
341
- });
342
- proc.on("error", (err) => {
343
- stderrChunks.push(err.message);
344
- resolve({
345
- stdout: stdoutChunks.join(""),
346
- stderr: stderrChunks.join(""),
347
- exitCode: 1
348
- });
349
- });
217
+ const proc = spawn(command, args, {
218
+ stdio: ["pipe", "pipe", "pipe"]
350
219
  });
220
+ proc.stdin?.write(stdinContent);
221
+ proc.stdin?.end();
222
+ return collectOutput(proc, verbose);
351
223
  }
352
224
 
353
225
  // src/core/onepassword.ts
@@ -432,47 +304,369 @@ async function createSecureNote(options) {
432
304
  vaultId: item.vault?.id ?? "",
433
305
  fieldIds
434
306
  };
435
- } catch (error) {
436
- const message = error instanceof Error ? error.message : String(error);
437
- throw errors.itemCreateFailed(message);
307
+ } catch (error) {
308
+ const message = error instanceof Error ? error.message : String(error);
309
+ throw errors.itemCreateFailed(message);
310
+ }
311
+ }
312
+ async function editSecureNote(options) {
313
+ const { vault, title, fields, secret, verbose, itemId } = options;
314
+ const template = buildItemTemplate(title, vault, fields, secret);
315
+ const json = JSON.stringify(template);
316
+ try {
317
+ const result = await execWithStdin("op", ["item", "edit", itemId, "--format", "json"], {
318
+ stdin: json,
319
+ verbose
320
+ });
321
+ if (result.exitCode !== 0) {
322
+ throw new Error(result.stderr || "Failed to edit item");
323
+ }
324
+ const item = JSON.parse(result.stdout);
325
+ const fieldIds = {};
326
+ for (const field of item.fields ?? []) {
327
+ if (field.label && field.id) {
328
+ fieldIds[field.label] = field.id;
329
+ }
330
+ }
331
+ return {
332
+ id: item.id,
333
+ title: item.title,
334
+ vault: item.vault?.name ?? vault,
335
+ vaultId: item.vault?.id ?? "",
336
+ fieldIds
337
+ };
338
+ } catch (error) {
339
+ const message = error instanceof Error ? error.message : String(error);
340
+ throw errors.itemEditFailed(message);
341
+ }
342
+ }
343
+
344
+ // src/core/auth.ts
345
+ async function ensureOpAuthenticated(options) {
346
+ const { verbose } = options;
347
+ const authSpinner = p.spinner();
348
+ authSpinner.start("Checking 1Password CLI...");
349
+ const opInstalled = await checkOpCli({ verbose });
350
+ if (!opInstalled) {
351
+ authSpinner.stop("1Password CLI not found");
352
+ throw errors.opCliNotInstalled();
353
+ }
354
+ let signedIn = await checkSignedIn({ verbose });
355
+ if (!signedIn) {
356
+ authSpinner.message("Signing in to 1Password...");
357
+ const signInSuccess = await signIn({ verbose });
358
+ if (!signInSuccess) {
359
+ authSpinner.stop();
360
+ throw errors.opSigninFailed();
361
+ }
362
+ signedIn = await checkSignedIn({ verbose });
363
+ if (!signedIn) {
364
+ authSpinner.stop();
365
+ throw errors.opNotSignedIn();
366
+ }
367
+ }
368
+ authSpinner.stop("1Password CLI ready");
369
+ }
370
+
371
+ // src/core/env-parser.ts
372
+ import { readFile } from "node:fs/promises";
373
+
374
+ // src/core/constants.ts
375
+ var HEADER_SEPARATOR = `# ${"=".repeat(75)}`;
376
+
377
+ // src/core/env-parser.ts
378
+ function stripHeaders(content) {
379
+ const lines = content.split(`
380
+ `);
381
+ const result = [];
382
+ let inHeader = false;
383
+ for (const line of lines) {
384
+ const trimmed = line.trim();
385
+ if (trimmed === HEADER_SEPARATOR) {
386
+ if (!inHeader) {
387
+ inHeader = true;
388
+ } else {
389
+ inHeader = false;
390
+ }
391
+ continue;
392
+ }
393
+ if (!inHeader) {
394
+ result.push(line);
395
+ }
396
+ }
397
+ while (result.length > 0 && result[0]?.trim() === "") {
398
+ result.shift();
399
+ }
400
+ return result.join(`
401
+ `);
402
+ }
403
+ function parseValue(raw) {
404
+ const trimmed = raw.trim();
405
+ if (trimmed.startsWith('"')) {
406
+ const endQuote = trimmed.indexOf('"', 1);
407
+ if (endQuote !== -1) {
408
+ return trimmed.slice(1, endQuote);
409
+ }
410
+ }
411
+ if (trimmed.startsWith("'")) {
412
+ const endQuote = trimmed.indexOf("'", 1);
413
+ if (endQuote !== -1) {
414
+ return trimmed.slice(1, endQuote);
415
+ }
416
+ }
417
+ const parts = trimmed.split(/\s+#/);
418
+ return (parts[0] ?? trimmed).trim();
419
+ }
420
+ function stripBom(content) {
421
+ if (content.charCodeAt(0) === 65279) {
422
+ return content.slice(1);
423
+ }
424
+ return content;
425
+ }
426
+ async function parseEnvFile(filePath) {
427
+ let rawContent;
428
+ try {
429
+ rawContent = await readFile(filePath, "utf-8");
430
+ } catch {
431
+ throw errors.envFileNotFound(filePath);
432
+ }
433
+ const content = stripHeaders(stripBom(rawContent));
434
+ const rawLines = content.split(`
435
+ `);
436
+ const variables = [];
437
+ const lines = [];
438
+ const parseErrors = [];
439
+ let currentComment = "";
440
+ for (let i = 0;i < rawLines.length; i++) {
441
+ const line = rawLines[i] ?? "";
442
+ const trimmed = line.trim();
443
+ const lineNumber = i + 1;
444
+ if (!trimmed) {
445
+ lines.push({ type: "empty" });
446
+ currentComment = "";
447
+ continue;
448
+ }
449
+ if (trimmed.startsWith("#")) {
450
+ lines.push({ type: "comment", content: line });
451
+ currentComment = trimmed.slice(1).trim();
452
+ continue;
453
+ }
454
+ const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
455
+ if (match?.[1]) {
456
+ const key = match[1];
457
+ const rawValue = match[2] ?? "";
458
+ const value = parseValue(rawValue);
459
+ variables.push({
460
+ key,
461
+ value,
462
+ comment: currentComment || undefined,
463
+ line: lineNumber
464
+ });
465
+ lines.push({ type: "variable", key, value });
466
+ currentComment = "";
467
+ } else if (trimmed.includes("=")) {
468
+ parseErrors.push(`Line ${lineNumber}: Invalid variable name`);
469
+ }
470
+ }
471
+ return { variables, lines, errors: parseErrors };
472
+ }
473
+ function validateParseResult(result, filePath) {
474
+ if (result.variables.length === 0) {
475
+ throw errors.envFileEmpty(filePath);
476
+ }
477
+ }
478
+
479
+ // src/core/template-generator.ts
480
+ import { writeFileSync as writeFileSync2 } from "node:fs";
481
+
482
+ // src/lib/update.ts
483
+ import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
484
+ import { homedir } from "node:os";
485
+ import { join } from "node:path";
486
+
487
+ // src/lib/package-manager.ts
488
+ var UPDATE_COMMANDS = {
489
+ homebrew: "brew upgrade tolgamorf/tap/env2op-cli",
490
+ npm: "npm update -g @tolgamorf/env2op-cli",
491
+ bun: "bun update -g @tolgamorf/env2op-cli",
492
+ pnpm: "pnpm update -g @tolgamorf/env2op-cli",
493
+ unknown: "npm update -g @tolgamorf/env2op-cli"
494
+ };
495
+ var DISPLAY_NAMES = {
496
+ homebrew: "Homebrew",
497
+ npm: "npm",
498
+ bun: "Bun",
499
+ pnpm: "pnpm",
500
+ unknown: "npm (default)"
501
+ };
502
+ function detectFromPath() {
503
+ const binPath = process.argv[1] ?? "";
504
+ if (binPath.includes("/Cellar/") || binPath.includes("/homebrew/") || binPath.includes("/opt/homebrew/") || binPath.includes("/home/linuxbrew/")) {
505
+ return "homebrew";
506
+ }
507
+ if (binPath.includes("/.bun/")) {
508
+ return "bun";
509
+ }
510
+ if (binPath.includes("/pnpm/") || binPath.includes("/.pnpm/")) {
511
+ return "pnpm";
512
+ }
513
+ if (binPath.includes("/node_modules/")) {
514
+ return "npm";
515
+ }
516
+ return null;
517
+ }
518
+ async function detectFromCommands() {
519
+ const brewResult = await exec("brew", ["list", "env2op-cli"], { verbose: false });
520
+ if (brewResult.exitCode === 0) {
521
+ return "homebrew";
522
+ }
523
+ return "npm";
524
+ }
525
+ async function detectPackageManager() {
526
+ const fromPath = detectFromPath();
527
+ if (fromPath) {
528
+ return {
529
+ type: fromPath,
530
+ updateCommand: UPDATE_COMMANDS[fromPath],
531
+ displayName: DISPLAY_NAMES[fromPath]
532
+ };
533
+ }
534
+ const fromCommands = await detectFromCommands();
535
+ return {
536
+ type: fromCommands,
537
+ updateCommand: UPDATE_COMMANDS[fromCommands],
538
+ displayName: DISPLAY_NAMES[fromCommands]
539
+ };
540
+ }
541
+
542
+ // src/lib/update.ts
543
+ var CACHE_DIR = join(homedir(), ".env2op");
544
+ var CACHE_FILE = join(CACHE_DIR, "update-check.json");
545
+ var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
546
+ function getCliVersion() {
547
+ try {
548
+ const pkg = require_package();
549
+ return pkg.version ?? "0.0.0";
550
+ } catch {
551
+ return "0.0.0";
552
+ }
553
+ }
554
+ function loadCache() {
555
+ try {
556
+ if (existsSync(CACHE_FILE)) {
557
+ const content = readFileSync(CACHE_FILE, "utf-8");
558
+ return JSON.parse(content);
559
+ }
560
+ } catch {}
561
+ return { lastCheck: 0, latestVersion: null };
562
+ }
563
+ function saveCache(cache) {
564
+ try {
565
+ if (!existsSync(CACHE_DIR)) {
566
+ mkdirSync(CACHE_DIR, { recursive: true });
567
+ }
568
+ writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2));
569
+ } catch {}
570
+ }
571
+ function shouldCheckForUpdate(cache) {
572
+ const now = Date.now();
573
+ return now - cache.lastCheck > CHECK_INTERVAL_MS;
574
+ }
575
+ async function fetchLatestVersion() {
576
+ try {
577
+ const response = await fetch("https://registry.npmjs.org/@tolgamorf/env2op-cli/latest");
578
+ if (!response.ok)
579
+ return null;
580
+ const data = await response.json();
581
+ return data.version ?? null;
582
+ } catch {
583
+ return null;
584
+ }
585
+ }
586
+ function compareVersions(v1, v2) {
587
+ const parts1 = v1.split(".").map(Number);
588
+ const parts2 = v2.split(".").map(Number);
589
+ for (let i = 0;i < 3; i++) {
590
+ const p1 = parts1[i] || 0;
591
+ const p2 = parts2[i] || 0;
592
+ if (p1 < p2)
593
+ return -1;
594
+ if (p1 > p2)
595
+ return 1;
596
+ }
597
+ return 0;
598
+ }
599
+ async function checkForUpdate(forceCheck = false) {
600
+ const currentVersion = getCliVersion();
601
+ const cache = loadCache();
602
+ if (!forceCheck && !shouldCheckForUpdate(cache) && cache.latestVersion) {
603
+ const updateAvailable2 = compareVersions(currentVersion, cache.latestVersion) < 0;
604
+ const isSkipped2 = cache.skipVersion === cache.latestVersion;
605
+ return {
606
+ currentVersion,
607
+ latestVersion: cache.latestVersion,
608
+ updateAvailable: updateAvailable2,
609
+ isSkipped: isSkipped2,
610
+ fromCache: true
611
+ };
612
+ }
613
+ const latestVersion = await fetchLatestVersion();
614
+ saveCache({
615
+ ...cache,
616
+ lastCheck: Date.now(),
617
+ latestVersion
618
+ });
619
+ if (!latestVersion) {
620
+ return {
621
+ currentVersion,
622
+ latestVersion: null,
623
+ updateAvailable: false,
624
+ isSkipped: false,
625
+ fromCache: false
626
+ };
438
627
  }
628
+ const updateAvailable = compareVersions(currentVersion, latestVersion) < 0;
629
+ const isSkipped = cache.skipVersion === latestVersion;
630
+ return {
631
+ currentVersion,
632
+ latestVersion,
633
+ updateAvailable,
634
+ isSkipped,
635
+ fromCache: false
636
+ };
439
637
  }
440
- async function editSecureNote(options) {
441
- const { vault, title, fields, secret, verbose, itemId } = options;
442
- const template = buildItemTemplate(title, vault, fields, secret);
443
- const json = JSON.stringify(template);
638
+ async function performUpdate(pm) {
639
+ const packageManager = pm ?? await detectPackageManager();
444
640
  try {
445
- const result = await execWithStdin("op", ["item", "edit", itemId, "--format", "json"], {
446
- stdin: json,
447
- verbose
448
- });
641
+ const [command, ...args] = packageManager.updateCommand.split(" ");
642
+ const result = await exec(command, args, { verbose: false });
449
643
  if (result.exitCode !== 0) {
450
- throw new Error(result.stderr || "Failed to edit item");
451
- }
452
- const item = JSON.parse(result.stdout);
453
- const fieldIds = {};
454
- for (const field of item.fields ?? []) {
455
- if (field.label && field.id) {
456
- fieldIds[field.label] = field.id;
457
- }
644
+ return {
645
+ success: false,
646
+ error: result.stderr || `Command exited with code ${result.exitCode}`
647
+ };
458
648
  }
459
- return {
460
- id: item.id,
461
- title: item.title,
462
- vault: item.vault?.name ?? vault,
463
- vaultId: item.vault?.id ?? "",
464
- fieldIds
465
- };
649
+ return { success: true };
466
650
  } catch (error) {
467
651
  const message = error instanceof Error ? error.message : String(error);
468
- throw errors.itemEditFailed(message);
652
+ return { success: false, error: message };
469
653
  }
470
654
  }
655
+ function skipVersion(version) {
656
+ const cache = loadCache();
657
+ cache.skipVersion = version;
658
+ saveCache(cache);
659
+ }
660
+ async function maybeShowUpdateNotification(cliName, showNotification) {
661
+ try {
662
+ const result = await checkForUpdate();
663
+ if (result.updateAvailable && !result.isSkipped) {
664
+ showNotification(result, cliName);
665
+ }
666
+ } catch {}
667
+ }
471
668
 
472
669
  // src/core/template-generator.ts
473
- var import__package = __toESM(require_package(), 1);
474
- import { writeFileSync } from "node:fs";
475
- var SEPARATOR = "# ===========================================================================";
476
670
  function deriveEnvFileName(templateFileName) {
477
671
  if (templateFileName.endsWith(".tpl")) {
478
672
  return templateFileName.slice(0, -4);
@@ -488,7 +682,7 @@ function formatTimestamp() {
488
682
  function generateTemplateHeader(templateFileName) {
489
683
  const envFileName = deriveEnvFileName(templateFileName);
490
684
  return [
491
- SEPARATOR,
685
+ HEADER_SEPARATOR,
492
686
  `# ${templateFileName} — 1Password Secret References`,
493
687
  "#",
494
688
  "# This template contains references to secrets stored in 1Password.",
@@ -501,27 +695,27 @@ function generateTemplateHeader(templateFileName) {
501
695
  `# op run --env-file ${templateFileName} -- npm start`,
502
696
  "#",
503
697
  `# Pushed: ${formatTimestamp()}`,
504
- `# Generated by env2op v${import__package.default.version}`,
698
+ `# Generated by env2op v${getCliVersion()}`,
505
699
  "# https://github.com/tolgamorf/env2op-cli",
506
- SEPARATOR,
700
+ HEADER_SEPARATOR,
507
701
  ""
508
702
  ];
509
703
  }
510
704
  function generateEnvHeader(envFileName) {
511
705
  const templateFileName = deriveTemplateFileName(envFileName);
512
706
  return [
513
- SEPARATOR,
707
+ HEADER_SEPARATOR,
514
708
  `# ${envFileName} — Environment Variables`,
515
709
  "#",
516
710
  "# WARNING: This file contains sensitive values. Do not commit to git!",
517
711
  "#",
518
712
  `# To push updates to 1Password and generate ${templateFileName}:`,
519
- `# env2op ${envFileName} <vault> "<item_name>"`,
713
+ `# env2op ${envFileName} "<vault>" "<item_name>"`,
520
714
  "#",
521
715
  `# Pulled: ${formatTimestamp()}`,
522
- `# Generated by op2env v${import__package.default.version}`,
716
+ `# Generated by op2env v${getCliVersion()}`,
523
717
  "# https://github.com/tolgamorf/env2op-cli",
524
- SEPARATOR,
718
+ HEADER_SEPARATOR,
525
719
  "",
526
720
  ""
527
721
  ];
@@ -549,7 +743,7 @@ function generateTemplateContent(options, templateFileName) {
549
743
  `;
550
744
  }
551
745
  function writeTemplate(content, outputPath) {
552
- writeFileSync(outputPath, content, "utf-8");
746
+ writeFileSync2(outputPath, content, "utf-8");
553
747
  }
554
748
  function generateUsageInstructions(templatePath) {
555
749
  return ["Usage:", ` op2env ${templatePath}`, ` op run --env-file ${templatePath} -- npm start`].join(`
@@ -557,7 +751,7 @@ function generateUsageInstructions(templatePath) {
557
751
  }
558
752
 
559
753
  // src/utils/logger.ts
560
- import * as p from "@clack/prompts";
754
+ import * as p2 from "@clack/prompts";
561
755
  import pc2 from "picocolors";
562
756
  var symbols = {
563
757
  success: pc2.green("✓"),
@@ -570,29 +764,29 @@ var symbols = {
570
764
  var logger = {
571
765
  intro(name, version, dryRun = false) {
572
766
  const label = dryRun ? pc2.bgYellow(pc2.black(` ${name} v${version} [DRY RUN] `)) : pc2.bgCyan(pc2.black(` ${name} v${version} `));
573
- p.intro(label);
767
+ p2.intro(label);
574
768
  },
575
769
  section(title) {
576
770
  console.log(`
577
771
  ${pc2.bold(pc2.underline(title))}`);
578
772
  },
579
773
  success(message) {
580
- p.log.success(message);
774
+ p2.log.success(message);
581
775
  },
582
776
  error(message) {
583
- p.log.error(message);
777
+ p2.log.error(message);
584
778
  },
585
779
  warn(message) {
586
- p.log.warn(message);
780
+ p2.log.warn(message);
587
781
  },
588
782
  info(message) {
589
- p.log.info(message);
783
+ p2.log.info(message);
590
784
  },
591
785
  step(message) {
592
- p.log.step(message);
786
+ p2.log.step(message);
593
787
  },
594
788
  message(message) {
595
- p.log.message(message);
789
+ p2.log.message(message);
596
790
  },
597
791
  keyValue(key, value, indent = 2) {
598
792
  console.log(`${" ".repeat(indent)}${pc2.dim(key)}: ${pc2.cyan(value)}`);
@@ -607,16 +801,16 @@ ${pc2.bold(pc2.underline(title))}`);
607
801
  console.log(`${pc2.yellow("[DRY RUN]")} ${message}`);
608
802
  },
609
803
  spinner() {
610
- return p.spinner();
804
+ return p2.spinner();
611
805
  },
612
806
  outro(message) {
613
- p.outro(pc2.green(message));
807
+ p2.outro(pc2.green(message));
614
808
  },
615
809
  cancel(message) {
616
- p.cancel(message);
810
+ p2.cancel(message);
617
811
  },
618
812
  note(message, title) {
619
- p.note(message, title);
813
+ p2.note(message, title);
620
814
  },
621
815
  formatFields(fields, max = 3) {
622
816
  if (fields.length <= max) {
@@ -626,6 +820,28 @@ ${pc2.bold(pc2.underline(title))}`);
626
820
  }
627
821
  };
628
822
 
823
+ // src/utils/error-handler.ts
824
+ function handleCommandError(error) {
825
+ if (error instanceof Env2OpError) {
826
+ logger.error(error.message);
827
+ if (error.suggestion) {
828
+ logger.info(`Suggestion: ${error.suggestion}`);
829
+ }
830
+ process.exit(1);
831
+ }
832
+ throw error;
833
+ }
834
+
835
+ // src/utils/prompts.ts
836
+ import * as p3 from "@clack/prompts";
837
+ async function confirmOrExit(message) {
838
+ const confirmed = await p3.confirm({ message });
839
+ if (p3.isCancel(confirmed) || !confirmed) {
840
+ logger.cancel("Operation cancelled");
841
+ process.exit(0);
842
+ }
843
+ }
844
+
629
845
  // src/utils/timing.ts
630
846
  import { setTimeout } from "node:timers/promises";
631
847
  var MIN_SPINNER_TIME = 500;
@@ -644,38 +860,16 @@ function deriveOutputPath(templatePath) {
644
860
  async function runInject(options) {
645
861
  const { templateFile, output, dryRun, force, verbose } = options;
646
862
  const outputPath = output ?? deriveOutputPath(templateFile);
647
- const pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
648
- logger.intro("op2env", pkg2.version, dryRun);
863
+ logger.intro("op2env", getCliVersion(), dryRun);
649
864
  try {
650
- if (!existsSync(templateFile)) {
651
- throw new Env2OpError(`Template file not found: ${templateFile}`, "TEMPLATE_NOT_FOUND", "Ensure the file exists and the path is correct");
865
+ if (!existsSync2(templateFile)) {
866
+ throw errors.templateNotFound(templateFile);
652
867
  }
653
868
  logger.success(`Found template: ${basename(templateFile)}`);
654
869
  if (!dryRun) {
655
- const authSpinner = p2.spinner();
656
- authSpinner.start("Checking 1Password CLI...");
657
- const opInstalled = await checkOpCli({ verbose });
658
- if (!opInstalled) {
659
- authSpinner.stop("1Password CLI not found");
660
- throw new Env2OpError("1Password CLI (op) is not installed", "OP_CLI_NOT_INSTALLED", "Install from https://1password.com/downloads/command-line/");
661
- }
662
- let signedIn = await checkSignedIn({ verbose });
663
- if (!signedIn) {
664
- authSpinner.message("Signing in to 1Password...");
665
- const signInSuccess = await signIn({ verbose });
666
- if (!signInSuccess) {
667
- authSpinner.stop();
668
- throw new Env2OpError("Failed to sign in to 1Password CLI", "OP_SIGNIN_FAILED", 'Try running "op signin" manually');
669
- }
670
- signedIn = await checkSignedIn({ verbose });
671
- if (!signedIn) {
672
- authSpinner.stop();
673
- throw new Env2OpError("Not signed in to 1Password CLI", "OP_NOT_SIGNED_IN", 'Run "op signin" to authenticate');
674
- }
675
- }
676
- authSpinner.stop("1Password CLI ready");
870
+ await ensureOpAuthenticated({ verbose });
677
871
  }
678
- const outputExists = existsSync(outputPath);
872
+ const outputExists = existsSync2(outputPath);
679
873
  if (dryRun) {
680
874
  if (outputExists) {
681
875
  logger.warn(`Would overwrite: ${outputPath}`);
@@ -686,13 +880,7 @@ async function runInject(options) {
686
880
  return;
687
881
  }
688
882
  if (outputExists && !force) {
689
- const shouldOverwrite = await p2.confirm({
690
- message: `File "${outputPath}" already exists. Overwrite?`
691
- });
692
- if (p2.isCancel(shouldOverwrite) || !shouldOverwrite) {
693
- logger.cancel("Operation cancelled");
694
- process.exit(0);
695
- }
883
+ await confirmOrExit(`File "${outputPath}" already exists. Overwrite?`);
696
884
  }
697
885
  const spinner3 = verbose ? null : logger.spinner();
698
886
  spinner3?.start("Pulling secrets from 1Password...");
@@ -701,11 +889,11 @@ async function runInject(options) {
701
889
  if (result.exitCode !== 0) {
702
890
  throw new Error(result.stderr);
703
891
  }
704
- const rawContent = readFileSync(outputPath, "utf-8");
892
+ const rawContent = readFileSync2(outputPath, "utf-8");
705
893
  const envContent = stripHeaders(rawContent);
706
894
  const header = generateEnvHeader(basename(outputPath)).join(`
707
895
  `);
708
- writeFileSync2(outputPath, header + envContent, "utf-8");
896
+ writeFileSync3(outputPath, header + envContent, "utf-8");
709
897
  const varCount = envContent.split(`
710
898
  `).filter((line) => line.trim() && !line.trim().startsWith("#")).length;
711
899
  const stopMessage = `Generated ${basename(outputPath)} — ${varCount} variable${varCount === 1 ? "" : "s"}`;
@@ -718,204 +906,20 @@ async function runInject(options) {
718
906
  spinner3?.stop("Failed to pull secrets");
719
907
  const stderr = error?.stderr;
720
908
  const message = stderr || (error instanceof Error ? error.message : String(error));
721
- throw new Env2OpError("Failed to pull secrets from 1Password", "INJECT_FAILED", message);
909
+ throw errors.injectFailed(message);
722
910
  }
723
911
  logger.outro("Done! Your .env file is ready");
724
912
  } catch (error) {
725
- if (error instanceof Env2OpError) {
726
- logger.error(error.message);
727
- if (error.suggestion) {
728
- logger.info(`Suggestion: ${error.suggestion}`);
729
- }
730
- process.exit(1);
731
- }
732
- throw error;
913
+ handleCommandError(error);
733
914
  }
734
915
  }
735
916
 
736
917
  // src/commands/update.ts
737
- import * as p4 from "@clack/prompts";
918
+ import * as p5 from "@clack/prompts";
738
919
  import pc4 from "picocolors";
739
920
 
740
- // src/lib/package-manager.ts
741
- var UPDATE_COMMANDS = {
742
- homebrew: "brew upgrade tolgamorf/tap/env2op-cli",
743
- npm: "npm update -g @tolgamorf/env2op-cli",
744
- bun: "bun update -g @tolgamorf/env2op-cli",
745
- pnpm: "pnpm update -g @tolgamorf/env2op-cli",
746
- unknown: "npm update -g @tolgamorf/env2op-cli"
747
- };
748
- var DISPLAY_NAMES = {
749
- homebrew: "Homebrew",
750
- npm: "npm",
751
- bun: "Bun",
752
- pnpm: "pnpm",
753
- unknown: "npm (default)"
754
- };
755
- function detectFromPath() {
756
- const binPath = process.argv[1] ?? "";
757
- if (binPath.includes("/Cellar/") || binPath.includes("/homebrew/") || binPath.includes("/opt/homebrew/") || binPath.includes("/home/linuxbrew/")) {
758
- return "homebrew";
759
- }
760
- if (binPath.includes("/.bun/")) {
761
- return "bun";
762
- }
763
- if (binPath.includes("/pnpm/") || binPath.includes("/.pnpm/")) {
764
- return "pnpm";
765
- }
766
- if (binPath.includes("/node_modules/")) {
767
- return "npm";
768
- }
769
- return null;
770
- }
771
- async function detectFromCommands() {
772
- const brewResult = await exec("brew", ["list", "env2op-cli"], { verbose: false });
773
- if (brewResult.exitCode === 0) {
774
- return "homebrew";
775
- }
776
- return "npm";
777
- }
778
- async function detectPackageManager() {
779
- const fromPath = detectFromPath();
780
- if (fromPath) {
781
- return {
782
- type: fromPath,
783
- updateCommand: UPDATE_COMMANDS[fromPath],
784
- displayName: DISPLAY_NAMES[fromPath]
785
- };
786
- }
787
- const fromCommands = await detectFromCommands();
788
- return {
789
- type: fromCommands,
790
- updateCommand: UPDATE_COMMANDS[fromCommands],
791
- displayName: DISPLAY_NAMES[fromCommands]
792
- };
793
- }
794
-
795
- // src/lib/update.ts
796
- import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
797
- import { homedir } from "node:os";
798
- import { join } from "node:path";
799
- var CACHE_DIR = join(homedir(), ".env2op");
800
- var CACHE_FILE = join(CACHE_DIR, "update-check.json");
801
- var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
802
- function getCliVersion() {
803
- try {
804
- const pkg2 = require_package();
805
- return pkg2.version ?? "0.0.0";
806
- } catch {
807
- return "0.0.0";
808
- }
809
- }
810
- function loadCache() {
811
- try {
812
- if (existsSync2(CACHE_FILE)) {
813
- const content = readFileSync2(CACHE_FILE, "utf-8");
814
- return JSON.parse(content);
815
- }
816
- } catch {}
817
- return { lastCheck: 0, latestVersion: null };
818
- }
819
- function saveCache(cache) {
820
- try {
821
- if (!existsSync2(CACHE_DIR)) {
822
- mkdirSync(CACHE_DIR, { recursive: true });
823
- }
824
- writeFileSync3(CACHE_FILE, JSON.stringify(cache, null, 2));
825
- } catch {}
826
- }
827
- function shouldCheckForUpdate(cache) {
828
- const now = Date.now();
829
- return now - cache.lastCheck > CHECK_INTERVAL_MS;
830
- }
831
- async function fetchLatestVersion() {
832
- try {
833
- const response = await fetch("https://registry.npmjs.org/@tolgamorf/env2op-cli/latest");
834
- if (!response.ok)
835
- return null;
836
- const data = await response.json();
837
- return data.version ?? null;
838
- } catch {
839
- return null;
840
- }
841
- }
842
- function compareVersions(v1, v2) {
843
- const parts1 = v1.split(".").map(Number);
844
- const parts2 = v2.split(".").map(Number);
845
- for (let i = 0;i < 3; i++) {
846
- const p1 = parts1[i] || 0;
847
- const p22 = parts2[i] || 0;
848
- if (p1 < p22)
849
- return -1;
850
- if (p1 > p22)
851
- return 1;
852
- }
853
- return 0;
854
- }
855
- async function checkForUpdate(forceCheck = false) {
856
- const currentVersion = getCliVersion();
857
- const cache = loadCache();
858
- if (!forceCheck && !shouldCheckForUpdate(cache) && cache.latestVersion) {
859
- const updateAvailable2 = compareVersions(currentVersion, cache.latestVersion) < 0;
860
- const isSkipped2 = cache.skipVersion === cache.latestVersion;
861
- return {
862
- currentVersion,
863
- latestVersion: cache.latestVersion,
864
- updateAvailable: updateAvailable2,
865
- isSkipped: isSkipped2,
866
- fromCache: true
867
- };
868
- }
869
- const latestVersion = await fetchLatestVersion();
870
- saveCache({
871
- ...cache,
872
- lastCheck: Date.now(),
873
- latestVersion
874
- });
875
- if (!latestVersion) {
876
- return {
877
- currentVersion,
878
- latestVersion: null,
879
- updateAvailable: false,
880
- isSkipped: false,
881
- fromCache: false
882
- };
883
- }
884
- const updateAvailable = compareVersions(currentVersion, latestVersion) < 0;
885
- const isSkipped = cache.skipVersion === latestVersion;
886
- return {
887
- currentVersion,
888
- latestVersion,
889
- updateAvailable,
890
- isSkipped,
891
- fromCache: false
892
- };
893
- }
894
- async function performUpdate(pm) {
895
- const packageManager = pm ?? await detectPackageManager();
896
- try {
897
- const [command, ...args] = packageManager.updateCommand.split(" ");
898
- const result = await exec(command, args, { verbose: false });
899
- if (result.exitCode !== 0) {
900
- return {
901
- success: false,
902
- error: result.stderr || `Command exited with code ${result.exitCode}`
903
- };
904
- }
905
- return { success: true };
906
- } catch (error) {
907
- const message = error instanceof Error ? error.message : String(error);
908
- return { success: false, error: message };
909
- }
910
- }
911
- function skipVersion(version) {
912
- const cache = loadCache();
913
- cache.skipVersion = version;
914
- saveCache(cache);
915
- }
916
-
917
921
  // src/lib/update-prompts.ts
918
- import * as p3 from "@clack/prompts";
922
+ import * as p4 from "@clack/prompts";
919
923
  import pc3 from "picocolors";
920
924
  var S_BAR_START = "┌";
921
925
  var S_BAR_END = "└";
@@ -925,7 +929,7 @@ function showUpdateNotification(result, cliName = "env2op") {
925
929
  console.log(`${pc3.gray(S_BAR_END)}${pc3.gray("─")} Run ${pc3.cyan(`'${cliName} update'`)} to update`);
926
930
  }
927
931
  async function askToUpdate(result) {
928
- const response = await p3.select({
932
+ const response = await p4.select({
929
933
  message: "Would you like to update?",
930
934
  options: [
931
935
  { value: "update", label: "Update now", hint: "Download and install the latest version" },
@@ -933,16 +937,16 @@ async function askToUpdate(result) {
933
937
  { value: "skip", label: "Skip this version", hint: `Don't ask about ${result.latestVersion} again` }
934
938
  ]
935
939
  });
936
- if (p3.isCancel(response)) {
940
+ if (p4.isCancel(response)) {
937
941
  return "later";
938
942
  }
939
943
  return response;
940
944
  }
941
945
  function showUpdateAvailable(result) {
942
- p3.log.success(`Update available: ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
946
+ p4.log.success(`Update available: ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
943
947
  }
944
948
  function showUpToDate(currentVersion) {
945
- p3.log.success(`You're on the latest version ${pc3.green(`(${currentVersion})`)}`);
949
+ p4.log.success(`You're on the latest version ${pc3.green(`(${currentVersion})`)}`);
946
950
  }
947
951
  function showPackageManagerInfo(pm) {
948
952
  console.log();
@@ -951,21 +955,21 @@ function showPackageManagerInfo(pm) {
951
955
  console.log();
952
956
  }
953
957
  function showUpdateSuccess(newVersion) {
954
- p3.log.success(`Updated to version ${pc3.green(newVersion)}`);
955
- p3.log.info("Please restart to use the new version.");
958
+ p4.log.success(`Updated to version ${pc3.green(newVersion)}`);
959
+ p4.log.info("Please restart to use the new version.");
956
960
  }
957
961
  function showUpdateError(error, pm) {
958
962
  if (error) {
959
- p3.log.error(pc3.dim(error));
963
+ p4.log.error(pc3.dim(error));
960
964
  }
961
- p3.log.info(`Try running manually: ${pc3.cyan(pm.updateCommand)}`);
965
+ p4.log.info(`Try running manually: ${pc3.cyan(pm.updateCommand)}`);
962
966
  }
963
967
 
964
968
  // src/commands/update.ts
965
969
  async function runUpdate(options) {
966
970
  const { force = false, cliName = "env2op" } = options;
967
- p4.intro(pc4.bgCyan(pc4.black(` ${cliName} update `)));
968
- const spinner4 = p4.spinner();
971
+ p5.intro(pc4.bgCyan(pc4.black(` ${cliName} update `)));
972
+ const spinner4 = p5.spinner();
969
973
  spinner4.start("Checking for updates...");
970
974
  const result = await checkForUpdate(true);
971
975
  spinner4.stop("Checked for updates");
@@ -980,15 +984,15 @@ async function runUpdate(options) {
980
984
  const choice = await askToUpdate(result);
981
985
  if (choice === "skip") {
982
986
  skipVersion(result.latestVersion);
983
- p4.log.info(`Skipped version ${result.latestVersion}`);
987
+ p5.log.info(`Skipped version ${result.latestVersion}`);
984
988
  return;
985
989
  }
986
990
  if (choice === "later") {
987
- p4.log.info("Update postponed");
991
+ p5.log.info("Update postponed");
988
992
  return;
989
993
  }
990
994
  }
991
- const updateSpinner = p4.spinner();
995
+ const updateSpinner = p5.spinner();
992
996
  updateSpinner.start(`Updating to ${result.latestVersion}...`);
993
997
  const updateResult = await performUpdate(pm);
994
998
  if (updateResult.success) {
@@ -1001,35 +1005,39 @@ async function runUpdate(options) {
1001
1005
  }
1002
1006
  }
1003
1007
 
1004
- // src/op2env-cli.ts
1005
- var pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
1006
- var args = process.argv.slice(2);
1007
- var flags = new Set;
1008
- var positional = [];
1009
- var options = {};
1010
- for (let i = 0;i < args.length; i++) {
1011
- const arg = args[i];
1012
- if (arg === "-o" || arg === "--output") {
1013
- const next = args[i + 1];
1014
- if (next && !next.startsWith("-")) {
1015
- options.output = next;
1016
- i++;
1017
- }
1018
- } else if (arg.startsWith("--")) {
1019
- flags.add(arg.slice(2));
1020
- } else if (arg.startsWith("-")) {
1021
- for (const char of arg.slice(1)) {
1022
- flags.add(char);
1008
+ // src/utils/args.ts
1009
+ function parseArgs(args) {
1010
+ const flags = new Set;
1011
+ const positional = [];
1012
+ const options = {};
1013
+ for (let i = 0;i < args.length; i++) {
1014
+ const arg = args[i];
1015
+ if (arg === "-o" || arg === "--output") {
1016
+ const next = args[i + 1];
1017
+ if (next && !next.startsWith("-")) {
1018
+ options.output = next;
1019
+ i++;
1020
+ }
1021
+ } else if (arg.startsWith("--")) {
1022
+ flags.add(arg.slice(2));
1023
+ } else if (arg.startsWith("-")) {
1024
+ for (const char of arg.slice(1)) {
1025
+ flags.add(char);
1026
+ }
1027
+ } else {
1028
+ positional.push(arg);
1023
1029
  }
1024
- } else {
1025
- positional.push(arg);
1026
1030
  }
1031
+ return { flags, positional, options };
1027
1032
  }
1033
+
1034
+ // src/op2env-cli.ts
1035
+ var { flags, positional, options } = parseArgs(process.argv.slice(2));
1028
1036
  var hasHelp = flags.has("h") || flags.has("help");
1029
1037
  var hasVersion = flags.has("v") || flags.has("version");
1030
1038
  var hasUpdate = flags.has("update");
1031
1039
  if (hasVersion) {
1032
- console.log(pkg2.version);
1040
+ console.log(getCliVersion());
1033
1041
  process.exit(0);
1034
1042
  }
1035
1043
  if (hasUpdate) {
@@ -1052,15 +1060,10 @@ await runInject({
1052
1060
  force: flags.has("f") || flags.has("force"),
1053
1061
  verbose: flags.has("verbose")
1054
1062
  });
1055
- try {
1056
- const updateResult = await checkForUpdate();
1057
- if (updateResult.updateAvailable && !updateResult.isSkipped) {
1058
- showUpdateNotification(updateResult, "op2env");
1059
- }
1060
- } catch {}
1063
+ await maybeShowUpdateNotification("op2env", showUpdateNotification);
1061
1064
  function showHelp() {
1062
1065
  const name = pc5.bold(pc5.cyan("op2env"));
1063
- const version = pc5.dim(`v${pkg2.version}`);
1066
+ const version = pc5.dim(`v${getCliVersion()}`);
1064
1067
  console.log(`
1065
1068
  ${name} ${version}
1066
1069
  Pull secrets from 1Password to generate .env files