@uagents/syncenv-cli 0.1.5 → 0.1.7

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/index.js CHANGED
@@ -14,10 +14,12 @@ import {
14
14
  isKEKCached,
15
15
  lockKEK,
16
16
  unlockAndStoreKEK
17
- } from "./chunk-JBMZAAVP.js";
17
+ } from "./chunk-LWTV6MO2.js";
18
18
  import {
19
19
  clearAuthState,
20
+ clearConfig,
20
21
  client,
22
+ deleteConfig,
21
23
  getConfig,
22
24
  getConfigPath,
23
25
  getOrUnlockUserKEK,
@@ -25,10 +27,11 @@ import {
25
27
  isAuthenticated,
26
28
  loadConfig,
27
29
  loadProjectConfig,
30
+ resolveProjectIdentifier,
28
31
  saveProjectConfig,
29
32
  setConfig,
30
33
  withAuthGuard
31
- } from "./chunk-OVEYHV4C.js";
34
+ } from "./chunk-YXE467TO.js";
32
35
  import {
33
36
  applyMergeStrategy,
34
37
  interactiveMerge,
@@ -36,11 +39,11 @@ import {
36
39
  } from "./chunk-F7ZZUTRW.js";
37
40
 
38
41
  // src/index.ts
39
- import chalk8 from "chalk";
40
- import { Command as Command7 } from "commander";
41
42
  import { readFileSync } from "fs";
42
43
  import { dirname as dirname2, join as join2 } from "path";
43
44
  import { fileURLToPath } from "url";
45
+ import chalk10 from "chalk";
46
+ import { Command as Command9 } from "commander";
44
47
 
45
48
  // src/commands/auth.ts
46
49
  import chalk2 from "chalk";
@@ -134,8 +137,8 @@ authCommands.command("signup").description("Guide to create a new account via we
134
137
  console.log(" \u2022 Secure password setup");
135
138
  console.log(" \u2022 User-friendly interface");
136
139
  console.log();
137
- const apiUrl = getConfig("apiUrl") || "https://syncenv.uagents.app";
138
- const signupUrl = `${apiUrl}/signup`;
140
+ const apiUrl = getConfig("apiUrl") || "https://syncenv-api.uagents.app";
141
+ const signupUrl = apiUrl.replace("-api", "") + "/signup";
139
142
  console.log(chalk2.cyan("\u{1F310} Please visit:"));
140
143
  console.log(chalk2.bold(signupUrl));
141
144
  console.log();
@@ -161,7 +164,8 @@ authCommands.command("signup").description("Guide to create a new account via we
161
164
  success("Waiting for you to complete registration...");
162
165
  info("Once done, run: syncenv auth login");
163
166
  });
164
- authCommands.command("login").description("Login to your account").option("-e, --email <email>", "email address").action(async (options) => {
167
+ authCommands.command("login").description("Login to your account").option("-e, --email <email>", "email address").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
168
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
165
169
  try {
166
170
  let email = options.email;
167
171
  if (!email) {
@@ -186,29 +190,95 @@ authCommands.command("login").description("Login to your account").option("-e, -
186
190
  mask: "*"
187
191
  }
188
192
  ]);
193
+ if (isDebug) {
194
+ console.log(chalk2.dim("\n[DEBUG] Configuration:"));
195
+ console.log(chalk2.dim(` API URL: ${getConfig("apiUrl")}`));
196
+ console.log(chalk2.dim(` Email: ${email}`));
197
+ console.log(chalk2.dim(` Debug Mode: ${isDebug}
198
+ `));
199
+ }
189
200
  const spinner = createSpinner("Authenticating...");
190
201
  spinner.start();
191
202
  try {
192
203
  const apiUrl = getConfig("apiUrl");
193
- const loginResponse = await fetch(`${apiUrl}/api/auth/sign-in/email`, {
204
+ const webOrigin = apiUrl.replace("-api", "");
205
+ const origin = apiUrl.startsWith("http://localhost") ? apiUrl : webOrigin;
206
+ const loginUrl = `${apiUrl}/api/auth/sign-in/email`;
207
+ const requestHeaders = {
208
+ "Content-Type": "application/json",
209
+ Origin: origin
210
+ };
211
+ if (isDebug) {
212
+ spinner.stop();
213
+ console.log(chalk2.dim("\n[DEBUG] Login Request:"));
214
+ console.log(chalk2.dim(` URL: ${loginUrl}`));
215
+ console.log(chalk2.dim(` Method: POST`));
216
+ console.log(chalk2.dim(` Headers:`));
217
+ console.log(chalk2.dim(` Content-Type: ${requestHeaders["Content-Type"]}`));
218
+ console.log(chalk2.dim(` Origin: ${requestHeaders.Origin}`));
219
+ console.log(chalk2.dim(` Body: { email: "${email}", password: "***" }
220
+ `));
221
+ spinner.start("Authenticating...");
222
+ }
223
+ const loginResponse = await fetch(loginUrl, {
194
224
  method: "POST",
195
- headers: { "Content-Type": "application/json" },
225
+ headers: requestHeaders,
196
226
  body: JSON.stringify({ email, password })
197
227
  });
228
+ if (isDebug) {
229
+ spinner.stop();
230
+ console.log(chalk2.dim("[DEBUG] Login Response:"));
231
+ console.log(chalk2.dim(` Status: ${loginResponse.status} ${loginResponse.statusText}`));
232
+ console.log(chalk2.dim(` Headers:`));
233
+ loginResponse.headers.forEach((value, key) => {
234
+ if (key.toLowerCase() === "set-cookie") {
235
+ console.log(chalk2.dim(` ${key}: [redacted]`));
236
+ } else {
237
+ console.log(chalk2.dim(` ${key}: ${value}`));
238
+ }
239
+ });
240
+ console.log("");
241
+ spinner.start("Authenticating...");
242
+ }
198
243
  if (!loginResponse.ok) {
199
- const errorData = await loginResponse.json().catch(() => ({ error: "Login failed" }));
244
+ const errorText = await loginResponse.text().catch(() => "Login failed");
245
+ let errorData = {};
246
+ try {
247
+ errorData = JSON.parse(errorText);
248
+ } catch {
249
+ errorData = { error: errorText };
250
+ }
251
+ if (isDebug) {
252
+ spinner.stop();
253
+ console.log(chalk2.dim("[DEBUG] Error Response Body:"));
254
+ console.log(chalk2.dim(` ${errorText}
255
+ `));
256
+ }
200
257
  throw new Error(errorData.error || errorData.message || "Login failed");
201
258
  }
202
259
  const setCookie = loginResponse.headers.getSetCookie?.() || (loginResponse.headers.get("set-cookie") ? [loginResponse.headers.get("set-cookie")] : []);
203
260
  if (setCookie.length > 0) {
204
- const { storeCookies } = await import("./cookie-store-Z6DNTUGS.js");
261
+ const { storeCookies } = await import("./cookie-store-UGGEBXBV.js");
205
262
  storeCookies(setCookie);
206
263
  }
207
264
  const result = await loginResponse.json();
265
+ if (isDebug) {
266
+ spinner.stop();
267
+ console.log(chalk2.dim("[DEBUG] Login Success:"));
268
+ console.log(chalk2.dim(` User ID: ${result.user.id}`));
269
+ console.log(chalk2.dim(` Email: ${result.user.email}`));
270
+ console.log(chalk2.dim(` Name: ${result.user.name}
271
+ `));
272
+ spinner.start("Finalizing...");
273
+ }
208
274
  setConfig("userId", result.user.id);
209
275
  setConfig("userEmail", result.user.email);
210
276
  spinner.succeed("Authenticated");
211
277
  success("Session active");
278
+ if (isDebug) {
279
+ info(`User: ${result.user.email}`);
280
+ info(`Cookies stored: ${setCookie.length > 0 ? "Yes" : "No"}`);
281
+ }
212
282
  } catch (err) {
213
283
  spinner.fail("Authentication failed");
214
284
  throw err;
@@ -218,8 +288,14 @@ authCommands.command("login").description("Login to your account").option("-e, -
218
288
  process.exit(1);
219
289
  }
220
290
  });
221
- authCommands.command("logout").description("Logout and clear session").action(async () => {
291
+ authCommands.command("logout").description("Logout and clear session").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
292
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
222
293
  try {
294
+ if (isDebug) {
295
+ console.log(chalk2.dim("\n[DEBUG] Logout Command"));
296
+ console.log(chalk2.dim(`[DEBUG] Authenticated: ${isAuthenticated()}
297
+ `));
298
+ }
223
299
  if (!isAuthenticated()) {
224
300
  info("Not logged in.");
225
301
  return;
@@ -228,18 +304,38 @@ authCommands.command("logout").description("Logout and clear session").action(as
228
304
  spinner.start();
229
305
  try {
230
306
  await client.request("POST", "/api/auth/signout");
231
- } catch {
307
+ if (isDebug) {
308
+ console.log(chalk2.dim("[DEBUG] Server signout successful"));
309
+ }
310
+ } catch (err) {
311
+ if (isDebug) {
312
+ console.log(chalk2.dim(`[DEBUG] Server signout error (ignored): ${err.message}`));
313
+ }
232
314
  }
233
315
  await clearAuthState();
316
+ if (isDebug) {
317
+ console.log(chalk2.dim("[DEBUG] Local auth state cleared"));
318
+ }
234
319
  spinner.succeed("Logged out");
235
320
  success("Session terminated and local keys cleared");
236
321
  } catch (err) {
322
+ if (isDebug) {
323
+ console.log(chalk2.dim(`
324
+ [DEBUG] Logout error: ${err.message}`));
325
+ console.log(chalk2.dim(`[DEBUG] Error stack: ${err.stack}`));
326
+ }
237
327
  error("Logout failed:", err.message);
238
328
  process.exit(1);
239
329
  }
240
330
  });
241
- authCommands.command("status").description("Check authentication status").action(async () => {
331
+ authCommands.command("status").description("Check authentication status").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
332
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
242
333
  try {
334
+ if (isDebug) {
335
+ console.log(chalk2.dim("\n[DEBUG] Status Command"));
336
+ console.log(chalk2.dim(`[DEBUG] Authenticated: ${isAuthenticated()}
337
+ `));
338
+ }
243
339
  if (!isAuthenticated()) {
244
340
  info("Not authenticated. Run `syncenv auth login` to login.");
245
341
  return;
@@ -247,6 +343,9 @@ authCommands.command("status").description("Check authentication status").action
247
343
  const { data: session } = await withAuthGuard(
248
344
  () => client.request("GET", "/api/auth/get-session")
249
345
  );
346
+ if (isDebug) {
347
+ console.log(chalk2.dim("[DEBUG] Session fetched from server"));
348
+ }
250
349
  if (session.session) {
251
350
  console.log(
252
351
  `${chalk2.green("\u2713")} Authenticated as ${chalk2.cyan(session.session.user.email)}`
@@ -256,12 +355,23 @@ authCommands.command("status").description("Check authentication status").action
256
355
  info("Session expired. Please login again.");
257
356
  }
258
357
  } catch (err) {
358
+ if (isDebug) {
359
+ console.log(chalk2.dim(`
360
+ [DEBUG] Status error: ${err.message}`));
361
+ console.log(chalk2.dim(`[DEBUG] Error stack: ${err.stack}`));
362
+ }
259
363
  error("Failed to check status:", err.message);
260
364
  process.exit(1);
261
365
  }
262
366
  });
263
- authCommands.command("change-password").description("Change your account password").action(async () => {
367
+ authCommands.command("change-password").description("Change your account password").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
368
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
264
369
  try {
370
+ if (isDebug) {
371
+ console.log(chalk2.dim("\n[DEBUG] Change Password Command"));
372
+ console.log(chalk2.dim(`[DEBUG] Authenticated: ${isAuthenticated()}
373
+ `));
374
+ }
265
375
  if (!isAuthenticated()) {
266
376
  error("Not authenticated. Run `syncenv auth login` first.");
267
377
  process.exit(1);
@@ -312,8 +422,14 @@ authCommands.command("change-password").description("Change your account passwor
312
422
  }
313
423
  })
314
424
  );
425
+ if (isDebug) {
426
+ console.log(chalk2.dim("[DEBUG] Account password changed successfully"));
427
+ }
315
428
  try {
316
429
  const userKeys = await withAuthGuard(() => client.userKeys.get());
430
+ if (isDebug) {
431
+ console.log(chalk2.dim("[DEBUG] User has encryption keys, re-encrypting..."));
432
+ }
317
433
  spinner.text = "Re-encrypting encryption keys...";
318
434
  const { unlockUserKEK: unlockUserKEK2, reencryptUserKEK: reencryptUserKEK2 } = await import("./crypto-X7MZU7DV.js");
319
435
  const userKek = await unlockUserKEK2(
@@ -328,10 +444,16 @@ authCommands.command("change-password").description("Change your account passwor
328
444
  info("Run `syncenv user-keys rotate` to fix this.");
329
445
  return;
330
446
  }
447
+ if (isDebug) {
448
+ console.log(chalk2.dim("[DEBUG] User KEK decrypted with old password"));
449
+ }
331
450
  const { encryptedUserKek, kekIv, kekSalt } = await reencryptUserKEK2(
332
451
  userKek,
333
452
  answers.newPassword
334
453
  );
454
+ if (isDebug) {
455
+ console.log(chalk2.dim("[DEBUG] User KEK re-encrypted with new password"));
456
+ }
335
457
  await withAuthGuard(
336
458
  () => client.userKeys.update({
337
459
  encryptedUserKek,
@@ -339,60 +461,310 @@ authCommands.command("change-password").description("Change your account passwor
339
461
  kekSalt
340
462
  })
341
463
  );
342
- const { saveEncryptedKEK, saveKEKPassword, getKEKPassword } = await import("./secure-storage-UEK3LD5L.js");
464
+ if (isDebug) {
465
+ console.log(chalk2.dim("[DEBUG] Updated encrypted keys on server"));
466
+ }
467
+ const { saveEncryptedKEK, saveKEKPassword, getKEKPassword } = await import("./secure-storage-AR7HZFTA.js");
343
468
  saveEncryptedKEK({ encryptedUserKek, kekIv, kekSalt });
344
469
  const existingPassword = await getKEKPassword();
345
470
  if (existingPassword) {
346
471
  await saveKEKPassword(answers.newPassword);
472
+ if (isDebug) {
473
+ console.log(chalk2.dim("[DEBUG] Updated stored password in keychain"));
474
+ }
347
475
  }
348
476
  } catch (err) {
349
477
  if (!err.message?.includes("404")) {
350
478
  throw err;
351
479
  }
480
+ if (isDebug) {
481
+ console.log(
482
+ chalk2.dim("[DEBUG] User has no encryption keys (404), skipping key re-encryption")
483
+ );
484
+ }
352
485
  }
353
486
  spinner.succeed("Password changed successfully");
354
487
  success("Your account password has been updated");
355
488
  } catch (err) {
489
+ if (isDebug) {
490
+ console.log(chalk2.dim(`
491
+ [DEBUG] Change password error: ${err.message}`));
492
+ console.log(chalk2.dim(`[DEBUG] Error stack: ${err.stack}`));
493
+ }
356
494
  spinner.fail("Failed to change password");
357
495
  throw err;
358
496
  }
359
497
  } catch (err) {
498
+ if (isDebug) {
499
+ console.log(chalk2.dim(`
500
+ [DEBUG] Outer change password error: ${err.message}`));
501
+ console.log(chalk2.dim(`[DEBUG] Error stack: ${err.stack}`));
502
+ }
360
503
  error("Change password failed:", err.message);
361
504
  process.exit(1);
362
505
  }
363
506
  });
364
507
 
365
- // src/commands/doctor.ts
366
- import fs from "fs/promises";
508
+ // src/commands/config.ts
367
509
  import chalk3 from "chalk";
368
510
  import { Command as Command2 } from "commander";
369
- var doctorCommand = new Command2("doctor").description("Diagnose configuration and connectivity issues").action(async () => {
511
+ import inquirer2 from "inquirer";
512
+ var CONFIG_KEYS = {
513
+ apiUrl: {
514
+ description: "API base URL",
515
+ defaultValue: "https://syncenv-api.uagents.app",
516
+ validate: (val) => {
517
+ try {
518
+ new URL(val);
519
+ return true;
520
+ } catch {
521
+ return "Please enter a valid URL";
522
+ }
523
+ }
524
+ },
525
+ autoLockMinutes: {
526
+ description: "Auto-lock timeout in minutes (0 to disable)",
527
+ defaultValue: "30",
528
+ validate: (val) => {
529
+ const num = parseInt(val);
530
+ if (isNaN(num) || num < 0) return "Please enter a positive number or 0";
531
+ return true;
532
+ },
533
+ transform: (val) => parseInt(val)
534
+ }
535
+ };
536
+ var ALL_KEYS = ["apiUrl", "autoLockMinutes"];
537
+ var configCommands = new Command2("config").description("Manage CLI configuration");
538
+ configCommands.command("list").alias("ls").description("List all configuration values").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
539
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
540
+ try {
541
+ if (isDebug) {
542
+ console.log(chalk3.dim("\n[DEBUG] Config List Command\n"));
543
+ }
544
+ console.log(chalk3.bold("\nConfiguration:"));
545
+ console.log(chalk3.dim("\u2500".repeat(60)));
546
+ for (const [key, meta] of Object.entries(CONFIG_KEYS)) {
547
+ const value = getConfig(key);
548
+ const displayValue = value !== void 0 ? chalk3.green(String(value)) : chalk3.gray("(not set)");
549
+ console.log(`${key.padEnd(20)} ${displayValue}`);
550
+ console.log(chalk3.dim(` ${meta.description}`));
551
+ if (value === void 0) {
552
+ console.log(chalk3.dim(` Default: ${meta.defaultValue}`));
553
+ }
554
+ console.log();
555
+ }
556
+ console.log(chalk3.dim(`Config file: ${getConfigPath()}`));
557
+ console.log();
558
+ } catch (err) {
559
+ if (isDebug) {
560
+ console.log(chalk3.dim(`
561
+ [DEBUG] List error: ${err.message}`));
562
+ console.log(chalk3.dim(`[DEBUG] Error stack: ${err.stack}`));
563
+ }
564
+ error("Failed to list config:", err.message);
565
+ process.exit(1);
566
+ }
567
+ });
568
+ configCommands.command("get <key>").description("Get a configuration value").option("--debug", "enable debug mode with verbose logging").action(async (key, options) => {
569
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
570
+ try {
571
+ if (isDebug) {
572
+ console.log(chalk3.dim("\n[DEBUG] Config Get Command"));
573
+ console.log(chalk3.dim(`[DEBUG] Key: ${key}
574
+ `));
575
+ }
576
+ if (!ALL_KEYS.includes(key)) {
577
+ error(`Unknown config key: ${key}`);
578
+ info(`Valid keys: ${ALL_KEYS.join(", ")}`);
579
+ process.exit(1);
580
+ }
581
+ const value = getConfig(key);
582
+ const meta = CONFIG_KEYS[key];
583
+ if (value === void 0) {
584
+ console.log(chalk3.gray("(not set)"));
585
+ info(`Default: ${meta.defaultValue}`);
586
+ } else {
587
+ console.log(String(value));
588
+ }
589
+ } catch (err) {
590
+ if (isDebug) {
591
+ console.log(chalk3.dim(`
592
+ [DEBUG] Get error: ${err.message}`));
593
+ console.log(chalk3.dim(`[DEBUG] Error stack: ${err.stack}`));
594
+ }
595
+ error("Failed to get config:", err.message);
596
+ process.exit(1);
597
+ }
598
+ });
599
+ configCommands.command("set <key> [value]").description("Set a configuration value").option("--debug", "enable debug mode with verbose logging").action(async (key, value, options) => {
600
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
601
+ try {
602
+ if (isDebug) {
603
+ console.log(chalk3.dim("\n[DEBUG] Config Set Command"));
604
+ console.log(chalk3.dim(`[DEBUG] Key: ${key}`));
605
+ console.log(chalk3.dim(`[DEBUG] Value: ${value}
606
+ `));
607
+ }
608
+ if (!ALL_KEYS.includes(key)) {
609
+ error(`Unknown config key: ${key}`);
610
+ info(`Valid keys: ${ALL_KEYS.join(", ")}`);
611
+ process.exit(1);
612
+ }
613
+ const meta = CONFIG_KEYS[key];
614
+ let finalValue = value;
615
+ if (!value) {
616
+ const currentValue = getConfig(key);
617
+ const { inputValue } = await inquirer2.prompt([
618
+ {
619
+ type: "input",
620
+ name: "inputValue",
621
+ message: `Enter value for ${key}:`,
622
+ default: currentValue !== void 0 ? String(currentValue) : meta.defaultValue,
623
+ validate: meta.validate
624
+ }
625
+ ]);
626
+ finalValue = inputValue;
627
+ }
628
+ const validation = meta.validate?.(String(finalValue));
629
+ if (validation !== true) {
630
+ error(`Invalid value: ${validation}`);
631
+ process.exit(1);
632
+ }
633
+ const transformedValue = meta.transform ? meta.transform(String(finalValue)) : finalValue;
634
+ setConfig(key, transformedValue);
635
+ success(`${key} set to: ${transformedValue}`);
636
+ } catch (err) {
637
+ if (isDebug) {
638
+ console.log(chalk3.dim(`
639
+ [DEBUG] Set error: ${err.message}`));
640
+ console.log(chalk3.dim(`[DEBUG] Error stack: ${err.stack}`));
641
+ }
642
+ error("Failed to set config:", err.message);
643
+ process.exit(1);
644
+ }
645
+ });
646
+ configCommands.command("delete <key>").alias("rm").description("Delete a configuration value (reset to default)").option("--debug", "enable debug mode with verbose logging").action(async (key, options) => {
647
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
648
+ try {
649
+ if (isDebug) {
650
+ console.log(chalk3.dim("\n[DEBUG] Config Delete Command"));
651
+ console.log(chalk3.dim(`[DEBUG] Key: ${key}
652
+ `));
653
+ }
654
+ if (!ALL_KEYS.includes(key)) {
655
+ error(`Unknown config key: ${key}`);
656
+ info(`Valid keys: ${ALL_KEYS.join(", ")}`);
657
+ process.exit(1);
658
+ }
659
+ const currentValue = getConfig(key);
660
+ if (currentValue === void 0) {
661
+ warning(`${key} is not set`);
662
+ return;
663
+ }
664
+ const { confirm } = await inquirer2.prompt([
665
+ {
666
+ type: "confirm",
667
+ name: "confirm",
668
+ message: `Reset ${key} to default?`,
669
+ default: false
670
+ }
671
+ ]);
672
+ if (!confirm) {
673
+ info("Cancelled.");
674
+ return;
675
+ }
676
+ deleteConfig(key);
677
+ success(`${key} reset to default`);
678
+ const meta = CONFIG_KEYS[key];
679
+ info(`Default value: ${meta.defaultValue}`);
680
+ } catch (err) {
681
+ if (isDebug) {
682
+ console.log(chalk3.dim(`
683
+ [DEBUG] Delete error: ${err.message}`));
684
+ console.log(chalk3.dim(`[DEBUG] Error stack: ${err.stack}`));
685
+ }
686
+ error("Failed to delete config:", err.message);
687
+ process.exit(1);
688
+ }
689
+ });
690
+ configCommands.command("reset").description("Reset all configuration to defaults").option("-f, --force", "skip confirmation").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
691
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
692
+ try {
693
+ if (isDebug) {
694
+ console.log(chalk3.dim("\n[DEBUG] Config Reset Command\n"));
695
+ }
696
+ if (!options.force) {
697
+ const { confirm } = await inquirer2.prompt([
698
+ {
699
+ type: "confirm",
700
+ name: "confirm",
701
+ message: "Reset ALL configuration to defaults? This cannot be undone.",
702
+ default: false
703
+ }
704
+ ]);
705
+ if (!confirm) {
706
+ info("Cancelled.");
707
+ return;
708
+ }
709
+ }
710
+ clearConfig();
711
+ success("All configuration reset to defaults");
712
+ info(`Config file: ${getConfigPath()}`);
713
+ } catch (err) {
714
+ if (isDebug) {
715
+ console.log(chalk3.dim(`
716
+ [DEBUG] Reset error: ${err.message}`));
717
+ console.log(chalk3.dim(`[DEBUG] Error stack: ${err.stack}`));
718
+ }
719
+ error("Failed to reset config:", err.message);
720
+ process.exit(1);
721
+ }
722
+ });
723
+ configCommands.command("path").description("Show configuration file path").action(() => {
724
+ console.log(getConfigPath());
725
+ });
726
+
727
+ // src/commands/doctor.ts
728
+ import fs from "fs/promises";
729
+ import chalk4 from "chalk";
730
+ import { Command as Command3 } from "commander";
731
+ var doctorCommand = new Command3("doctor").description("Diagnose configuration and connectivity issues").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
732
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
370
733
  let exitCode = 0;
371
- console.log(chalk3.bold("\n\u{1F50D} SyncEnv Doctor\n"));
734
+ if (isDebug) {
735
+ console.log(chalk4.dim("\n[DEBUG] Doctor Command\n"));
736
+ }
737
+ console.log(chalk4.bold("\n\u{1F50D} SyncEnv Doctor\n"));
372
738
  const nodeVersion = process.version;
373
739
  const majorVersion = parseInt(nodeVersion.slice(1).split(".")[0]);
374
- console.log(chalk3.dim("Checking Node.js version..."));
740
+ console.log(chalk4.dim("Checking Node.js version..."));
741
+ if (isDebug) {
742
+ console.log(chalk4.dim(`[DEBUG] Node.js version: ${nodeVersion}`));
743
+ }
375
744
  if (majorVersion >= 18) {
376
745
  success(`Node.js ${nodeVersion}`);
377
746
  } else {
378
747
  error(`Node.js ${nodeVersion} (>= 18 required)`);
379
748
  exitCode = 1;
380
749
  }
381
- console.log(chalk3.dim("\nChecking configuration..."));
750
+ console.log(chalk4.dim("\nChecking configuration..."));
382
751
  const configPath = getConfigPath();
752
+ if (isDebug) {
753
+ console.log(chalk4.dim(`[DEBUG] Config path: ${configPath}`));
754
+ }
383
755
  try {
384
756
  await fs.access(configPath);
385
757
  success(`Config file exists: ${configPath}`);
386
758
  } catch {
387
759
  warning(`Config file not found: ${configPath}`);
388
760
  }
389
- console.log(chalk3.dim("\nChecking project configuration..."));
761
+ console.log(chalk4.dim("\nChecking project configuration..."));
390
762
  const hasConfig = await hasProjectConfig();
391
763
  if (hasConfig) {
392
764
  try {
393
765
  const config = await loadProjectConfig();
394
766
  if (config) {
395
- success(`.envsyncrc found`);
767
+ success(`.syncenvrc found`);
396
768
  info(` Project: ${config.project.name}`);
397
769
  if (config.project.id) {
398
770
  info(` Project ID: ${config.project.id}`);
@@ -402,13 +774,13 @@ var doctorCommand = new Command2("doctor").description("Diagnose configuration a
402
774
  info(` Default env: ${config.defaults.environment}`);
403
775
  }
404
776
  } catch (err) {
405
- error(`.envsyncrc exists but is invalid: ${err.message}`);
777
+ error(`.syncenvrc exists but is invalid: ${err.message}`);
406
778
  exitCode = 1;
407
779
  }
408
780
  } else {
409
- warning(`.envsyncrc not found - run \`syncenv init\``);
781
+ warning(`.syncenvrc not found - run \`syncenv init\``);
410
782
  }
411
- console.log(chalk3.dim("\nChecking authentication..."));
783
+ console.log(chalk4.dim("\nChecking authentication..."));
412
784
  if (isAuthenticated()) {
413
785
  const userEmail = getConfig("userEmail");
414
786
  success(`Authenticated as ${userEmail}`);
@@ -428,8 +800,11 @@ var doctorCommand = new Command2("doctor").description("Diagnose configuration a
428
800
  } else {
429
801
  warning(`Not authenticated - run \`syncenv auth login\``);
430
802
  }
431
- console.log(chalk3.dim("\nChecking API connectivity..."));
432
- const apiUrl = process.env.SYNCENV_API_URL || getConfig("apiUrl") || "http://localhost:8787";
803
+ console.log(chalk4.dim("\nChecking API connectivity..."));
804
+ const apiUrl = process.env.SYNCENV_API_URL || getConfig("apiUrl") || "https://syncenv-api.uagents.app";
805
+ if (isDebug) {
806
+ console.log(chalk4.dim(`[DEBUG] API URL: ${apiUrl}`));
807
+ }
433
808
  try {
434
809
  const response = await fetch(apiUrl);
435
810
  if (response.ok) {
@@ -443,6 +818,9 @@ var doctorCommand = new Command2("doctor").description("Diagnose configuration a
443
818
  } catch (err) {
444
819
  error(`Cannot connect to API at ${apiUrl}`);
445
820
  info(` Error: ${err.message}`);
821
+ if (isDebug) {
822
+ console.log(chalk4.dim(`[DEBUG] Connection error details: ${err.stack}`));
823
+ }
446
824
  exitCode = 1;
447
825
  }
448
826
  console.log("");
@@ -456,9 +834,9 @@ var doctorCommand = new Command2("doctor").description("Diagnose configuration a
456
834
 
457
835
  // src/commands/env.ts
458
836
  import fs2 from "fs/promises";
459
- import chalk4 from "chalk";
460
- import { Command as Command3 } from "commander";
461
- import inquirer2 from "inquirer";
837
+ import chalk5 from "chalk";
838
+ import { Command as Command4 } from "commander";
839
+ import inquirer3 from "inquirer";
462
840
 
463
841
  // src/state/index.ts
464
842
  import { existsSync } from "fs";
@@ -466,7 +844,7 @@ import { readFile, writeFile, mkdir } from "fs/promises";
466
844
  import { homedir } from "os";
467
845
  import { join, dirname } from "path";
468
846
  var STATE_VERSION = 1;
469
- var STATE_DIR = ".envsync";
847
+ var STATE_DIR = ".syncenv";
470
848
  var STATE_FILE = "state.json";
471
849
  var StateManager = class {
472
850
  statePath;
@@ -641,17 +1019,23 @@ function createStateManager(options) {
641
1019
  var globalState = createStateManager();
642
1020
 
643
1021
  // src/commands/env.ts
644
- var envCommands = new Command3("env").description("Environment variable commands");
645
- envCommands.command("push").description("Push .env file to server").option("-p, --project <id>", "project ID").option("-e, --env <name>", "environment name").option("-f, --file <path>", "file path").option("-m, --message <message>", "change description").option("--force", "force push without conflict check").option("--strategy <strategy>", "merge strategy on conflict: local-wins, remote-wins, fail-on-conflict").action(async (options) => {
1022
+ var envCommands = new Command4("env").description("Environment variable commands");
1023
+ envCommands.command("push").description("Push .env file to server").option("-p, --project <identifier>", "project ID or name").option("-e, --env <name>", "environment name").option("-f, --file <path>", "file path").option("-m, --message <message>", "change description").option("--force", "force push without conflict check").option(
1024
+ "--strategy <strategy>",
1025
+ "merge strategy on conflict: local-wins, remote-wins, fail-on-conflict"
1026
+ ).action(async (options) => {
646
1027
  try {
647
1028
  if (!isAuthenticated()) {
648
1029
  error("Not authenticated. Run `syncenv auth login` first.");
649
1030
  process.exit(1);
650
1031
  }
651
1032
  const config = await loadProjectConfig();
652
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1033
+ const projectId = options.project || config?.project.id;
653
1034
  if (!projectId) {
654
- error("No project specified. Use --project or run `syncenv init`");
1035
+ error("No project specified.");
1036
+ info("Use one of the following:");
1037
+ info(" --project <id> Specify project ID for this command");
1038
+ info(" syncenv project use <id> Set default project for current directory");
655
1039
  process.exit(1);
656
1040
  }
657
1041
  const envName = options.env || config?.defaults.environment || "dev";
@@ -708,10 +1092,24 @@ envCommands.command("push").description("Push .env file to server").option("-p,
708
1092
  warning(`Remote has been modified (version ${localVersion} \u2192 ${remoteVersion})`);
709
1093
  info("Attempting to merge...\n");
710
1094
  try {
711
- const baseDownload = await client.environments.downloadContent(environment.id, localVersion);
712
- const baseContent = decryptContent(baseDownload.content, baseDownload.encryptionIv, dek);
713
- const remoteDownload = await client.environments.downloadContent(environment.id, remoteVersion);
714
- const remoteContent = decryptContent(remoteDownload.content, remoteDownload.encryptionIv, dek);
1095
+ const baseDownload = await client.environments.downloadContent(
1096
+ environment.id,
1097
+ localVersion
1098
+ );
1099
+ const baseContent = decryptContent(
1100
+ baseDownload.content,
1101
+ baseDownload.encryptionIv,
1102
+ dek
1103
+ );
1104
+ const remoteDownload = await client.environments.downloadContent(
1105
+ environment.id,
1106
+ remoteVersion
1107
+ );
1108
+ const remoteContent = decryptContent(
1109
+ remoteDownload.content,
1110
+ remoteDownload.encryptionIv,
1111
+ dek
1112
+ );
715
1113
  const mergeInput = {
716
1114
  base: parseEnvFile(baseContent),
717
1115
  local: parseEnvFile(content),
@@ -719,10 +1117,10 @@ envCommands.command("push").description("Push .env file to server").option("-p,
719
1117
  };
720
1118
  const mergeResult = threeWayMerge(mergeInput);
721
1119
  if (mergeResult.autoMerged) {
722
- info(chalk4.green("\u2713 Auto-merged successfully"));
723
- info(chalk4.dim(` Added: ${mergeResult.statistics.added}`));
724
- info(chalk4.dim(` Modified: ${mergeResult.statistics.modified}`));
725
- info(chalk4.dim(` Deleted: ${mergeResult.statistics.deleted}`));
1120
+ info(chalk5.green("\u2713 Auto-merged successfully"));
1121
+ info(chalk5.dim(` Added: ${mergeResult.statistics.added}`));
1122
+ info(chalk5.dim(` Modified: ${mergeResult.statistics.modified}`));
1123
+ info(chalk5.dim(` Deleted: ${mergeResult.statistics.deleted}`));
726
1124
  const mergedEncrypted = encryptContent(mergeResult.mergedContent, dek);
727
1125
  spinner.start("Uploading merged content...");
728
1126
  const envId = environment.id;
@@ -734,7 +1132,13 @@ envCommands.command("push").description("Push .env file to server").option("-p,
734
1132
  changeSummary: options.message || `Merged with remote version ${remoteVersion}`
735
1133
  })
736
1134
  );
737
- await stateManager.recordSync(projectId, envId, updateResult.version, generateContentHash(mergeResult.mergedContent), "merge");
1135
+ await stateManager.recordSync(
1136
+ projectId,
1137
+ envId,
1138
+ updateResult.version,
1139
+ generateContentHash(mergeResult.mergedContent),
1140
+ "merge"
1141
+ );
738
1142
  spinner.succeed("Merged and uploaded");
739
1143
  success(`Environment "${envName}" updated to version ${updateResult.version}`);
740
1144
  return;
@@ -760,7 +1164,13 @@ envCommands.command("push").description("Push .env file to server").option("-p,
760
1164
  changeSummary: options.message || `Merged with remote version ${remoteVersion} (with conflict resolution)`
761
1165
  })
762
1166
  );
763
- await stateManager.recordSync(projectId, envId, updateResult.version, generateContentHash(interactiveResult.content), "merge");
1167
+ await stateManager.recordSync(
1168
+ projectId,
1169
+ envId,
1170
+ updateResult.version,
1171
+ generateContentHash(interactiveResult.content),
1172
+ "merge"
1173
+ );
764
1174
  spinner.succeed("Resolved and uploaded");
765
1175
  success(`Environment "${envName}" updated to version ${updateResult.version}`);
766
1176
  return;
@@ -822,16 +1232,19 @@ envCommands.command("push").description("Push .env file to server").option("-p,
822
1232
  process.exit(1);
823
1233
  }
824
1234
  });
825
- envCommands.command("pull").description("Pull .env file from server").option("-p, --project <id>", "project ID").option("-e, --env <name>", "environment name").option("-f, --file <path>", "output file path").option("-v, --version <number>", "specific version to pull").option("-m, --merge", "merge with existing file (using three-way merge if possible)").option("-y, --yes", "skip confirmation").action(async (options) => {
1235
+ envCommands.command("pull").description("Pull .env file from server").option("-p, --project <identifier>", "project ID or name").option("-e, --env <name>", "environment name").option("-f, --file <path>", "output file path").option("-v, --version <number>", "specific version to pull").option("-m, --merge", "merge with existing file (using three-way merge if possible)").option("-y, --yes", "skip confirmation").action(async (options) => {
826
1236
  try {
827
1237
  if (!isAuthenticated()) {
828
1238
  error("Not authenticated. Run `syncenv auth login` first.");
829
1239
  process.exit(1);
830
1240
  }
831
1241
  const config = await loadProjectConfig();
832
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1242
+ const projectId = options.project || config?.project.id;
833
1243
  if (!projectId) {
834
- error("No project specified. Use --project or run `syncenv init`");
1244
+ error("No project specified.");
1245
+ info("Use one of the following:");
1246
+ info(" --project <id> Specify project ID for this command");
1247
+ info(" syncenv project use <id> Set default project for current directory");
835
1248
  process.exit(1);
836
1249
  }
837
1250
  const envName = options.env || config?.defaults.environment || "dev";
@@ -879,7 +1292,7 @@ envCommands.command("pull").description("Pull .env file from server").option("-p
879
1292
  try {
880
1293
  existingContent = await fs2.readFile(filePath, "utf-8");
881
1294
  if (!options.yes && !options.merge) {
882
- const { action } = await inquirer2.prompt([
1295
+ const { action } = await inquirer3.prompt([
883
1296
  {
884
1297
  type: "list",
885
1298
  name: "action",
@@ -909,19 +1322,19 @@ envCommands.command("pull").description("Pull .env file from server").option("-p
909
1322
  info("No changes to merge.");
910
1323
  return;
911
1324
  }
912
- console.log(chalk4.bold("\nMerge changes:"));
1325
+ console.log(chalk5.bold("\nMerge changes:"));
913
1326
  for (const key of diff.added) {
914
- console.log(chalk4.green(` + ${key}=${localEnv[key]}`));
1327
+ console.log(chalk5.green(` + ${key}=${localEnv[key]}`));
915
1328
  }
916
1329
  for (const key of diff.removed) {
917
- console.log(chalk4.red(` - ${key}`));
1330
+ console.log(chalk5.red(` - ${key}`));
918
1331
  }
919
1332
  for (const key of diff.modified) {
920
- console.log(chalk4.yellow(` ~ ${key}`));
921
- console.log(chalk4.dim(` local: ${localEnv[key]}`));
922
- console.log(chalk4.dim(` remote: ${remoteEnv[key]}`));
1333
+ console.log(chalk5.yellow(` ~ ${key}`));
1334
+ console.log(chalk5.dim(` local: ${localEnv[key]}`));
1335
+ console.log(chalk5.dim(` remote: ${remoteEnv[key]}`));
923
1336
  }
924
- const { mergeAction } = await inquirer2.prompt([
1337
+ const { mergeAction } = await inquirer3.prompt([
925
1338
  {
926
1339
  type: "list",
927
1340
  name: "mergeAction",
@@ -962,16 +1375,19 @@ envCommands.command("pull").description("Pull .env file from server").option("-p
962
1375
  process.exit(1);
963
1376
  }
964
1377
  });
965
- envCommands.command("history").description("Show version history").option("-p, --project <id>", "project ID").option("-e, --env <name>", "environment name").option("-l, --limit <number>", "number of versions to show", "10").action(async (options) => {
1378
+ envCommands.command("history").description("Show version history").option("-p, --project <identifier>", "project ID or name").option("-e, --env <name>", "environment name").option("-l, --limit <number>", "number of versions to show", "10").action(async (options) => {
966
1379
  try {
967
1380
  if (!isAuthenticated()) {
968
1381
  error("Not authenticated. Run `syncenv auth login` first.");
969
1382
  process.exit(1);
970
1383
  }
971
1384
  const config = await loadProjectConfig();
972
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1385
+ const projectId = options.project || config?.project.id;
973
1386
  if (!projectId) {
974
1387
  error("No project specified.");
1388
+ info("Use one of the following:");
1389
+ info(" --project <id> Specify project ID for this command");
1390
+ info(" syncenv project use <id> Set default project for current directory");
975
1391
  process.exit(1);
976
1392
  }
977
1393
  const envName = options.env || config?.defaults.environment || "dev";
@@ -986,14 +1402,16 @@ envCommands.command("history").description("Show version history").option("-p, -
986
1402
  spinner.fail(`Environment "${envName}" not found`);
987
1403
  process.exit(1);
988
1404
  }
989
- const versions = await withAuthGuard(() => client.sync.getVersions(environment.id, parseInt(options.limit)));
1405
+ const versions = await withAuthGuard(
1406
+ () => client.sync.getVersions(environment.id, parseInt(options.limit))
1407
+ );
990
1408
  spinner.stop();
991
1409
  if (versions.length === 0) {
992
1410
  info("No versions found.");
993
1411
  return;
994
1412
  }
995
- console.log(chalk4.bold("\nVERSION DATE AUTHOR MESSAGE"));
996
- console.log(chalk4.dim("\u2500".repeat(70)));
1413
+ console.log(chalk5.bold("\nVERSION DATE AUTHOR MESSAGE"));
1414
+ console.log(chalk5.dim("\u2500".repeat(70)));
997
1415
  for (const version2 of versions.slice(0, limit)) {
998
1416
  const ver = String(version2.versionNumber).padEnd(9);
999
1417
  const date = formatRelativeTime(version2.createdAt).padEnd(17);
@@ -1007,16 +1425,19 @@ envCommands.command("history").description("Show version history").option("-p, -
1007
1425
  process.exit(1);
1008
1426
  }
1009
1427
  });
1010
- envCommands.command("diff <v1> <v2>").description("Compare two versions (not supported yet)").option("-p, --project <id>", "project ID").option("-e, --env <name>", "environment name").action(async (v1, v2, options) => {
1428
+ envCommands.command("diff <v1> <v2>").description("Compare two versions (not supported yet)").option("-p, --project <identifier>", "project ID or name").option("-e, --env <name>", "environment name").action(async (v1, v2, options) => {
1011
1429
  try {
1012
1430
  if (!isAuthenticated()) {
1013
1431
  error("Not authenticated. Run `syncenv auth login` first.");
1014
1432
  process.exit(1);
1015
1433
  }
1016
1434
  const config = await loadProjectConfig();
1017
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1435
+ const projectId = options.project || config?.project.id;
1018
1436
  if (!projectId) {
1019
1437
  error("No project specified.");
1438
+ info("Use one of the following:");
1439
+ info(" --project <id> Specify project ID for this command");
1440
+ info(" syncenv project use <id> Set default project for current directory");
1020
1441
  process.exit(1);
1021
1442
  }
1022
1443
  const envName = options.env || config?.defaults.environment || "dev";
@@ -1037,22 +1458,22 @@ envCommands.command("diff <v1> <v2>").description("Compare two versions (not sup
1037
1458
  client.environments.downloadContent(environment.id, version2)
1038
1459
  ]);
1039
1460
  spinner.stop();
1040
- console.log(chalk4.bold(`
1461
+ console.log(chalk5.bold(`
1041
1462
  Comparing versions ${version1} and ${version2}`));
1042
- console.log(chalk4.dim("\u2500".repeat(50)));
1043
- console.log(chalk4.dim(`
1463
+ console.log(chalk5.dim("\u2500".repeat(50)));
1464
+ console.log(chalk5.dim(`
1044
1465
  Version ${version1}:`));
1045
1466
  console.log(` Content hash: ${generateContentHash(download1.content).slice(0, 16)}...`);
1046
1467
  console.log(` Date: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
1047
- console.log(chalk4.dim(`
1468
+ console.log(chalk5.dim(`
1048
1469
  Version ${version2}:`));
1049
1470
  console.log(` Content hash: ${generateContentHash(download2.content).slice(0, 16)}...`);
1050
1471
  console.log(` Date: ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
1051
1472
  if (download1.content !== download2.content) {
1052
- console.log(chalk4.yellow("\n\u26A0 Content differs between versions"));
1473
+ console.log(chalk5.yellow("\n\u26A0 Content differs between versions"));
1053
1474
  info("Use `syncenv env pull --version <number>` to view a specific version");
1054
1475
  } else {
1055
- console.log(chalk4.green("\n\u2713 Content is identical"));
1476
+ console.log(chalk5.green("\n\u2713 Content is identical"));
1056
1477
  }
1057
1478
  console.log("");
1058
1479
  } catch (err) {
@@ -1060,22 +1481,25 @@ Version ${version2}:`));
1060
1481
  process.exit(1);
1061
1482
  }
1062
1483
  });
1063
- envCommands.command("rollback <version>").description("Rollback to a specific version").option("-p, --project <id>", "project ID").option("-e, --env <name>", "environment name").option("-y, --yes", "skip confirmation").action(async (version2, options) => {
1484
+ envCommands.command("rollback <version>").description("Rollback to a specific version").option("-p, --project <identifier>", "project ID or name").option("-e, --env <name>", "environment name").option("-y, --yes", "skip confirmation").action(async (version2, options) => {
1064
1485
  try {
1065
1486
  if (!isAuthenticated()) {
1066
1487
  error("Not authenticated. Run `syncenv auth login` first.");
1067
1488
  process.exit(1);
1068
1489
  }
1069
1490
  const config = await loadProjectConfig();
1070
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1491
+ const projectId = options.project || config?.project.id;
1071
1492
  if (!projectId) {
1072
1493
  error("No project specified.");
1494
+ info("Use one of the following:");
1495
+ info(" --project <id> Specify project ID for this command");
1496
+ info(" syncenv project use <id> Set default project for current directory");
1073
1497
  process.exit(1);
1074
1498
  }
1075
1499
  const envName = options.env || config?.defaults.environment || "dev";
1076
1500
  const targetVersion = parseInt(version2);
1077
1501
  if (!options.yes) {
1078
- const { confirm } = await inquirer2.prompt([
1502
+ const { confirm } = await inquirer3.prompt([
1079
1503
  {
1080
1504
  type: "confirm",
1081
1505
  name: "confirm",
@@ -1131,16 +1555,19 @@ envCommands.command("rollback <version>").description("Rollback to a specific ve
1131
1555
  process.exit(1);
1132
1556
  }
1133
1557
  });
1134
- envCommands.command("list-envs").description("List all environments for a project").option("-p, --project <id>", "project ID").action(async (options) => {
1558
+ envCommands.command("list-envs").description("List all environments for a project").option("-p, --project <identifier>", "project ID or name").action(async (options) => {
1135
1559
  try {
1136
1560
  if (!isAuthenticated()) {
1137
1561
  error("Not authenticated. Run `syncenv auth login` first.");
1138
1562
  process.exit(1);
1139
1563
  }
1140
1564
  const config = await loadProjectConfig();
1141
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1565
+ const projectId = options.project || config?.project.id;
1142
1566
  if (!projectId) {
1143
- error("No project specified. Use --project or run `syncenv init`");
1567
+ error("No project specified.");
1568
+ info("Use one of the following:");
1569
+ info(" --project <id> Specify project ID for this command");
1570
+ info(" syncenv project use <id> Set default project for current directory");
1144
1571
  process.exit(1);
1145
1572
  }
1146
1573
  const spinner = createSpinner("Fetching environments...");
@@ -1155,11 +1582,11 @@ envCommands.command("list-envs").description("List all environments for a projec
1155
1582
  return;
1156
1583
  }
1157
1584
  const project = await withAuthGuard(() => client.projects.get(projectId));
1158
- console.log(chalk4.bold(`
1585
+ console.log(chalk5.bold(`
1159
1586
  Environments for ${project.name}`));
1160
- console.log(chalk4.dim("\u2500".repeat(70)));
1161
- console.log(chalk4.bold("NAME VERSION SIZE LAST MODIFIED DESCRIPTION"));
1162
- console.log(chalk4.dim("\u2500".repeat(70)));
1587
+ console.log(chalk5.dim("\u2500".repeat(70)));
1588
+ console.log(chalk5.bold("NAME VERSION SIZE LAST MODIFIED DESCRIPTION"));
1589
+ console.log(chalk5.dim("\u2500".repeat(70)));
1163
1590
  for (const env of environments) {
1164
1591
  const name = env.name.padEnd(11).slice(0, 11);
1165
1592
  const version2 = `v${env.currentVersion || 1}`.padEnd(9);
@@ -1174,21 +1601,24 @@ Environments for ${project.name}`));
1174
1601
  process.exit(1);
1175
1602
  }
1176
1603
  });
1177
- envCommands.command("create-env").description("Create a new environment for a project").option("-p, --project <id>", "project ID").option("-n, --name <name>", "environment name").option("-d, --description <description>", "environment description").action(async (options) => {
1604
+ envCommands.command("create-env").description("Create a new environment for a project").option("-p, --project <identifier>", "project ID or name").option("-n, --name <name>", "environment name").option("-d, --description <description>", "environment description").action(async (options) => {
1178
1605
  try {
1179
1606
  if (!isAuthenticated()) {
1180
1607
  error("Not authenticated. Run `syncenv auth login` first.");
1181
1608
  process.exit(1);
1182
1609
  }
1183
1610
  const config = await loadProjectConfig();
1184
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1611
+ const projectId = options.project || config?.project.id;
1185
1612
  if (!projectId) {
1186
- error("No project specified. Use --project or run `syncenv init`");
1613
+ error("No project specified.");
1614
+ info("Use one of the following:");
1615
+ info(" --project <id> Specify project ID for this command");
1616
+ info(" syncenv project use <id> Set default project for current directory");
1187
1617
  process.exit(1);
1188
1618
  }
1189
1619
  let envName = options.name;
1190
1620
  if (!envName) {
1191
- const answer = await inquirer2.prompt([
1621
+ const answer = await inquirer3.prompt([
1192
1622
  {
1193
1623
  type: "input",
1194
1624
  name: "name",
@@ -1205,7 +1635,7 @@ envCommands.command("create-env").description("Create a new environment for a pr
1205
1635
  }
1206
1636
  let description = options.description;
1207
1637
  if (!description) {
1208
- const answer = await inquirer2.prompt([
1638
+ const answer = await inquirer3.prompt([
1209
1639
  {
1210
1640
  type: "input",
1211
1641
  name: "description",
@@ -1242,7 +1672,7 @@ envCommands.command("create-env").description("Create a new environment for a pr
1242
1672
  requireConfirmation: envName === "production"
1243
1673
  };
1244
1674
  await saveProjectConfig(config);
1245
- info(`Updated .envsyncrc with environment "${envName}"`);
1675
+ info(`Updated .syncenvrc with environment "${envName}"`);
1246
1676
  }
1247
1677
  } catch (err) {
1248
1678
  spinner.fail("Failed to create environment");
@@ -1261,7 +1691,7 @@ envCommands.command("delete-env <id>").description("Delete an environment").opti
1261
1691
  }
1262
1692
  const env = await withAuthGuard(() => client.environments.get(id));
1263
1693
  if (!options.force) {
1264
- const { confirm } = await inquirer2.prompt([
1694
+ const { confirm } = await inquirer3.prompt([
1265
1695
  {
1266
1696
  type: "input",
1267
1697
  name: "confirm",
@@ -1282,23 +1712,30 @@ envCommands.command("delete-env <id>").description("Delete an environment").opti
1282
1712
  if (config && config.environments[env.name]) {
1283
1713
  delete config.environments[env.name];
1284
1714
  await saveProjectConfig(config);
1285
- info("Updated .envsyncrc");
1715
+ info("Updated .syncenvrc");
1286
1716
  }
1287
1717
  } catch (err) {
1288
1718
  error("Failed to delete environment:", err.message);
1289
1719
  process.exit(1);
1290
1720
  }
1291
1721
  });
1292
- envCommands.command("sync").description("Sync with remote (pull + merge + push in one command)").option("-p, --project <id>", "project ID").option("-e, --env <name>", "environment name").option("-f, --file <path>", "file path").option("--dry-run", "preview changes without applying").option("--strategy <strategy>", "conflict resolution: local-wins, remote-wins, fail-on-conflict", "interactive").option("-y, --yes", "skip confirmation prompts").action(async (options) => {
1722
+ envCommands.command("sync").description("Sync with remote (pull + merge + push in one command)").option("-p, --project <identifier>", "project ID or name").option("-e, --env <name>", "environment name").option("-f, --file <path>", "file path").option("--dry-run", "preview changes without applying").option(
1723
+ "--strategy <strategy>",
1724
+ "conflict resolution: local-wins, remote-wins, fail-on-conflict",
1725
+ "interactive"
1726
+ ).option("-y, --yes", "skip confirmation prompts").action(async (options) => {
1293
1727
  try {
1294
1728
  if (!isAuthenticated()) {
1295
1729
  error("Not authenticated. Run `syncenv auth login` first.");
1296
1730
  process.exit(1);
1297
1731
  }
1298
1732
  const config = await loadProjectConfig();
1299
- const projectId = options.project || config?.project.id || getConfig("defaultProject");
1733
+ const projectId = options.project || config?.project.id;
1300
1734
  if (!projectId) {
1301
- error("No project specified. Use --project or run `syncenv init`");
1735
+ error("No project specified.");
1736
+ info("Use one of the following:");
1737
+ info(" --project <id> Specify project ID for this command");
1738
+ info(" syncenv project use <id> Set default project for current directory");
1302
1739
  process.exit(1);
1303
1740
  }
1304
1741
  const envName = options.env || config?.defaults.environment || "dev";
@@ -1336,7 +1773,13 @@ envCommands.command("sync").description("Sync with remote (pull + merge + push i
1336
1773
  const decrypted = decryptContent(download.content, download.encryptionIv, dek2);
1337
1774
  if (!options.dryRun) {
1338
1775
  await fs2.writeFile(filePath, decrypted, "utf-8");
1339
- await stateManager.recordSync(projectId, environment.id, download.version || 1, download.contentHash || "", "pull");
1776
+ await stateManager.recordSync(
1777
+ projectId,
1778
+ environment.id,
1779
+ download.version || 1,
1780
+ download.contentHash || "",
1781
+ "pull"
1782
+ );
1340
1783
  }
1341
1784
  spinner.succeed("Pulled from remote");
1342
1785
  success(`Written to ${filePath}`);
@@ -1354,12 +1797,19 @@ envCommands.command("sync").description("Sync with remote (pull + merge + push i
1354
1797
  const dek = decryptDEK(project.encryptedDek, project.dekIv, userKek);
1355
1798
  const remoteVersion = environment.currentVersion || 1;
1356
1799
  const remoteDownload = await client.environments.downloadContent(environment.id);
1357
- const remoteContent = decryptContent(remoteDownload.content, remoteDownload.encryptionIv, dek);
1800
+ const remoteContent = decryptContent(
1801
+ remoteDownload.content,
1802
+ remoteDownload.encryptionIv,
1803
+ dek
1804
+ );
1358
1805
  let mergedContent;
1359
1806
  let mergeSource = "push";
1360
1807
  if (localState && localState.lastSync.version < remoteVersion) {
1361
1808
  spinner.text = "Merging changes...";
1362
- const baseDownload = await client.environments.downloadContent(environment.id, localState.lastSync.version);
1809
+ const baseDownload = await client.environments.downloadContent(
1810
+ environment.id,
1811
+ localState.lastSync.version
1812
+ );
1363
1813
  const baseContent = decryptContent(baseDownload.content, baseDownload.encryptionIv, dek);
1364
1814
  const mergeInput = {
1365
1815
  base: parseEnvFile(baseContent),
@@ -1414,12 +1864,14 @@ envCommands.command("sync").description("Sync with remote (pull + merge + push i
1414
1864
  }
1415
1865
  mergedContent = localContent;
1416
1866
  if (!options.yes) {
1417
- const { confirm } = await inquirer2.prompt([{
1418
- type: "confirm",
1419
- name: "confirm",
1420
- message: `Push local changes to "${envName}"?`,
1421
- default: true
1422
- }]);
1867
+ const { confirm } = await inquirer3.prompt([
1868
+ {
1869
+ type: "confirm",
1870
+ name: "confirm",
1871
+ message: `Push local changes to "${envName}"?`,
1872
+ default: true
1873
+ }
1874
+ ]);
1423
1875
  if (!confirm) {
1424
1876
  info("Cancelled.");
1425
1877
  return;
@@ -1437,7 +1889,13 @@ envCommands.command("sync").description("Sync with remote (pull + merge + push i
1437
1889
  changeSummary: mergeSource === "merge" ? "Synced with merge" : "Synced local changes"
1438
1890
  })
1439
1891
  );
1440
- await stateManager.recordSync(projectId, environment.id, result.version, newContentHash, mergeSource);
1892
+ await stateManager.recordSync(
1893
+ projectId,
1894
+ environment.id,
1895
+ result.version,
1896
+ newContentHash,
1897
+ mergeSource
1898
+ );
1441
1899
  spinner.succeed("Sync complete");
1442
1900
  success(`Environment "${envName}" updated to version ${result.version}`);
1443
1901
  } catch (err) {
@@ -1448,13 +1906,20 @@ envCommands.command("sync").description("Sync with remote (pull + merge + push i
1448
1906
 
1449
1907
  // src/commands/init.ts
1450
1908
  import path from "path";
1451
- import chalk5 from "chalk";
1452
- import { Command as Command4 } from "commander";
1453
- import inquirer3 from "inquirer";
1454
- var initCommand = new Command4("init").description("Initialize a new project configuration").option("-y, --yes", "skip prompts and use defaults").action(async (options) => {
1909
+ import chalk6 from "chalk";
1910
+ import { Command as Command5 } from "commander";
1911
+ import inquirer4 from "inquirer";
1912
+ import { ApiError } from "@uagents/syncenv-client";
1913
+ var initCommand = new Command5("init").description("Initialize a new project configuration").option("-y, --yes", "skip prompts and use defaults").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
1914
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1455
1915
  try {
1916
+ if (isDebug) {
1917
+ console.log(chalk6.dim("\n[DEBUG] Init Command"));
1918
+ console.log(chalk6.dim(`[DEBUG] Yes mode: ${options.yes || false}
1919
+ `));
1920
+ }
1456
1921
  if (await hasProjectConfig()) {
1457
- const { overwrite } = await inquirer3.prompt([
1922
+ const { overwrite } = await inquirer4.prompt([
1458
1923
  {
1459
1924
  type: "confirm",
1460
1925
  name: "overwrite",
@@ -1467,24 +1932,38 @@ var initCommand = new Command4("init").description("Initialize a new project con
1467
1932
  return;
1468
1933
  }
1469
1934
  }
1470
- let projectName;
1935
+ let projectName2;
1471
1936
  let environments;
1937
+ let envFileMappings = {};
1472
1938
  if (options.yes) {
1473
- projectName = path.basename(process.cwd());
1939
+ projectName2 = path.basename(process.cwd());
1474
1940
  environments = ["dev", "staging", "production"];
1941
+ envFileMappings = {
1942
+ dev: ".env",
1943
+ staging: ".env.staging",
1944
+ production: ".env.production"
1945
+ };
1946
+ if (isDebug) {
1947
+ console.log(
1948
+ chalk6.dim(`[DEBUG] Using defaults: ${projectName2}, [${environments.join(", ")}]`)
1949
+ );
1950
+ }
1475
1951
  } else {
1476
- const answers = await inquirer3.prompt([
1952
+ const { projectName: pname } = await inquirer4.prompt([
1477
1953
  {
1478
1954
  type: "input",
1479
1955
  name: "projectName",
1480
1956
  message: "Project name:",
1481
1957
  default: path.basename(process.cwd()),
1482
1958
  validate: (input) => input.length > 0 || "Project name is required"
1483
- },
1959
+ }
1960
+ ]);
1961
+ projectName2 = pname;
1962
+ const { environments: envs } = await inquirer4.prompt([
1484
1963
  {
1485
1964
  type: "checkbox",
1486
1965
  name: "environments",
1487
- message: "Select default environments:",
1966
+ message: "Select environments to create:",
1488
1967
  choices: [
1489
1968
  { name: "dev", checked: true },
1490
1969
  { name: "staging", checked: true },
@@ -1492,70 +1971,164 @@ var initCommand = new Command4("init").description("Initialize a new project con
1492
1971
  { name: "test", checked: false }
1493
1972
  ],
1494
1973
  validate: (input) => input.length > 0 || "Select at least one environment"
1495
- },
1496
- {
1497
- type: "confirm",
1498
- name: "pushOnChange",
1499
- message: "Auto-push on change?",
1500
- default: false
1501
- },
1502
- {
1503
- type: "confirm",
1504
- name: "confirmOverwrite",
1505
- message: "Confirm before overwriting files?",
1506
- default: true
1507
1974
  }
1508
1975
  ]);
1509
- projectName = answers.projectName;
1510
- environments = answers.environments;
1511
- const envConfig = {};
1976
+ environments = envs;
1977
+ console.log(chalk6.dim("\nConfigure environment file mappings:"));
1512
1978
  for (const env of environments) {
1513
- const fileName = env === "dev" ? ".env" : `.env.${env}`;
1514
- envConfig[env] = {
1515
- file: fileName,
1516
- requireConfirmation: env === "production"
1517
- };
1979
+ const defaultFile = env === "dev" ? ".env" : `.env.${env}`;
1980
+ const { fileName } = await inquirer4.prompt([
1981
+ {
1982
+ type: "input",
1983
+ name: "fileName",
1984
+ message: `File for "${env}" environment:`,
1985
+ default: defaultFile,
1986
+ validate: (input) => input.length > 0 || "File name is required"
1987
+ }
1988
+ ]);
1989
+ envFileMappings[env] = fileName;
1518
1990
  }
1519
- const config = {
1520
- project: {
1521
- name: projectName,
1522
- id: ""
1523
- // Will be set after creating project on server
1524
- },
1525
- defaults: {
1526
- environment: "dev",
1527
- pushOnChange: answers.pushOnChange,
1528
- confirmOverwrite: answers.confirmOverwrite
1529
- },
1530
- encryption: {
1531
- algorithm: "AES-256-GCM",
1532
- keyDerivation: "Argon2id"
1533
- },
1534
- environments: envConfig
1991
+ }
1992
+ const envConfig = {};
1993
+ for (const env of environments) {
1994
+ envConfig[env] = {
1995
+ file: envFileMappings[env],
1996
+ requireConfirmation: env === "production"
1535
1997
  };
1536
- await saveProjectConfig(config);
1537
- success(`Created ${chalk5.cyan(".envsyncrc")}`);
1538
- console.log("");
1539
- console.log(chalk5.dim("Next steps:"));
1540
- console.log(chalk5.dim(" 1. Run `syncenv signup` to create an account"));
1541
- console.log(
1542
- chalk5.dim(" 2. Run `syncenv project create` to create the project on the server")
1998
+ }
1999
+ const config = {
2000
+ project: {
2001
+ name: projectName2,
2002
+ id: ""
2003
+ // Will be set after creating project on server
2004
+ },
2005
+ defaults: {
2006
+ environment: "dev",
2007
+ pushOnChange: false,
2008
+ confirmOverwrite: true
2009
+ },
2010
+ encryption: {
2011
+ algorithm: "AES-256-GCM",
2012
+ keyDerivation: "Argon2id"
2013
+ },
2014
+ environments: envConfig
2015
+ };
2016
+ await saveProjectConfig(config);
2017
+ success(`Created ${chalk6.cyan(".syncenvrc")}`);
2018
+ if (isDebug) {
2019
+ console.log(chalk6.dim("[DEBUG] Config saved successfully"));
2020
+ }
2021
+ console.log("");
2022
+ console.log(chalk6.bold("Configuration Summary:"));
2023
+ console.log(` Project: ${projectName2}`);
2024
+ console.log(` Environments:`);
2025
+ for (const env of environments) {
2026
+ console.log(` - ${env}: ${envFileMappings[env]}`);
2027
+ }
2028
+ console.log("");
2029
+ if (!isAuthenticated()) {
2030
+ warning("Not authenticated. Skipping remote resource creation.");
2031
+ info("Run `syncenv auth login` to authenticate, then:");
2032
+ info(" 1. Run `syncenv project create` to create the project on the server");
2033
+ info(" 2. Run `syncenv env push` to upload your .env file");
2034
+ return;
2035
+ }
2036
+ const { createRemote } = await inquirer4.prompt([
2037
+ {
2038
+ type: "confirm",
2039
+ name: "createRemote",
2040
+ message: "Create project on remote server?",
2041
+ default: true
2042
+ }
2043
+ ]);
2044
+ if (!createRemote) {
2045
+ info("Skipping remote project creation.");
2046
+ info("You can create it later with:");
2047
+ info(" syncenv project create # Create project on server");
2048
+ info(" syncenv env push # Push environment files");
2049
+ return;
2050
+ }
2051
+ console.log("");
2052
+ info("Creating remote project...");
2053
+ if (isDebug) {
2054
+ console.log(chalk6.dim("[DEBUG] Getting/unlocking User KEK..."));
2055
+ }
2056
+ const userKek = await getOrUnlockUserKEK();
2057
+ if (!userKek) {
2058
+ error("Your encryption keys are locked.");
2059
+ info("Run `syncenv user-keys unlock` to unlock your keys.");
2060
+ process.exit(1);
2061
+ }
2062
+ if (isDebug) {
2063
+ console.log(chalk6.dim("[DEBUG] User KEK acquired"));
2064
+ }
2065
+ const spinner = createSpinner("Creating project on server...");
2066
+ spinner.start();
2067
+ try {
2068
+ const { generateDEK, encryptDEK } = await import("./crypto-X7MZU7DV.js");
2069
+ const dek = generateDEK();
2070
+ const dekEncryption = encryptDEK(dek, userKek);
2071
+ const project = await withAuthGuard(
2072
+ () => client.projects.create({
2073
+ name: projectName2,
2074
+ slug: projectName2.toLowerCase().replace(/\s+/g, "-"),
2075
+ description: "",
2076
+ encryptedDek: dekEncryption.encrypted,
2077
+ dekIv: dekEncryption.iv
2078
+ })
1543
2079
  );
1544
- console.log(chalk5.dim(" 3. Run `syncenv push` to upload your .env file"));
2080
+ spinner.succeed(`Project "${projectName2}" created on server`);
2081
+ const updatedConfig = await loadProjectConfig();
2082
+ if (updatedConfig) {
2083
+ updatedConfig.project.id = project.id;
2084
+ await saveProjectConfig(updatedConfig);
2085
+ if (isDebug) {
2086
+ console.log(chalk6.dim(`[DEBUG] Updated .syncenvrc with project ID: ${project.id}`));
2087
+ }
2088
+ }
2089
+ console.log("");
2090
+ success("Project created on server successfully!");
2091
+ info("Environments will be created automatically when you push:");
2092
+ info(" syncenv env push -e dev");
2093
+ info(" syncenv env push -e staging");
2094
+ info(" syncenv env push -e production");
2095
+ } catch (err) {
2096
+ spinner.fail("Failed to create project on server");
2097
+ throw err;
1545
2098
  }
1546
2099
  } catch (err) {
2100
+ if (isDebug) {
2101
+ console.log(chalk6.dim(`
2102
+ [DEBUG] Init error: ${err.message}`));
2103
+ console.log(chalk6.dim(`[DEBUG] Error stack: ${err.stack}`));
2104
+ }
2105
+ if (err instanceof ApiError && err.statusCode === 409) {
2106
+ error(`Project "${projectName}" already exists on the server`);
2107
+ info('Use `syncenv project use "' + projectName + '"` to use the existing project');
2108
+ info("Or choose a different project name");
2109
+ process.exit(1);
2110
+ }
1547
2111
  error("Failed to initialize project:", err.message);
1548
2112
  process.exit(1);
1549
2113
  }
1550
2114
  });
1551
2115
 
1552
2116
  // src/commands/project.ts
1553
- import chalk6 from "chalk";
1554
- import { Command as Command5 } from "commander";
1555
- import inquirer4 from "inquirer";
1556
- var projectCommands = new Command5("project").description("Project management commands");
1557
- projectCommands.command("list").alias("ls").description("List all projects").option("-s, --search <query>", "search projects by name").option("-l, --limit <number>", "number of projects to show", "20").option("--cursor <cursor>", "cursor for pagination").action(async (options) => {
2117
+ import chalk7 from "chalk";
2118
+ import { Command as Command6 } from "commander";
2119
+ import inquirer5 from "inquirer";
2120
+ import { ApiError as ApiError2 } from "@uagents/syncenv-client";
2121
+ var projectCommands = new Command6("project").description("Project management commands");
2122
+ projectCommands.command("list").alias("ls").description("List all projects").option("-s, --search <query>", "search projects by name").option("-l, --limit <number>", "number of projects to show", "20").option("--cursor <cursor>", "cursor for pagination").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
2123
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1558
2124
  try {
2125
+ if (isDebug) {
2126
+ console.log(chalk7.dim("\n[DEBUG] List Projects Command"));
2127
+ console.log(chalk7.dim(`[DEBUG] Authenticated: ${isAuthenticated()}`));
2128
+ console.log(chalk7.dim(`[DEBUG] Search: ${options.search || "none"}`));
2129
+ console.log(chalk7.dim(`[DEBUG] Limit: ${options.limit}
2130
+ `));
2131
+ }
1559
2132
  if (!isAuthenticated()) {
1560
2133
  error("Not authenticated. Run `syncenv auth login` first.");
1561
2134
  process.exit(1);
@@ -1573,6 +2146,11 @@ projectCommands.command("list").alias("ls").description("List all projects").opt
1573
2146
  })
1574
2147
  );
1575
2148
  spinner.stop();
2149
+ if (isDebug) {
2150
+ console.log(chalk7.dim(`[DEBUG] Found ${result.data.length} projects`));
2151
+ console.log(chalk7.dim(`[DEBUG] Has more: ${result.hasMore}
2152
+ `));
2153
+ }
1576
2154
  if (result.data.length === 0) {
1577
2155
  if (options.search) {
1578
2156
  info(`No projects found matching "${options.search}"`);
@@ -1581,8 +2159,8 @@ projectCommands.command("list").alias("ls").description("List all projects").opt
1581
2159
  }
1582
2160
  return;
1583
2161
  }
1584
- console.log(chalk6.bold("\nNAME DESCRIPTION UPDATED"));
1585
- console.log(chalk6.dim("\u2500".repeat(70)));
2162
+ console.log(chalk7.bold("\nNAME DESCRIPTION UPDATED"));
2163
+ console.log(chalk7.dim("\u2500".repeat(70)));
1586
2164
  for (const project of result.data) {
1587
2165
  const name = project.name.padEnd(13).slice(0, 13);
1588
2166
  const description = (project.description || "-").padEnd(24).slice(0, 24);
@@ -1590,31 +2168,43 @@ projectCommands.command("list").alias("ls").description("List all projects").opt
1590
2168
  console.log(`${name} ${description} ${updated}`);
1591
2169
  }
1592
2170
  if (result.hasMore) {
1593
- console.log(chalk6.dim(`
2171
+ console.log(chalk7.dim(`
1594
2172
  ... and more projects available`));
1595
2173
  info(`Use --cursor ${result.nextCursor} to see more`);
1596
2174
  }
1597
2175
  console.log("");
1598
2176
  } catch (err) {
2177
+ if (isDebug) {
2178
+ console.log(chalk7.dim(`
2179
+ [DEBUG] List error: ${err.message}`));
2180
+ console.log(chalk7.dim(`[DEBUG] Error stack: ${err.stack}`));
2181
+ }
1599
2182
  error("Failed to list projects:", err.message);
1600
2183
  process.exit(1);
1601
2184
  }
1602
2185
  });
1603
- projectCommands.command("create [name]").description("Create a new project").option("-d, --description <description>", "project description").action(async (name, options) => {
2186
+ projectCommands.command("create [name]").description("Create a new project").option("-d, --description <description>", "project description").option("--debug", "enable debug mode with verbose logging").action(async (name, options) => {
2187
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1604
2188
  try {
2189
+ if (isDebug) {
2190
+ console.log(chalk7.dim("\n[DEBUG] Create Project Command"));
2191
+ console.log(chalk7.dim(`[DEBUG] Authenticated: ${isAuthenticated()}`));
2192
+ console.log(chalk7.dim(`[DEBUG] Name: ${name || "not provided"}
2193
+ `));
2194
+ }
1605
2195
  if (!isAuthenticated()) {
1606
2196
  error("Not authenticated. Run `syncenv auth login` first.");
1607
2197
  process.exit(1);
1608
2198
  }
1609
- let projectName = name;
2199
+ let projectName2 = name;
1610
2200
  let description = options.description;
1611
- if (!projectName) {
2201
+ if (!projectName2) {
1612
2202
  const existingConfig = await loadProjectConfig();
1613
2203
  if (existingConfig) {
1614
- projectName = existingConfig.project.name;
1615
- info(`Using project name from .envsyncrc: ${projectName}`);
2204
+ projectName2 = existingConfig.project.name;
2205
+ info(`Using project name from .syncenvrc: ${projectName2}`);
1616
2206
  } else {
1617
- const answer = await inquirer4.prompt([
2207
+ const answer = await inquirer5.prompt([
1618
2208
  {
1619
2209
  type: "input",
1620
2210
  name: "name",
@@ -1622,11 +2212,11 @@ projectCommands.command("create [name]").description("Create a new project").opt
1622
2212
  validate: (input) => input.length > 0 || "Name is required"
1623
2213
  }
1624
2214
  ]);
1625
- projectName = answer.name;
2215
+ projectName2 = answer.name;
1626
2216
  }
1627
2217
  }
1628
2218
  if (!description) {
1629
- const answer = await inquirer4.prompt([
2219
+ const answer = await inquirer5.prompt([
1630
2220
  {
1631
2221
  type: "input",
1632
2222
  name: "description",
@@ -1635,36 +2225,46 @@ projectCommands.command("create [name]").description("Create a new project").opt
1635
2225
  ]);
1636
2226
  description = answer.description;
1637
2227
  }
2228
+ if (isDebug) {
2229
+ console.log(chalk7.dim("[DEBUG] Getting/unlocking User KEK..."));
2230
+ }
1638
2231
  const userKek = await getOrUnlockUserKEK();
1639
2232
  if (!userKek) {
1640
2233
  error("Your encryption keys are locked.");
1641
2234
  info("Run `syncenv user-keys unlock` to unlock your keys before creating a project.");
1642
2235
  process.exit(1);
1643
2236
  }
2237
+ if (isDebug) {
2238
+ console.log(chalk7.dim("[DEBUG] User KEK acquired\n"));
2239
+ }
1644
2240
  const spinner = createSpinner("Creating project...");
1645
2241
  spinner.start();
1646
2242
  const { generateDEK, encryptDEK } = await import("./crypto-X7MZU7DV.js");
1647
2243
  const dek = generateDEK();
1648
2244
  const dekEncryption = encryptDEK(dek, userKek);
2245
+ if (isDebug) {
2246
+ console.log(chalk7.dim("[DEBUG] Generated DEK and encrypted with User KEK"));
2247
+ console.log(chalk7.dim(`[DEBUG] DEK IV: ${dekEncryption.iv.substring(0, 16)}...`));
2248
+ }
1649
2249
  const project = await withAuthGuard(
1650
2250
  () => client.projects.create({
1651
- name: projectName,
1652
- slug: projectName.toLowerCase().replace(/\s+/g, "-"),
2251
+ name: projectName2,
2252
+ slug: projectName2.toLowerCase().replace(/\s+/g, "-"),
1653
2253
  description,
1654
2254
  encryptedDek: dekEncryption.encrypted,
1655
2255
  dekIv: dekEncryption.iv
1656
2256
  })
1657
2257
  );
1658
- spinner.succeed(`Project "${projectName}" created`);
2258
+ spinner.succeed(`Project "${projectName2}" created`);
1659
2259
  const config = await loadProjectConfig();
1660
2260
  if (config) {
1661
2261
  config.project.id = project.id;
1662
2262
  await saveProjectConfig(config);
1663
- success("Updated .envsyncrc with project ID");
2263
+ success("Updated .syncenvrc with project ID");
1664
2264
  } else {
1665
2265
  await saveProjectConfig({
1666
2266
  project: {
1667
- name: projectName,
2267
+ name: projectName2,
1668
2268
  id: project.id
1669
2269
  },
1670
2270
  defaults: {
@@ -1680,27 +2280,48 @@ projectCommands.command("create [name]").description("Create a new project").opt
1680
2280
  dev: { file: ".env" }
1681
2281
  }
1682
2282
  });
1683
- success("Created .envsyncrc");
2283
+ success("Created .syncenvrc");
1684
2284
  }
1685
2285
  info(`Project ID: ${project.id}`);
1686
2286
  info("Default environments: dev, staging, prod");
1687
2287
  } catch (err) {
2288
+ if (isDebug) {
2289
+ console.log(chalk7.dim(`
2290
+ [DEBUG] Create error: ${err.message}`));
2291
+ console.log(chalk7.dim(`[DEBUG] Error stack: ${err.stack}`));
2292
+ }
2293
+ if (err instanceof ApiError2 && err.statusCode === 409) {
2294
+ error(`Project "${projectName}" already exists`);
2295
+ info("Use `syncenv project list` to see your projects");
2296
+ info('Or use `syncenv project use "' + projectName + '"` to use the existing project');
2297
+ process.exit(1);
2298
+ }
1688
2299
  error("Failed to create project:", err.message);
1689
2300
  process.exit(1);
1690
2301
  }
1691
2302
  });
1692
- projectCommands.command("get <id>").description("Get project details").action(async (id) => {
2303
+ projectCommands.command("get <identifier>").description("Get project details (by ID or name)").option("--debug", "enable debug mode with verbose logging").action(async (identifier, options) => {
2304
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1693
2305
  try {
2306
+ if (isDebug) {
2307
+ console.log(chalk7.dim("\n[DEBUG] Get Project Command"));
2308
+ console.log(chalk7.dim(`[DEBUG] Identifier: ${identifier}
2309
+ `));
2310
+ }
1694
2311
  if (!isAuthenticated()) {
1695
2312
  error("Not authenticated. Run `syncenv auth login` first.");
1696
2313
  process.exit(1);
1697
2314
  }
1698
2315
  const spinner = createSpinner("Fetching project...");
1699
2316
  spinner.start();
1700
- const project = await withAuthGuard(() => client.projects.get(id));
2317
+ const project = await resolveProjectIdentifier(identifier);
1701
2318
  spinner.stop();
1702
- console.log(chalk6.bold("\nProject Details"));
1703
- console.log(chalk6.dim("\u2500".repeat(40)));
2319
+ if (isDebug) {
2320
+ console.log(chalk7.dim(`[DEBUG] Project resolved: ${project.name} (${project.id})
2321
+ `));
2322
+ }
2323
+ console.log(chalk7.bold("\nProject Details"));
2324
+ console.log(chalk7.dim("\u2500".repeat(40)));
1704
2325
  console.log(`Name: ${project.name}`);
1705
2326
  console.log(`ID: ${project.id}`);
1706
2327
  console.log(`Slug: ${project.slug}`);
@@ -1710,28 +2331,40 @@ projectCommands.command("get <id>").description("Get project details").action(as
1710
2331
  console.log(`Created: ${new Date(project.createdAt).toLocaleString()}`);
1711
2332
  console.log(`Updated: ${new Date(project.updatedAt).toLocaleString()}`);
1712
2333
  const { data: environments } = await withAuthGuard(
1713
- () => client.environments.listByProject(id)
2334
+ () => client.environments.listByProject(project.id)
1714
2335
  );
1715
2336
  if (environments?.length > 0) {
1716
- console.log(chalk6.bold("\nEnvironments:"));
2337
+ console.log(chalk7.bold("\nEnvironments:"));
1717
2338
  for (const env of environments) {
1718
2339
  console.log(` \u2022 ${env.name} (v${env.currentVersion || 1})`);
1719
2340
  }
1720
2341
  }
1721
2342
  } catch (err) {
2343
+ if (isDebug) {
2344
+ console.log(chalk7.dim(`
2345
+ [DEBUG] Get error: ${err.message}`));
2346
+ console.log(chalk7.dim(`[DEBUG] Error stack: ${err.stack}`));
2347
+ }
1722
2348
  error("Failed to get project:", err.message);
1723
2349
  process.exit(1);
1724
2350
  }
1725
2351
  });
1726
- projectCommands.command("delete <id>").description("Delete a project").option("-f, --force", "skip confirmation").action(async (id, options) => {
2352
+ projectCommands.command("delete <identifier>").description("Delete a project (by ID or name)").option("-f, --force", "skip confirmation").option("--debug", "enable debug mode with verbose logging").action(async (identifier, options) => {
2353
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1727
2354
  try {
2355
+ if (isDebug) {
2356
+ console.log(chalk7.dim("\n[DEBUG] Delete Project Command"));
2357
+ console.log(chalk7.dim(`[DEBUG] Identifier: ${identifier}`));
2358
+ console.log(chalk7.dim(`[DEBUG] Force: ${options.force}
2359
+ `));
2360
+ }
1728
2361
  if (!isAuthenticated()) {
1729
2362
  error("Not authenticated. Run `syncenv auth login` first.");
1730
2363
  process.exit(1);
1731
2364
  }
1732
- const project = await withAuthGuard(() => client.projects.get(id));
2365
+ const project = await resolveProjectIdentifier(identifier);
1733
2366
  if (!options.force) {
1734
- const { confirm } = await inquirer4.prompt([
2367
+ const { confirm } = await inquirer5.prompt([
1735
2368
  {
1736
2369
  type: "input",
1737
2370
  name: "confirm",
@@ -1746,62 +2379,329 @@ projectCommands.command("delete <id>").description("Delete a project").option("-
1746
2379
  }
1747
2380
  const spinner = createSpinner("Deleting project...");
1748
2381
  spinner.start();
1749
- await withAuthGuard(() => client.projects.delete(id));
2382
+ await withAuthGuard(() => client.projects.delete(project.id));
1750
2383
  spinner.succeed(`Project "${project.name}" deleted`);
1751
2384
  const config = await loadProjectConfig();
1752
- if (config && config.project.id === id) {
2385
+ if (config && config.project.id === project.id) {
1753
2386
  config.project.id = "";
1754
2387
  await saveProjectConfig(config);
1755
- info("Cleared project ID from .envsyncrc");
2388
+ info("Cleared project ID from .syncenvrc");
1756
2389
  }
1757
2390
  } catch (err) {
2391
+ if (isDebug) {
2392
+ console.log(chalk7.dim(`
2393
+ [DEBUG] Delete error: ${err.message}`));
2394
+ console.log(chalk7.dim(`[DEBUG] Error stack: ${err.stack}`));
2395
+ }
1758
2396
  error("Failed to delete project:", err.message);
1759
2397
  process.exit(1);
1760
2398
  }
1761
2399
  });
1762
- projectCommands.command("use <id>").description("Set default project").action(async (id) => {
2400
+ projectCommands.command("use <identifier>").description("Set default project for current directory (updates .syncenvrc)").option("--debug", "enable debug mode with verbose logging").action(async (identifier, options) => {
2401
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1763
2402
  try {
2403
+ if (isDebug) {
2404
+ console.log(chalk7.dim("\n[DEBUG] Use Project Command"));
2405
+ console.log(chalk7.dim(`[DEBUG] Identifier: ${identifier}
2406
+ `));
2407
+ }
1764
2408
  if (!isAuthenticated()) {
1765
2409
  error("Not authenticated. Run `syncenv auth login` first.");
1766
2410
  process.exit(1);
1767
2411
  }
1768
- const project = await withAuthGuard(() => client.projects.get(id));
1769
- setConfig("defaultProject", id);
1770
- success(`Set "${project.name}" as default project`);
2412
+ const project = await resolveProjectIdentifier(identifier);
2413
+ const config = await loadProjectConfig();
2414
+ if (config) {
2415
+ config.project.id = project.id;
2416
+ config.project.name = project.name;
2417
+ await saveProjectConfig(config);
2418
+ if (isDebug) {
2419
+ console.log(chalk7.dim("[DEBUG] Updated .syncenvrc with project ID"));
2420
+ }
2421
+ success(`Set "${project.name}" as project for current directory`);
2422
+ info("This project will be used by default when running commands in this directory");
2423
+ } else {
2424
+ await saveProjectConfig({
2425
+ project: {
2426
+ name: project.name,
2427
+ id: project.id
2428
+ },
2429
+ defaults: {
2430
+ environment: "dev",
2431
+ pushOnChange: false,
2432
+ confirmOverwrite: true
2433
+ },
2434
+ encryption: {
2435
+ algorithm: "AES-256-GCM",
2436
+ keyDerivation: "Argon2id"
2437
+ },
2438
+ environments: {
2439
+ dev: { file: ".env" }
2440
+ }
2441
+ });
2442
+ if (isDebug) {
2443
+ console.log(chalk7.dim("[DEBUG] Created new .syncenvrc with project ID"));
2444
+ }
2445
+ success(`Created .syncenvrc with "${project.name}" as project`);
2446
+ }
1771
2447
  } catch (err) {
2448
+ if (isDebug) {
2449
+ console.log(chalk7.dim(`
2450
+ [DEBUG] Use error: ${err.message}`));
2451
+ console.log(chalk7.dim(`[DEBUG] Error stack: ${err.stack}`));
2452
+ }
1772
2453
  error("Failed to set default project:", err.message);
1773
2454
  process.exit(1);
1774
2455
  }
1775
2456
  });
1776
2457
 
2458
+ // src/commands/setup.ts
2459
+ import chalk8 from "chalk";
2460
+ import { Command as Command7 } from "commander";
2461
+ import inquirer6 from "inquirer";
2462
+ var setupCommand = new Command7("setup").description("First-time setup wizard (login + encryption keys)").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
2463
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
2464
+ console.log(chalk8.bold("\n\u{1F527} SyncEnv Setup Wizard\n"));
2465
+ console.log(chalk8.dim("This wizard will help you set up SyncEnv for the first time.\n"));
2466
+ try {
2467
+ let userEmail = getConfig("userEmail");
2468
+ if (!isAuthenticated() || !userEmail) {
2469
+ info("Step 1: Authentication");
2470
+ console.log(chalk8.dim("You need to log in to use SyncEnv.\n"));
2471
+ const { hasAccount } = await inquirer6.prompt([
2472
+ {
2473
+ type: "confirm",
2474
+ name: "hasAccount",
2475
+ message: "Do you already have a SyncEnv account?",
2476
+ default: true
2477
+ }
2478
+ ]);
2479
+ if (!hasAccount) {
2480
+ console.log("");
2481
+ info("Let's create your account first!");
2482
+ console.log(chalk8.dim("Opening browser for registration...\n"));
2483
+ const apiUrl = getConfig("apiUrl") || "https://syncenv-api.uagents.app";
2484
+ const signupUrl = apiUrl.replace("-api", "") + "/signup";
2485
+ console.log(chalk8.cyan("\u{1F310} Please visit:"));
2486
+ console.log(chalk8.bold(signupUrl));
2487
+ console.log("");
2488
+ const { openedBrowser } = await inquirer6.prompt([
2489
+ {
2490
+ type: "confirm",
2491
+ name: "openedBrowser",
2492
+ message: "Have you completed registration in the browser?",
2493
+ default: true
2494
+ }
2495
+ ]);
2496
+ if (!openedBrowser) {
2497
+ warning("Please complete registration before continuing.");
2498
+ info(`Visit: ${signupUrl}`);
2499
+ process.exit(1);
2500
+ }
2501
+ }
2502
+ console.log("");
2503
+ info("Please log in with your credentials:\n");
2504
+ const { email, password: password2 } = await inquirer6.prompt([
2505
+ {
2506
+ type: "input",
2507
+ name: "email",
2508
+ message: "Email:",
2509
+ validate: (input) => input.includes("@") || "Please enter a valid email"
2510
+ },
2511
+ {
2512
+ type: "password",
2513
+ name: "password",
2514
+ message: "Password:",
2515
+ mask: "*",
2516
+ validate: (input) => input.length > 0 || "Password is required"
2517
+ }
2518
+ ]);
2519
+ const spinner = createSpinner("Logging in...");
2520
+ spinner.start();
2521
+ try {
2522
+ const apiUrl = getConfig("apiUrl") || "https://syncenv-api.uagents.app";
2523
+ const webOrigin = apiUrl.replace("-api", "");
2524
+ const origin = apiUrl.startsWith("http://localhost") ? apiUrl : webOrigin;
2525
+ const loginResponse = await fetch(`${apiUrl}/api/auth/sign-in/email`, {
2526
+ method: "POST",
2527
+ headers: {
2528
+ "Content-Type": "application/json",
2529
+ Origin: origin
2530
+ },
2531
+ body: JSON.stringify({ email, password: password2 })
2532
+ });
2533
+ if (!loginResponse.ok) {
2534
+ const errorData = await loginResponse.json().catch(() => ({ error: "Login failed" }));
2535
+ throw new Error(errorData.error || errorData.message || "Login failed");
2536
+ }
2537
+ const setCookie = loginResponse.headers.getSetCookie?.() || (loginResponse.headers.get("set-cookie") ? [loginResponse.headers.get("set-cookie")] : []);
2538
+ if (setCookie.length > 0) {
2539
+ const { storeCookies } = await import("./cookie-store-UGGEBXBV.js");
2540
+ storeCookies(setCookie);
2541
+ }
2542
+ const result = await loginResponse.json();
2543
+ setConfig("userId", result.user.id);
2544
+ setConfig("userEmail", result.user.email);
2545
+ userEmail = result.user.email;
2546
+ spinner.succeed("Logged in successfully");
2547
+ } catch (err) {
2548
+ spinner.fail("Login failed");
2549
+ throw err;
2550
+ }
2551
+ } else {
2552
+ success(`Already authenticated as ${userEmail}`);
2553
+ }
2554
+ console.log("");
2555
+ info("Step 2: Encryption Keys");
2556
+ console.log(
2557
+ chalk8.dim("SyncEnv uses end-to-end encryption to protect your environment variables.\n")
2558
+ );
2559
+ const hasKeys = await checkUserKeys(isDebug);
2560
+ if (hasKeys) {
2561
+ warning("You already have encryption keys set up.");
2562
+ const { resetKeys } = await inquirer6.prompt([
2563
+ {
2564
+ type: "confirm",
2565
+ name: "resetKeys",
2566
+ message: "Do you want to reset your encryption keys?",
2567
+ default: false
2568
+ }
2569
+ ]);
2570
+ if (!resetKeys) {
2571
+ info("Setup complete! Your existing keys will be used.");
2572
+ printNextSteps();
2573
+ return;
2574
+ }
2575
+ }
2576
+ console.log("");
2577
+ info("Creating encryption keys...");
2578
+ console.log(chalk8.dim("Your login password will be used to encrypt your keys.\n"));
2579
+ const { password } = await inquirer6.prompt([
2580
+ {
2581
+ type: "password",
2582
+ name: "password",
2583
+ message: "Enter your login password:",
2584
+ mask: "*",
2585
+ validate: (input) => {
2586
+ if (input.length < 8) return "Password must be at least 8 characters";
2587
+ return true;
2588
+ }
2589
+ }
2590
+ ]);
2591
+ const keySpinner = createSpinner("Generating encryption keys...");
2592
+ keySpinner.start();
2593
+ try {
2594
+ const { encryptedUserKek, kekIv, kekSalt } = await setupUserKEK(password);
2595
+ await withAuthGuard(
2596
+ () => client.userKeys.create({
2597
+ encryptedUserKek,
2598
+ kekIv,
2599
+ kekSalt
2600
+ })
2601
+ );
2602
+ await unlockAndStoreKEK(
2603
+ password,
2604
+ { encryptedUserKek, kekIv, kekSalt },
2605
+ true
2606
+ // Remember in keychain
2607
+ );
2608
+ keySpinner.succeed("Encryption keys created successfully");
2609
+ } catch (err) {
2610
+ keySpinner.fail("Failed to create encryption keys");
2611
+ throw err;
2612
+ }
2613
+ console.log("");
2614
+ success("\u{1F389} Setup complete!");
2615
+ console.log("");
2616
+ console.log(chalk8.bold("Your encryption keys:"));
2617
+ console.log(" \u2713 Generated and encrypted with your password");
2618
+ console.log(" \u2713 Stored securely on server");
2619
+ console.log(" \u2713 Cached locally for convenience");
2620
+ console.log("");
2621
+ printNextSteps();
2622
+ } catch (err) {
2623
+ if (isDebug) {
2624
+ console.log(chalk8.dim(`
2625
+ [DEBUG] Setup error: ${err.message}`));
2626
+ console.log(chalk8.dim(`[DEBUG] Error stack: ${err.stack}`));
2627
+ }
2628
+ error("Setup failed:", err.message);
2629
+ process.exit(1);
2630
+ }
2631
+ });
2632
+ async function checkUserKeys(debug = false) {
2633
+ try {
2634
+ if (debug) {
2635
+ console.log(chalk8.dim("[DEBUG] Checking if user keys exist..."));
2636
+ }
2637
+ await withAuthGuard(() => client.userKeys.get());
2638
+ return true;
2639
+ } catch (err) {
2640
+ if (err.message?.includes("404") || err.status === 404) {
2641
+ return false;
2642
+ }
2643
+ throw err;
2644
+ }
2645
+ }
2646
+ function printNextSteps() {
2647
+ console.log(chalk8.bold("Next steps:"));
2648
+ console.log(" 1. Run `syncenv init` to initialize a project");
2649
+ console.log(" 2. Run `syncenv env push` to upload your .env file");
2650
+ console.log("");
2651
+ info("For help, run: syncenv --help");
2652
+ }
2653
+
1777
2654
  // src/commands/user-keys.ts
1778
- import chalk7 from "chalk";
1779
- import { Command as Command6 } from "commander";
1780
- import inquirer5 from "inquirer";
1781
- var userKeysCommands = new Command6("user-keys").description(
2655
+ import chalk9 from "chalk";
2656
+ import { Command as Command8 } from "commander";
2657
+ import inquirer7 from "inquirer";
2658
+ var userKeysCommands = new Command8("user-keys").description(
1782
2659
  "User encryption key management commands"
1783
2660
  );
1784
- async function hasUserKeys() {
2661
+ async function hasUserKeys(debug = false) {
1785
2662
  try {
2663
+ if (debug) {
2664
+ console.log(chalk9.dim("[DEBUG] Checking if user keys exist..."));
2665
+ console.log(chalk9.dim(`[DEBUG] API URL: ${client.getBaseUrl?.() || "N/A"}`));
2666
+ }
1786
2667
  await withAuthGuard(() => client.userKeys.get());
2668
+ if (debug) {
2669
+ console.log(chalk9.dim("[DEBUG] User keys found on server"));
2670
+ }
1787
2671
  return true;
1788
2672
  } catch (err) {
1789
- if (err.message?.includes("404")) {
2673
+ if (debug) {
2674
+ console.log(chalk9.dim(`[DEBUG] Error checking keys: ${err.message}`));
2675
+ console.log(chalk9.dim(`[DEBUG] Error status: ${err.status || err.statusCode || "unknown"}`));
2676
+ }
2677
+ if (err.message?.includes("404") || err.status === 404 || err.statusCode === 404) {
2678
+ if (debug) {
2679
+ console.log(chalk9.dim("[DEBUG] User keys not found (404)"));
2680
+ }
1790
2681
  return false;
1791
2682
  }
1792
2683
  throw err;
1793
2684
  }
1794
2685
  }
1795
- userKeysCommands.command("setup").description("Initialize your encryption keys (first time setup)").action(async () => {
2686
+ userKeysCommands.command("setup").description("Initialize your encryption keys (first time setup)").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
2687
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1796
2688
  try {
2689
+ if (isDebug) {
2690
+ console.log(chalk9.dim("\n[DEBUG] Setup Command"));
2691
+ console.log(chalk9.dim(`[DEBUG] Authenticated: ${isAuthenticated()}
2692
+ `));
2693
+ }
1797
2694
  if (!isAuthenticated()) {
1798
2695
  error("Not authenticated. Run `syncenv auth login` first.");
1799
2696
  process.exit(1);
1800
2697
  }
1801
- const hasKeys = await hasUserKeys();
2698
+ if (isDebug) {
2699
+ console.log(chalk9.dim("[DEBUG] Checking if user keys already exist..."));
2700
+ }
2701
+ const hasKeys = await hasUserKeys(isDebug);
1802
2702
  if (hasKeys) {
1803
2703
  warning("You already have encryption keys set up.");
1804
- const { overwrite } = await inquirer5.prompt([
2704
+ const { overwrite } = await inquirer7.prompt([
1805
2705
  {
1806
2706
  type: "confirm",
1807
2707
  name: "overwrite",
@@ -1814,11 +2714,12 @@ userKeysCommands.command("setup").description("Initialize your encryption keys (
1814
2714
  return;
1815
2715
  }
1816
2716
  }
1817
- const { password } = await inquirer5.prompt([
2717
+ info("Your login password will be used to encrypt your encryption keys.");
2718
+ const { password } = await inquirer7.prompt([
1818
2719
  {
1819
2720
  type: "password",
1820
2721
  name: "password",
1821
- message: "Create encryption password:",
2722
+ message: "Enter your login password:",
1822
2723
  mask: "*",
1823
2724
  validate: (input) => {
1824
2725
  if (input.length < 8) return "Password must be at least 8 characters";
@@ -1826,11 +2727,11 @@ userKeysCommands.command("setup").description("Initialize your encryption keys (
1826
2727
  }
1827
2728
  }
1828
2729
  ]);
1829
- await inquirer5.prompt([
2730
+ await inquirer7.prompt([
1830
2731
  {
1831
2732
  type: "password",
1832
2733
  name: "confirmPassword",
1833
- message: "Confirm encryption password:",
2734
+ message: "Confirm your login password:",
1834
2735
  mask: "*",
1835
2736
  validate: (input) => {
1836
2737
  if (input !== password) return "Passwords do not match";
@@ -1857,68 +2758,138 @@ userKeysCommands.command("setup").description("Initialize your encryption keys (
1857
2758
  );
1858
2759
  spinner.succeed("Encryption keys set up successfully");
1859
2760
  success("Your encryption keys are ready");
1860
- info("Your keys are encrypted with your password and stored securely");
1861
- warning("Remember your encryption password! It cannot be recovered.");
2761
+ info("Your keys are encrypted with your login password and stored securely");
2762
+ info("Use your login password to unlock keys in the future");
1862
2763
  } catch (err) {
2764
+ if (isDebug) {
2765
+ console.log(chalk9.dim(`
2766
+ [DEBUG] Setup error: ${err.message}`));
2767
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
2768
+ }
1863
2769
  spinner.fail("Failed to set up encryption keys");
1864
2770
  throw err;
1865
2771
  }
1866
2772
  } catch (err) {
2773
+ if (isDebug) {
2774
+ console.log(chalk9.dim(`
2775
+ [DEBUG] Outer setup error: ${err.message}`));
2776
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
2777
+ }
1867
2778
  error("Setup failed:", err.message);
1868
2779
  process.exit(1);
1869
2780
  }
1870
2781
  });
1871
- userKeysCommands.command("unlock").description("Unlock your encryption keys for this session").option("--remember", "Remember KEK in system keychain (allows unlocking without password)").action(async (options) => {
2782
+ userKeysCommands.command("unlock").description("Unlock your encryption keys for this session").option(
2783
+ "--no-remember",
2784
+ "Do not remember KEK in system keychain (will require password next time)"
2785
+ ).option("--debug", "enable debug mode with verbose logging").action(async (options) => {
2786
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
2787
+ const remember = options.remember !== false;
1872
2788
  try {
2789
+ if (isDebug) {
2790
+ console.log(chalk9.dim("\n[DEBUG] Unlock Command"));
2791
+ console.log(chalk9.dim(`[DEBUG] Authenticated: ${isAuthenticated()}`));
2792
+ console.log(chalk9.dim(`[DEBUG] Remember option: ${remember}
2793
+ `));
2794
+ }
1873
2795
  if (!isAuthenticated()) {
1874
2796
  error("Not authenticated. Run `syncenv auth login` first.");
1875
2797
  process.exit(1);
1876
2798
  }
1877
- const hasKeys = await hasUserKeys();
2799
+ if (isDebug) {
2800
+ console.log(chalk9.dim("[DEBUG] Checking user keys..."));
2801
+ }
2802
+ const hasKeys = await hasUserKeys(isDebug);
1878
2803
  if (!hasKeys) {
1879
2804
  error("You do not have encryption keys set up yet.");
1880
2805
  info("Run `syncenv user-keys setup` to create your keys.");
1881
2806
  process.exit(1);
1882
2807
  }
2808
+ if (isDebug) {
2809
+ console.log(chalk9.dim("[DEBUG] Checking if keys already cached..."));
2810
+ }
1883
2811
  const cached = await getUserKEK();
1884
2812
  if (cached) {
1885
2813
  const remaining = Math.round(getKEKTimeRemaining() / 6e4);
1886
2814
  info(`Your encryption keys are already unlocked (${remaining} minutes remaining).`);
1887
2815
  return;
1888
2816
  }
1889
- const userKeys = await withAuthGuard(() => client.userKeys.get());
1890
- const { password } = await inquirer5.prompt([
2817
+ if (isDebug) {
2818
+ console.log(chalk9.dim("[DEBUG] Keys not cached, need to unlock from server"));
2819
+ }
2820
+ if (isDebug) {
2821
+ console.log(chalk9.dim("[DEBUG] Fetching user keys from server..."));
2822
+ }
2823
+ let userKeys;
2824
+ try {
2825
+ userKeys = await withAuthGuard(() => client.userKeys.get());
2826
+ if (isDebug) {
2827
+ console.log(chalk9.dim("[DEBUG] User keys retrieved successfully"));
2828
+ console.log(chalk9.dim(`[DEBUG] Key version: ${userKeys.version || 1}`));
2829
+ console.log(chalk9.dim(`[DEBUG] Has encryptedUserKek: ${!!userKeys.encryptedUserKek}`));
2830
+ console.log(chalk9.dim(`[DEBUG] Has kekIv: ${!!userKeys.kekIv}`));
2831
+ console.log(chalk9.dim(`[DEBUG] Has kekSalt: ${!!userKeys.kekSalt}`));
2832
+ }
2833
+ } catch (err) {
2834
+ if (isDebug) {
2835
+ console.log(chalk9.dim(`[DEBUG] Failed to fetch user keys: ${err.message}`));
2836
+ console.log(
2837
+ chalk9.dim(`[DEBUG] Error status: ${err.status || err.statusCode || "unknown"}`)
2838
+ );
2839
+ console.log(chalk9.dim(`[DEBUG] Error body: ${JSON.stringify(err.body || {})}`));
2840
+ }
2841
+ throw err;
2842
+ }
2843
+ const { password } = await inquirer7.prompt([
1891
2844
  {
1892
2845
  type: "password",
1893
2846
  name: "password",
1894
- message: "Enter your encryption password:",
2847
+ message: "Enter your login password:",
1895
2848
  mask: "*"
1896
2849
  }
1897
2850
  ]);
1898
2851
  const spinner = createSpinner("Unlocking encryption keys...");
1899
2852
  spinner.start();
1900
2853
  try {
1901
- await unlockAndStoreKEK(password, userKeys, options.remember);
2854
+ await unlockAndStoreKEK(password, userKeys, remember);
1902
2855
  spinner.succeed("Encryption keys unlocked");
1903
2856
  success("Your keys are now available for this session");
1904
- if (options.remember) {
2857
+ if (remember) {
1905
2858
  info("Keys are stored in system keychain for future sessions");
2859
+ } else {
2860
+ info("Keys are not stored (use --no-remember to skip storing in keychain)");
1906
2861
  }
1907
2862
  info("Keys will auto-lock after 30 minutes of inactivity");
1908
2863
  info("Run `syncenv user-keys lock` to lock immediately");
1909
2864
  } catch (err) {
2865
+ if (isDebug) {
2866
+ console.log(chalk9.dim(`
2867
+ [DEBUG] Unlock error: ${err.message}`));
2868
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
2869
+ }
1910
2870
  console.error(`Faild to unlock encryption keys: ${err}`);
1911
2871
  spinner.fail("Failed to unlock encryption keys");
1912
- error("Incorrect password. Please try again.");
2872
+ error("Incorrect login password. Please try again.");
1913
2873
  process.exit(1);
1914
2874
  }
1915
2875
  } catch (err) {
2876
+ if (isDebug) {
2877
+ console.log(chalk9.dim(`
2878
+ [DEBUG] Outer error: ${err.message}`));
2879
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
2880
+ }
1916
2881
  error("Unlock failed:", err.message);
1917
2882
  process.exit(1);
1918
2883
  }
1919
2884
  });
1920
- userKeysCommands.command("lock").description("Lock (clear) your encryption keys from memory").option("--forget", "Also remove from system keychain").action(async (options) => {
2885
+ userKeysCommands.command("lock").description("Lock (clear) your encryption keys from memory").option("--forget", "Also remove from system keychain").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
2886
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1921
2887
  try {
2888
+ if (isDebug) {
2889
+ console.log(chalk9.dim("\n[DEBUG] Lock Command"));
2890
+ console.log(chalk9.dim(`[DEBUG] Forget option: ${options.forget}
2891
+ `));
2892
+ }
1922
2893
  await lockKEK(options.forget);
1923
2894
  success("Encryption keys locked");
1924
2895
  if (options.forget) {
@@ -1928,70 +2899,116 @@ userKeysCommands.command("lock").description("Lock (clear) your encryption keys
1928
2899
  }
1929
2900
  info("Run `syncenv user-keys unlock` to unlock again");
1930
2901
  } catch (err) {
2902
+ if (isDebug) {
2903
+ console.log(chalk9.dim(`
2904
+ [DEBUG] Lock error: ${err.message}`));
2905
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
2906
+ }
1931
2907
  error("Lock failed:", err.message);
1932
2908
  process.exit(1);
1933
2909
  }
1934
2910
  });
1935
- userKeysCommands.command("status").description("Check your encryption key status").action(async () => {
2911
+ userKeysCommands.command("status").description("Check your encryption key status").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
2912
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1936
2913
  try {
2914
+ if (isDebug) {
2915
+ console.log(chalk9.dim("\n[DEBUG] Status Command"));
2916
+ console.log(chalk9.dim(`[DEBUG] Authenticated: ${isAuthenticated()}
2917
+ `));
2918
+ }
1937
2919
  if (!isAuthenticated()) {
1938
2920
  error("Not authenticated. Run `syncenv auth login` first.");
1939
2921
  process.exit(1);
1940
2922
  }
1941
- console.log(chalk7.bold("\nEncryption Key Status"));
1942
- console.log(chalk7.dim("\u2500".repeat(40)));
1943
- const hasKeys = await hasUserKeys();
2923
+ console.log(chalk9.bold("\nEncryption Key Status"));
2924
+ console.log(chalk9.dim("\u2500".repeat(40)));
2925
+ if (isDebug) {
2926
+ console.log(chalk9.dim("[DEBUG] Checking user keys on server..."));
2927
+ }
2928
+ const hasKeys = await hasUserKeys(isDebug);
1944
2929
  if (!hasKeys) {
1945
- console.log(`Setup: ${chalk7.yellow("Not set up")}`);
2930
+ console.log(`Setup: ${chalk9.yellow("Not set up")}`);
1946
2931
  info("\nRun `syncenv user-keys setup` to create your encryption keys");
1947
2932
  return;
1948
2933
  }
1949
- console.log(`Setup: ${chalk7.green("\u2713 Complete")}`);
2934
+ console.log(`Setup: ${chalk9.green("\u2713 Complete")}`);
2935
+ if (isDebug) {
2936
+ console.log(chalk9.dim("[DEBUG] Checking local KEK cache..."));
2937
+ }
1950
2938
  const cached = isKEKCached();
1951
2939
  if (cached) {
1952
2940
  const remaining = Math.round(getKEKTimeRemaining() / 6e4);
1953
- console.log(`Session: ${chalk7.green("\u2713 Unlocked")} (${remaining} min remaining)`);
2941
+ console.log(`Session: ${chalk9.green("\u2713 Unlocked")} (${remaining} min remaining)`);
1954
2942
  info("\nYour encryption keys are active for this session");
1955
2943
  } else {
1956
- console.log(`Session: ${chalk7.yellow("\u25CB Locked")}`);
2944
+ console.log(`Session: ${chalk9.yellow("\u25CB Locked")}`);
1957
2945
  info("\nRun `syncenv user-keys unlock` to unlock your keys");
1958
2946
  }
2947
+ if (isDebug) {
2948
+ console.log(chalk9.dim("[DEBUG] Fetching user keys from server..."));
2949
+ }
1959
2950
  const userKeys = await withAuthGuard(() => client.userKeys.get());
1960
2951
  console.log(`
1961
2952
  Key Version: ${userKeys.version || 1}`);
1962
2953
  console.log(`Algorithm: AES-256-GCM with PBKDF2`);
2954
+ if (isDebug) {
2955
+ console.log(chalk9.dim("\n[DEBUG] Key details:"));
2956
+ console.log(chalk9.dim(`[DEBUG] Has encryptedUserKek: ${!!userKeys.encryptedUserKek}`));
2957
+ console.log(chalk9.dim(`[DEBUG] Has kekIv: ${!!userKeys.kekIv}`));
2958
+ console.log(chalk9.dim(`[DEBUG] Has kekSalt: ${!!userKeys.kekSalt}`));
2959
+ }
1963
2960
  console.log("");
1964
2961
  } catch (err) {
2962
+ if (isDebug) {
2963
+ console.log(chalk9.dim(`
2964
+ [DEBUG] Status error: ${err.message}`));
2965
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
2966
+ }
1965
2967
  error("Status check failed:", err.message);
1966
2968
  process.exit(1);
1967
2969
  }
1968
2970
  });
1969
- userKeysCommands.command("rotate").description("Re-encrypt your keys with a new password").action(async () => {
2971
+ userKeysCommands.command("rotate").description("Re-encrypt your keys with a new password").option("--debug", "enable debug mode with verbose logging").action(async (options) => {
2972
+ const isDebug = options.debug || process.env.SYNCENV_DEBUG === "true";
1970
2973
  try {
2974
+ if (isDebug) {
2975
+ console.log(chalk9.dim("\n[DEBUG] Rotate Command"));
2976
+ console.log(chalk9.dim(`[DEBUG] Authenticated: ${isAuthenticated()}
2977
+ `));
2978
+ }
1971
2979
  if (!isAuthenticated()) {
1972
2980
  error("Not authenticated. Run `syncenv auth login` first.");
1973
2981
  process.exit(1);
1974
2982
  }
1975
- const hasKeys = await hasUserKeys();
2983
+ if (isDebug) {
2984
+ console.log(chalk9.dim("[DEBUG] Checking user keys..."));
2985
+ }
2986
+ const hasKeys = await hasUserKeys(isDebug);
1976
2987
  if (!hasKeys) {
1977
2988
  error("You do not have encryption keys set up yet.");
1978
2989
  info("Run `syncenv user-keys setup` to create your keys.");
1979
2990
  process.exit(1);
1980
2991
  }
2992
+ if (isDebug) {
2993
+ console.log(chalk9.dim("[DEBUG] Fetching user keys from server..."));
2994
+ }
1981
2995
  const userKeys = await withAuthGuard(() => client.userKeys.get());
1982
- const { oldPassword } = await inquirer5.prompt([
2996
+ if (isDebug) {
2997
+ console.log(chalk9.dim("[DEBUG] User keys retrieved successfully"));
2998
+ }
2999
+ const { oldPassword } = await inquirer7.prompt([
1983
3000
  {
1984
3001
  type: "password",
1985
3002
  name: "oldPassword",
1986
- message: "Enter your current encryption password:",
3003
+ message: "Enter your current login password:",
1987
3004
  mask: "*"
1988
3005
  }
1989
3006
  ]);
1990
- const { newPassword } = await inquirer5.prompt([
3007
+ const { newPassword } = await inquirer7.prompt([
1991
3008
  {
1992
3009
  type: "password",
1993
3010
  name: "newPassword",
1994
- message: "Enter your new encryption password:",
3011
+ message: "Enter your new login password:",
1995
3012
  mask: "*",
1996
3013
  validate: (input) => {
1997
3014
  if (input.length < 8) return "Password must be at least 8 characters";
@@ -1999,7 +3016,7 @@ userKeysCommands.command("rotate").description("Re-encrypt your keys with a new
1999
3016
  }
2000
3017
  }
2001
3018
  ]);
2002
- await inquirer5.prompt([
3019
+ await inquirer7.prompt([
2003
3020
  {
2004
3021
  type: "password",
2005
3022
  name: "confirmPassword",
@@ -2033,20 +3050,30 @@ userKeysCommands.command("rotate").description("Re-encrypt your keys with a new
2033
3050
  kekSalt
2034
3051
  })
2035
3052
  );
2036
- const { saveEncryptedKEK, saveKEKPassword, getKEKPassword } = await import("./secure-storage-UEK3LD5L.js");
3053
+ const { saveEncryptedKEK, saveKEKPassword, getKEKPassword } = await import("./secure-storage-AR7HZFTA.js");
2037
3054
  saveEncryptedKEK({ encryptedUserKek, kekIv, kekSalt });
2038
3055
  const existingPassword = await getKEKPassword();
2039
3056
  if (existingPassword) {
2040
3057
  await saveKEKPassword(newPassword);
2041
3058
  }
2042
3059
  spinner.succeed("Keys re-encrypted successfully");
2043
- success("Your encryption password has been changed");
2044
- warning("Remember your new password! It cannot be recovered.");
3060
+ success("Your encryption keys have been updated for your new login password");
3061
+ info("Use your new login password to unlock keys in the future");
2045
3062
  } catch (err) {
3063
+ if (isDebug) {
3064
+ console.log(chalk9.dim(`
3065
+ [DEBUG] Rotation error: ${err.message}`));
3066
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
3067
+ }
2046
3068
  spinner.fail("Failed to re-encrypt keys");
2047
3069
  throw err;
2048
3070
  }
2049
3071
  } catch (err) {
3072
+ if (isDebug) {
3073
+ console.log(chalk9.dim(`
3074
+ [DEBUG] Outer rotation error: ${err.message}`));
3075
+ console.log(chalk9.dim(`[DEBUG] Error stack: ${err.stack}`));
3076
+ }
2050
3077
  error("Rotation failed:", err.message);
2051
3078
  process.exit(1);
2052
3079
  }
@@ -2062,9 +3089,13 @@ try {
2062
3089
  version = process.env.SYNCENV_VERSION || "0.0.0";
2063
3090
  }
2064
3091
  async function main() {
2065
- const program = new Command7();
3092
+ const program = new Command9();
2066
3093
  await loadConfig();
2067
- program.name("syncenv").description("CLI for SyncEnv - Secure environment variable synchronization").version(version).option("-v, --verbose", "enable verbose logging").option("--api-url <url>", "API base URL", process.env.SYNCENV_API_URL || "http://localhost:8787").hook("preAction", (thisCommand) => {
3094
+ program.name("syncenv").description("CLI for SyncEnv - Secure environment variable synchronization").version(version).option("-v, --verbose", "enable verbose logging").option(
3095
+ "--api-url <url>",
3096
+ "API base URL",
3097
+ process.env.SYNCENV_API_URL || "https://syncenv-api.uagents.app"
3098
+ ).hook("preAction", (thisCommand) => {
2068
3099
  const opts = thisCommand.opts();
2069
3100
  if (opts.verbose) {
2070
3101
  process.env.SYNCENV_VERBOSE = "true";
@@ -2073,11 +3104,13 @@ async function main() {
2073
3104
  process.env.SYNCENV_API_URL = opts.apiUrl;
2074
3105
  }
2075
3106
  });
3107
+ program.addCommand(setupCommand);
2076
3108
  program.addCommand(authCommands);
2077
3109
  program.addCommand(projectCommands);
2078
3110
  program.addCommand(envCommands);
2079
3111
  program.addCommand(userKeysCommands);
2080
3112
  program.addCommand(initCommand);
3113
+ program.addCommand(configCommands);
2081
3114
  program.addCommand(doctorCommand);
2082
3115
  program.exitOverride();
2083
3116
  try {
@@ -2092,7 +3125,7 @@ async function main() {
2092
3125
  if (error2.code === "commander.helpDisplayed") {
2093
3126
  process.exit(0);
2094
3127
  }
2095
- console.error(chalk8.red("\nError:"), error2.message || error2);
3128
+ console.error(chalk10.red("\nError:"), error2.message || error2);
2096
3129
  if (process.env.SYNCENV_VERBOSE) {
2097
3130
  console.error(error2.stack);
2098
3131
  }