@tolgamorf/env2op-cli 0.2.2 → 0.2.4

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
@@ -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.4",
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,11 +105,11 @@ var require_package = __commonJS((exports, module) => {
105
105
  import pc5 from "picocolors";
106
106
 
107
107
  // src/commands/convert.ts
108
- import { basename, dirname, join } from "node:path";
109
- import * as p2 from "@clack/prompts";
108
+ import { basename, dirname, join as join2 } from "node:path";
109
+ import * as p4 from "@clack/prompts";
110
110
 
111
- // src/core/env-parser.ts
112
- import { readFile } from "node:fs/promises";
111
+ // src/core/auth.ts
112
+ import * as p from "@clack/prompts";
113
113
 
114
114
  // src/utils/errors.ts
115
115
  class Env2OpError extends Error {
@@ -142,117 +142,17 @@ var errors = {
142
142
  envFileEmpty: (path) => new Env2OpError(`No valid environment variables found in ${path}`, ErrorCodes.ENV_FILE_EMPTY, "Ensure the file contains KEY=value pairs"),
143
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
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'),
145
146
  vaultNotFound: (vault) => new Env2OpError(`Vault not found: ${vault}`, ErrorCodes.VAULT_NOT_FOUND, 'Run "op vault list" to see available vaults'),
146
147
  vaultCreateFailed: (message) => new Env2OpError(`Failed to create vault: ${message}`, ErrorCodes.VAULT_CREATE_FAILED),
147
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"),
148
149
  itemCreateFailed: (message) => new Env2OpError(`Failed to create 1Password item: ${message}`, ErrorCodes.ITEM_CREATE_FAILED),
149
150
  itemEditFailed: (message) => new Env2OpError(`Failed to edit 1Password item: ${message}`, ErrorCodes.ITEM_EDIT_FAILED),
150
- 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)
151
154
  };
152
155
 
153
- // src/core/env-parser.ts
154
- var HEADER_SEPARATOR = "# ===========================================================================";
155
- function stripHeaders(content) {
156
- const lines = content.split(`
157
- `);
158
- const result = [];
159
- let inHeader = false;
160
- for (const line of lines) {
161
- const trimmed = line.trim();
162
- if (trimmed === HEADER_SEPARATOR) {
163
- if (!inHeader) {
164
- inHeader = true;
165
- } else {
166
- inHeader = false;
167
- }
168
- continue;
169
- }
170
- if (!inHeader) {
171
- result.push(line);
172
- }
173
- }
174
- while (result.length > 0 && result[0]?.trim() === "") {
175
- result.shift();
176
- }
177
- return result.join(`
178
- `);
179
- }
180
- function parseValue(raw) {
181
- const trimmed = raw.trim();
182
- if (trimmed.startsWith('"')) {
183
- const endQuote = trimmed.indexOf('"', 1);
184
- if (endQuote !== -1) {
185
- return trimmed.slice(1, endQuote);
186
- }
187
- }
188
- if (trimmed.startsWith("'")) {
189
- const endQuote = trimmed.indexOf("'", 1);
190
- if (endQuote !== -1) {
191
- return trimmed.slice(1, endQuote);
192
- }
193
- }
194
- const parts = trimmed.split(/\s+#/);
195
- return (parts[0] ?? trimmed).trim();
196
- }
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 {
208
- throw errors.envFileNotFound(filePath);
209
- }
210
- const content = stripHeaders(stripBom(rawContent));
211
- const rawLines = content.split(`
212
- `);
213
- const variables = [];
214
- const lines = [];
215
- const parseErrors = [];
216
- let currentComment = "";
217
- for (let i = 0;i < rawLines.length; i++) {
218
- const line = rawLines[i] ?? "";
219
- const trimmed = line.trim();
220
- const lineNumber = i + 1;
221
- if (!trimmed) {
222
- lines.push({ type: "empty" });
223
- currentComment = "";
224
- continue;
225
- }
226
- if (trimmed.startsWith("#")) {
227
- lines.push({ type: "comment", content: line });
228
- currentComment = trimmed.slice(1).trim();
229
- continue;
230
- }
231
- const match = trimmed.match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
232
- if (match?.[1]) {
233
- const key = match[1];
234
- const rawValue = match[2] ?? "";
235
- const value = parseValue(rawValue);
236
- variables.push({
237
- key,
238
- value,
239
- comment: currentComment || undefined,
240
- line: lineNumber
241
- });
242
- lines.push({ type: "variable", key, value });
243
- currentComment = "";
244
- } else if (trimmed.includes("=")) {
245
- parseErrors.push(`Line ${lineNumber}: Invalid variable name`);
246
- }
247
- }
248
- return { variables, lines, errors: parseErrors };
249
- }
250
- function validateParseResult(result, filePath) {
251
- if (result.variables.length === 0) {
252
- throw errors.envFileEmpty(filePath);
253
- }
254
- }
255
-
256
156
  // src/utils/shell.ts
257
157
  import { spawn } from "node:child_process";
258
158
  import pc from "picocolors";
@@ -262,16 +162,8 @@ function quoteArg(arg) {
262
162
  }
263
163
  return arg;
264
164
  }
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
- }
165
+ function collectOutput(proc, verbose) {
271
166
  return new Promise((resolve) => {
272
- const proc = spawn(command, args, {
273
- stdio: ["ignore", "pipe", "pipe"]
274
- });
275
167
  const stdoutChunks = [];
276
168
  const stderrChunks = [];
277
169
  proc.stdout?.on("data", (data) => {
@@ -305,48 +197,29 @@ async function exec(command, args = [], options = {}) {
305
197
  });
306
198
  });
307
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
+ }
308
211
  async function execWithStdin(command, args = [], options) {
309
212
  const { stdin: stdinContent, verbose = false } = options;
310
213
  if (verbose) {
311
214
  const fullCommand = `${command} ${args.map(quoteArg).join(" ")}`;
312
215
  console.log(pc.dim(`$ echo '...' | ${fullCommand}`));
313
216
  }
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);
333
- });
334
- proc.on("close", (code) => {
335
- resolve({
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
347
- });
348
- });
217
+ const proc = spawn(command, args, {
218
+ stdio: ["pipe", "pipe", "pipe"]
349
219
  });
220
+ proc.stdin?.write(stdinContent);
221
+ proc.stdin?.end();
222
+ return collectOutput(proc, verbose);
350
223
  }
351
224
 
352
225
  // src/core/onepassword.ts
@@ -431,47 +304,369 @@ async function createSecureNote(options) {
431
304
  vaultId: item.vault?.id ?? "",
432
305
  fieldIds
433
306
  };
434
- } catch (error) {
435
- const message = error instanceof Error ? error.message : String(error);
436
- 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
+ };
437
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
+ };
438
637
  }
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);
638
+ async function performUpdate(pm) {
639
+ const packageManager = pm ?? await detectPackageManager();
443
640
  try {
444
- const result = await execWithStdin("op", ["item", "edit", itemId, "--format", "json"], {
445
- stdin: json,
446
- verbose
447
- });
641
+ const [command, ...args] = packageManager.updateCommand.split(" ");
642
+ const result = await exec(command, args, { verbose: false });
448
643
  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
- }
644
+ return {
645
+ success: false,
646
+ error: result.stderr || `Command exited with code ${result.exitCode}`
647
+ };
457
648
  }
458
- return {
459
- id: item.id,
460
- title: item.title,
461
- vault: item.vault?.name ?? vault,
462
- vaultId: item.vault?.id ?? "",
463
- fieldIds
464
- };
649
+ return { success: true };
465
650
  } catch (error) {
466
651
  const message = error instanceof Error ? error.message : String(error);
467
- throw errors.itemEditFailed(message);
652
+ return { success: false, error: message };
468
653
  }
469
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
+ }
470
668
 
471
669
  // src/core/template-generator.ts
472
- var import__package = __toESM(require_package(), 1);
473
- import { writeFileSync } from "node:fs";
474
- var SEPARATOR = "# ===========================================================================";
475
670
  function deriveEnvFileName(templateFileName) {
476
671
  if (templateFileName.endsWith(".tpl")) {
477
672
  return templateFileName.slice(0, -4);
@@ -487,7 +682,7 @@ function formatTimestamp() {
487
682
  function generateTemplateHeader(templateFileName) {
488
683
  const envFileName = deriveEnvFileName(templateFileName);
489
684
  return [
490
- SEPARATOR,
685
+ HEADER_SEPARATOR,
491
686
  `# ${templateFileName} — 1Password Secret References`,
492
687
  "#",
493
688
  "# This template contains references to secrets stored in 1Password.",
@@ -500,27 +695,27 @@ function generateTemplateHeader(templateFileName) {
500
695
  `# op run --env-file ${templateFileName} -- npm start`,
501
696
  "#",
502
697
  `# Pushed: ${formatTimestamp()}`,
503
- `# Generated by env2op v${import__package.default.version}`,
698
+ `# Generated by env2op v${getCliVersion()}`,
504
699
  "# https://github.com/tolgamorf/env2op-cli",
505
- SEPARATOR,
700
+ HEADER_SEPARATOR,
506
701
  ""
507
702
  ];
508
703
  }
509
704
  function generateEnvHeader(envFileName) {
510
705
  const templateFileName = deriveTemplateFileName(envFileName);
511
706
  return [
512
- SEPARATOR,
707
+ HEADER_SEPARATOR,
513
708
  `# ${envFileName} — Environment Variables`,
514
709
  "#",
515
710
  "# WARNING: This file contains sensitive values. Do not commit to git!",
516
711
  "#",
517
712
  `# To push updates to 1Password and generate ${templateFileName}:`,
518
- `# env2op ${envFileName} <vault> "<item_name>"`,
713
+ `# env2op ${envFileName} "<vault>" "<item_name>"`,
519
714
  "#",
520
715
  `# Pulled: ${formatTimestamp()}`,
521
- `# Generated by op2env v${import__package.default.version}`,
716
+ `# Generated by op2env v${getCliVersion()}`,
522
717
  "# https://github.com/tolgamorf/env2op-cli",
523
- SEPARATOR,
718
+ HEADER_SEPARATOR,
524
719
  "",
525
720
  ""
526
721
  ];
@@ -548,7 +743,7 @@ function generateTemplateContent(options, templateFileName) {
548
743
  `;
549
744
  }
550
745
  function writeTemplate(content, outputPath) {
551
- writeFileSync(outputPath, content, "utf-8");
746
+ writeFileSync2(outputPath, content, "utf-8");
552
747
  }
553
748
  function generateUsageInstructions(templatePath) {
554
749
  return ["Usage:", ` op2env ${templatePath}`, ` op run --env-file ${templatePath} -- npm start`].join(`
@@ -556,7 +751,7 @@ function generateUsageInstructions(templatePath) {
556
751
  }
557
752
 
558
753
  // src/utils/logger.ts
559
- import * as p from "@clack/prompts";
754
+ import * as p2 from "@clack/prompts";
560
755
  import pc2 from "picocolors";
561
756
  var symbols = {
562
757
  success: pc2.green("✓"),
@@ -569,29 +764,29 @@ var symbols = {
569
764
  var logger = {
570
765
  intro(name, version, dryRun = false) {
571
766
  const label = dryRun ? pc2.bgYellow(pc2.black(` ${name} v${version} [DRY RUN] `)) : pc2.bgCyan(pc2.black(` ${name} v${version} `));
572
- p.intro(label);
767
+ p2.intro(label);
573
768
  },
574
769
  section(title) {
575
770
  console.log(`
576
771
  ${pc2.bold(pc2.underline(title))}`);
577
772
  },
578
773
  success(message) {
579
- p.log.success(message);
774
+ p2.log.success(message);
580
775
  },
581
776
  error(message) {
582
- p.log.error(message);
777
+ p2.log.error(message);
583
778
  },
584
779
  warn(message) {
585
- p.log.warn(message);
780
+ p2.log.warn(message);
586
781
  },
587
782
  info(message) {
588
- p.log.info(message);
783
+ p2.log.info(message);
589
784
  },
590
785
  step(message) {
591
- p.log.step(message);
786
+ p2.log.step(message);
592
787
  },
593
788
  message(message) {
594
- p.log.message(message);
789
+ p2.log.message(message);
595
790
  },
596
791
  keyValue(key, value, indent = 2) {
597
792
  console.log(`${" ".repeat(indent)}${pc2.dim(key)}: ${pc2.cyan(value)}`);
@@ -606,16 +801,16 @@ ${pc2.bold(pc2.underline(title))}`);
606
801
  console.log(`${pc2.yellow("[DRY RUN]")} ${message}`);
607
802
  },
608
803
  spinner() {
609
- return p.spinner();
804
+ return p2.spinner();
610
805
  },
611
806
  outro(message) {
612
- p.outro(pc2.green(message));
807
+ p2.outro(pc2.green(message));
613
808
  },
614
809
  cancel(message) {
615
- p.cancel(message);
810
+ p2.cancel(message);
616
811
  },
617
812
  note(message, title) {
618
- p.note(message, title);
813
+ p2.note(message, title);
619
814
  },
620
815
  formatFields(fields, max = 3) {
621
816
  if (fields.length <= max) {
@@ -625,6 +820,28 @@ ${pc2.bold(pc2.underline(title))}`);
625
820
  }
626
821
  };
627
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
+
628
845
  // src/utils/timing.ts
629
846
  import { setTimeout } from "node:timers/promises";
630
847
  var MIN_SPINNER_TIME = 500;
@@ -636,8 +853,7 @@ async function withMinTime(promise, minTime = MIN_SPINNER_TIME) {
636
853
  // src/commands/convert.ts
637
854
  async function runConvert(options) {
638
855
  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);
856
+ logger.intro("env2op", getCliVersion(), dryRun);
641
857
  try {
642
858
  const parseResult = await parseEnvFile(envFile);
643
859
  validateParseResult(parseResult, envFile);
@@ -662,29 +878,8 @@ async function runConvert(options) {
662
878
  logger.keyValue("Type", secret ? "password (hidden)" : "text (visible)");
663
879
  logger.keyValue("Fields", logger.formatFields(variables.map((v) => v.key)));
664
880
  } else {
665
- const authSpinner = p2.spinner();
666
- authSpinner.start("Checking 1Password CLI...");
667
- const opInstalled = await checkOpCli({ verbose });
668
- if (!opInstalled) {
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/");
671
- }
672
- let signedIn = await checkSignedIn({ verbose });
673
- if (!signedIn) {
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
- }
685
- }
686
- authSpinner.stop("1Password CLI ready");
687
- const vaultSpinner = p2.spinner();
881
+ await ensureOpAuthenticated({ verbose });
882
+ const vaultSpinner = p4.spinner();
688
883
  vaultSpinner.start(`Checking for vault "${vault}"...`);
689
884
  const vaultFound = await withMinTime(vaultExists(vault, { verbose }));
690
885
  if (vaultFound) {
@@ -696,38 +891,32 @@ async function runConvert(options) {
696
891
  vaultSpinner.stop(`Created vault "${vault}"`);
697
892
  } else {
698
893
  vaultSpinner.stop(`Vault "${vault}" not found`);
699
- const shouldCreate = await p2.confirm({
894
+ const shouldCreate = await p4.confirm({
700
895
  message: `Vault "${vault}" does not exist. Create it?`
701
896
  });
702
- if (p2.isCancel(shouldCreate) || !shouldCreate) {
897
+ if (p4.isCancel(shouldCreate) || !shouldCreate) {
703
898
  logger.cancel("Operation cancelled");
704
899
  logger.info('Run "op vault list" to see available vaults');
705
900
  process.exit(0);
706
901
  }
707
- const createSpinner = p2.spinner();
902
+ const createSpinner = p4.spinner();
708
903
  createSpinner.start(`Creating vault "${vault}"...`);
709
904
  await createVault(vault, { verbose });
710
905
  createSpinner.stop(`Created vault "${vault}"`);
711
906
  }
712
907
  }
713
- const itemSpinner = p2.spinner();
908
+ const itemSpinner = p4.spinner();
714
909
  itemSpinner.start(`Checking for item "${itemName}"...`);
715
910
  const existingItemId = await withMinTime(itemExists(vault, itemName, { verbose }));
716
911
  if (existingItemId) {
717
912
  itemSpinner.stop(`Item "${itemName}" found`);
718
913
  if (!force) {
719
- const shouldOverwrite = await p2.confirm({
720
- message: `Item "${itemName}" already exists in vault "${vault}". Update it?`
721
- });
722
- if (p2.isCancel(shouldOverwrite) || !shouldOverwrite) {
723
- logger.cancel("Operation cancelled");
724
- process.exit(0);
725
- }
914
+ await confirmOrExit(`Item "${itemName}" already exists in vault "${vault}". Update it?`);
726
915
  }
727
916
  } else {
728
917
  itemSpinner.stop(`Item "${itemName}" not found`);
729
918
  }
730
- const pushSpinner = p2.spinner();
919
+ const pushSpinner = p4.spinner();
731
920
  pushSpinner.start("Pushing environment variables...");
732
921
  try {
733
922
  if (existingItemId) {
@@ -754,7 +943,7 @@ async function runConvert(options) {
754
943
  throw error;
755
944
  }
756
945
  }
757
- const templatePath = output ?? join(dirname(envFile), `${basename(envFile)}.tpl`);
946
+ const templatePath = output ?? join2(dirname(envFile), `${basename(envFile)}.tpl`);
758
947
  const templateFileName = basename(templatePath);
759
948
  if (dryRun) {
760
949
  logger.warn(`Would generate template: ${templatePath}`);
@@ -777,210 +966,27 @@ async function runConvert(options) {
777
966
  logger.outro("Done! Your secrets are now in 1Password");
778
967
  }
779
968
  } catch (error) {
780
- if (error instanceof Env2OpError) {
781
- logger.error(error.message);
782
- if (error.suggestion) {
783
- logger.info(`Suggestion: ${error.suggestion}`);
784
- }
785
- process.exit(1);
786
- }
787
- throw error;
969
+ handleCommandError(error);
788
970
  }
789
971
  }
790
972
 
791
973
  // src/commands/update.ts
792
- import * as p4 from "@clack/prompts";
974
+ import * as p6 from "@clack/prompts";
793
975
  import pc4 from "picocolors";
794
976
 
795
- // src/lib/package-manager.ts
796
- var UPDATE_COMMANDS = {
797
- homebrew: "brew upgrade tolgamorf/tap/env2op-cli",
798
- npm: "npm update -g @tolgamorf/env2op-cli",
799
- bun: "bun update -g @tolgamorf/env2op-cli",
800
- pnpm: "pnpm update -g @tolgamorf/env2op-cli",
801
- unknown: "npm update -g @tolgamorf/env2op-cli"
802
- };
803
- var DISPLAY_NAMES = {
804
- homebrew: "Homebrew",
805
- npm: "npm",
806
- bun: "Bun",
807
- pnpm: "pnpm",
808
- unknown: "npm (default)"
809
- };
810
- function detectFromPath() {
811
- const binPath = process.argv[1] ?? "";
812
- if (binPath.includes("/Cellar/") || binPath.includes("/homebrew/") || binPath.includes("/opt/homebrew/") || binPath.includes("/home/linuxbrew/")) {
813
- return "homebrew";
814
- }
815
- if (binPath.includes("/.bun/")) {
816
- return "bun";
817
- }
818
- if (binPath.includes("/pnpm/") || binPath.includes("/.pnpm/")) {
819
- return "pnpm";
820
- }
821
- if (binPath.includes("/node_modules/")) {
822
- return "npm";
823
- }
824
- return null;
825
- }
826
- async function detectFromCommands() {
827
- const brewResult = await exec("brew", ["list", "env2op-cli"], { verbose: false });
828
- if (brewResult.exitCode === 0) {
829
- return "homebrew";
830
- }
831
- return "npm";
832
- }
833
- async function detectPackageManager() {
834
- const fromPath = detectFromPath();
835
- if (fromPath) {
836
- return {
837
- type: fromPath,
838
- updateCommand: UPDATE_COMMANDS[fromPath],
839
- displayName: DISPLAY_NAMES[fromPath]
840
- };
841
- }
842
- const fromCommands = await detectFromCommands();
843
- return {
844
- type: fromCommands,
845
- updateCommand: UPDATE_COMMANDS[fromCommands],
846
- displayName: DISPLAY_NAMES[fromCommands]
847
- };
848
- }
849
-
850
- // src/lib/update.ts
851
- import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync as writeFileSync2 } from "node:fs";
852
- import { homedir } from "node:os";
853
- import { join as join2 } from "node:path";
854
- var CACHE_DIR = join2(homedir(), ".env2op");
855
- var CACHE_FILE = join2(CACHE_DIR, "update-check.json");
856
- var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000;
857
- function getCliVersion() {
858
- try {
859
- const pkg2 = require_package();
860
- return pkg2.version ?? "0.0.0";
861
- } catch {
862
- return "0.0.0";
863
- }
864
- }
865
- function loadCache() {
866
- try {
867
- if (existsSync(CACHE_FILE)) {
868
- const content = readFileSync(CACHE_FILE, "utf-8");
869
- return JSON.parse(content);
870
- }
871
- } catch {}
872
- return { lastCheck: 0, latestVersion: null };
873
- }
874
- function saveCache(cache) {
875
- try {
876
- if (!existsSync(CACHE_DIR)) {
877
- mkdirSync(CACHE_DIR, { recursive: true });
878
- }
879
- writeFileSync2(CACHE_FILE, JSON.stringify(cache, null, 2));
880
- } catch {}
881
- }
882
- function shouldCheckForUpdate(cache) {
883
- const now = Date.now();
884
- return now - cache.lastCheck > CHECK_INTERVAL_MS;
885
- }
886
- async function fetchLatestVersion() {
887
- try {
888
- const response = await fetch("https://registry.npmjs.org/@tolgamorf/env2op-cli/latest");
889
- if (!response.ok)
890
- return null;
891
- const data = await response.json();
892
- return data.version ?? null;
893
- } catch {
894
- return null;
895
- }
896
- }
897
- function compareVersions(v1, v2) {
898
- const parts1 = v1.split(".").map(Number);
899
- const parts2 = v2.split(".").map(Number);
900
- for (let i = 0;i < 3; i++) {
901
- const p1 = parts1[i] || 0;
902
- const p22 = parts2[i] || 0;
903
- if (p1 < p22)
904
- return -1;
905
- if (p1 > p22)
906
- return 1;
907
- }
908
- return 0;
909
- }
910
- async function checkForUpdate(forceCheck = false) {
911
- const currentVersion = getCliVersion();
912
- const cache = loadCache();
913
- if (!forceCheck && !shouldCheckForUpdate(cache) && cache.latestVersion) {
914
- const updateAvailable2 = compareVersions(currentVersion, cache.latestVersion) < 0;
915
- const isSkipped2 = cache.skipVersion === cache.latestVersion;
916
- return {
917
- currentVersion,
918
- latestVersion: cache.latestVersion,
919
- updateAvailable: updateAvailable2,
920
- isSkipped: isSkipped2,
921
- fromCache: true
922
- };
923
- }
924
- const latestVersion = await fetchLatestVersion();
925
- saveCache({
926
- ...cache,
927
- lastCheck: Date.now(),
928
- latestVersion
929
- });
930
- if (!latestVersion) {
931
- return {
932
- currentVersion,
933
- latestVersion: null,
934
- updateAvailable: false,
935
- isSkipped: false,
936
- fromCache: false
937
- };
938
- }
939
- const updateAvailable = compareVersions(currentVersion, latestVersion) < 0;
940
- const isSkipped = cache.skipVersion === latestVersion;
941
- return {
942
- currentVersion,
943
- latestVersion,
944
- updateAvailable,
945
- isSkipped,
946
- fromCache: false
947
- };
948
- }
949
- async function performUpdate(pm) {
950
- const packageManager = pm ?? await detectPackageManager();
951
- try {
952
- const [command, ...args] = packageManager.updateCommand.split(" ");
953
- const result = await exec(command, args, { verbose: false });
954
- if (result.exitCode !== 0) {
955
- return {
956
- success: false,
957
- error: result.stderr || `Command exited with code ${result.exitCode}`
958
- };
959
- }
960
- return { success: true };
961
- } catch (error) {
962
- const message = error instanceof Error ? error.message : String(error);
963
- return { success: false, error: message };
964
- }
965
- }
966
- function skipVersion(version) {
967
- const cache = loadCache();
968
- cache.skipVersion = version;
969
- saveCache(cache);
970
- }
971
-
972
977
  // src/lib/update-prompts.ts
973
- import * as p3 from "@clack/prompts";
978
+ import * as p5 from "@clack/prompts";
974
979
  import pc3 from "picocolors";
980
+ var S_BAR = "│";
975
981
  var S_BAR_START = "┌";
976
982
  var S_BAR_END = "└";
977
983
  function showUpdateNotification(result, cliName = "env2op") {
978
984
  console.log();
979
985
  console.log(`${pc3.gray(S_BAR_START)}${pc3.gray("─")} ${pc3.yellow("Update available:")} ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
980
- console.log(`${pc3.gray(S_BAR_END)}${pc3.gray("─")} Run ${pc3.cyan(`'${cliName} update'`)} to update`);
986
+ console.log(`${pc3.gray(S_BAR_END)}${pc3.gray("─")} Run ${pc3.cyan(`'${cliName} --update'`)} to update`);
981
987
  }
982
988
  async function askToUpdate(result) {
983
- const response = await p3.select({
989
+ const response = await p5.select({
984
990
  message: "Would you like to update?",
985
991
  options: [
986
992
  { value: "update", label: "Update now", hint: "Download and install the latest version" },
@@ -988,42 +994,41 @@ async function askToUpdate(result) {
988
994
  { value: "skip", label: "Skip this version", hint: `Don't ask about ${result.latestVersion} again` }
989
995
  ]
990
996
  });
991
- if (p3.isCancel(response)) {
997
+ if (p5.isCancel(response)) {
992
998
  return "later";
993
999
  }
994
1000
  return response;
995
1001
  }
996
1002
  function showUpdateAvailable(result) {
997
- p3.log.success(`Update available: ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
1003
+ p5.log.success(`Update available: ${pc3.dim(result.currentVersion)} ${pc3.dim("→")} ${pc3.green(result.latestVersion)}`);
998
1004
  }
999
1005
  function showUpToDate(currentVersion) {
1000
- p3.log.success(`You're on the latest version ${pc3.green(`(${currentVersion})`)}`);
1006
+ p5.log.success(`You're on the latest version ${pc3.green(`(${currentVersion})`)}`);
1001
1007
  }
1002
1008
  function showPackageManagerInfo(pm) {
1003
- console.log();
1004
- console.log(` ${pc3.dim("Detected:")} ${pm.displayName} installation`);
1005
- console.log(` ${pc3.dim("Command:")} ${pc3.cyan(pm.updateCommand)}`);
1006
- console.log();
1009
+ console.log(pc3.gray(S_BAR));
1010
+ console.log(`${pc3.gray(S_BAR)} ${pc3.dim("Detected:")} ${pm.displayName} installation`);
1011
+ console.log(`${pc3.gray(S_BAR)} ${pc3.dim("Command:")} ${pc3.cyan(pm.updateCommand)}`);
1007
1012
  }
1008
1013
  function showUpdateSuccess(newVersion) {
1009
- p3.log.success(`Updated to version ${pc3.green(newVersion)}`);
1010
- p3.log.info("Please restart to use the new version.");
1014
+ p5.log.success(`Updated to version ${pc3.green(newVersion)}`);
1015
+ p5.log.info("Please restart to use the new version.");
1011
1016
  }
1012
1017
  function showUpdateError(error, pm) {
1013
1018
  if (error) {
1014
- p3.log.error(pc3.dim(error));
1019
+ p5.log.error(pc3.dim(error));
1015
1020
  }
1016
- p3.log.info(`Try running manually: ${pc3.cyan(pm.updateCommand)}`);
1021
+ p5.log.info(`Try running manually: ${pc3.cyan(pm.updateCommand)}`);
1017
1022
  }
1018
1023
 
1019
1024
  // src/commands/update.ts
1020
1025
  async function runUpdate(options) {
1021
1026
  const { force = false, cliName = "env2op" } = options;
1022
- p4.intro(pc4.bgCyan(pc4.black(` ${cliName} update `)));
1023
- const spinner4 = p4.spinner();
1024
- spinner4.start("Checking for updates...");
1027
+ p6.intro(pc4.bgCyan(pc4.black(` ${cliName} update `)));
1028
+ const spinner5 = p6.spinner();
1029
+ spinner5.start("Checking for updates...");
1025
1030
  const result = await checkForUpdate(true);
1026
- spinner4.stop("Checked for updates");
1031
+ spinner5.stop("Checked for updates");
1027
1032
  if (!result.updateAvailable || !result.latestVersion) {
1028
1033
  showUpToDate(result.currentVersion);
1029
1034
  return;
@@ -1035,15 +1040,15 @@ async function runUpdate(options) {
1035
1040
  const choice = await askToUpdate(result);
1036
1041
  if (choice === "skip") {
1037
1042
  skipVersion(result.latestVersion);
1038
- p4.log.info(`Skipped version ${result.latestVersion}`);
1043
+ p6.log.info(`Skipped version ${result.latestVersion}`);
1039
1044
  return;
1040
1045
  }
1041
1046
  if (choice === "later") {
1042
- p4.log.info("Update postponed");
1047
+ p6.log.info("Update postponed");
1043
1048
  return;
1044
1049
  }
1045
1050
  }
1046
- const updateSpinner = p4.spinner();
1051
+ const updateSpinner = p6.spinner();
1047
1052
  updateSpinner.start(`Updating to ${result.latestVersion}...`);
1048
1053
  const updateResult = await performUpdate(pm);
1049
1054
  if (updateResult.success) {
@@ -1056,35 +1061,40 @@ async function runUpdate(options) {
1056
1061
  }
1057
1062
  }
1058
1063
 
1059
- // src/cli.ts
1060
- var pkg2 = await Promise.resolve().then(() => __toESM(require_package(), 1));
1061
- var args = process.argv.slice(2);
1062
- var flags = new Set;
1063
- var positional = [];
1064
- var options = {};
1065
- for (let i = 0;i < args.length; i++) {
1066
- const arg = args[i];
1067
- if (arg === "-o" || arg === "--output") {
1068
- const next = args[i + 1];
1069
- if (next && !next.startsWith("-")) {
1070
- options.output = next;
1071
- i++;
1072
- }
1073
- } else if (arg.startsWith("--")) {
1074
- flags.add(arg.slice(2));
1075
- } else if (arg.startsWith("-")) {
1076
- for (const char of arg.slice(1)) {
1077
- flags.add(char);
1064
+ // src/utils/args.ts
1065
+ function parseArgs(args) {
1066
+ const flags = new Set;
1067
+ const positional = [];
1068
+ const options = {};
1069
+ for (let i = 0;i < args.length; i++) {
1070
+ const arg = args[i];
1071
+ if (arg === "-o" || arg === "--output") {
1072
+ const next = args[i + 1];
1073
+ if (next && !next.startsWith("-")) {
1074
+ options.output = next;
1075
+ i++;
1076
+ }
1077
+ } else if (arg.startsWith("--")) {
1078
+ flags.add(arg.slice(2));
1079
+ } else if (arg.startsWith("-")) {
1080
+ for (const char of arg.slice(1)) {
1081
+ flags.add(char);
1082
+ }
1083
+ } else {
1084
+ positional.push(arg);
1078
1085
  }
1079
- } else {
1080
- positional.push(arg);
1081
1086
  }
1087
+ return { flags, positional, options };
1082
1088
  }
1089
+
1090
+ // src/cli.ts
1091
+ var pkg = await Promise.resolve().then(() => __toESM(require_package(), 1));
1092
+ var { flags, positional, options } = parseArgs(process.argv.slice(2));
1083
1093
  var hasHelp = flags.has("h") || flags.has("help");
1084
1094
  var hasVersion = flags.has("v") || flags.has("version");
1085
1095
  var hasUpdate = flags.has("update");
1086
1096
  if (hasVersion) {
1087
- console.log(pkg2.version);
1097
+ console.log(getCliVersion());
1088
1098
  process.exit(0);
1089
1099
  }
1090
1100
  if (hasUpdate) {
@@ -1097,10 +1107,12 @@ if (hasUpdate) {
1097
1107
  }
1098
1108
  if (hasHelp || positional.length === 0) {
1099
1109
  showHelp();
1110
+ await maybeShowUpdateNotification("env2op", showUpdateNotification);
1100
1111
  process.exit(0);
1101
1112
  }
1102
1113
  if (positional.length < 3) {
1103
1114
  showMissingArgsError(positional);
1115
+ await maybeShowUpdateNotification("env2op", showUpdateNotification);
1104
1116
  process.exit(1);
1105
1117
  }
1106
1118
  var [envFile, vault, itemName] = positional;
@@ -1114,18 +1126,13 @@ await runConvert({
1114
1126
  force: flags.has("f") || flags.has("force"),
1115
1127
  verbose: flags.has("verbose")
1116
1128
  });
1117
- try {
1118
- const updateResult = await checkForUpdate();
1119
- if (updateResult.updateAvailable && !updateResult.isSkipped) {
1120
- showUpdateNotification(updateResult, "env2op");
1121
- }
1122
- } catch {}
1129
+ await maybeShowUpdateNotification("env2op", showUpdateNotification);
1123
1130
  function showHelp() {
1124
1131
  const name = pc5.bold(pc5.cyan("env2op"));
1125
- const version = pc5.dim(`v${pkg2.version}`);
1132
+ const version = pc5.dim(`v${getCliVersion()}`);
1126
1133
  console.log(`
1127
1134
  ${name} ${version}
1128
- ${pkg2.description}
1135
+ ${pkg.description}
1129
1136
 
1130
1137
  ${pc5.bold("USAGE")}
1131
1138
  ${pc5.cyan("$")} env2op ${pc5.yellow("<env_file>")} ${pc5.yellow("<vault>")} ${pc5.yellow("<item_name>")} ${pc5.dim("[options]")}