@uagents/syncenv-cli 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -30,85 +30,129 @@ curl -sL https://syncenv-files.uagents.app/cli/install.sh | INSTALL_DIR=~/.local
30
30
 
31
31
  ## Quick Start
32
32
 
33
- 1. **Initialize a project:**
33
+ ### One-Command Setup (Recommended)
34
34
 
35
- ```bash
36
- syncenv init
37
- ```
38
-
39
- 2. **Create an account or login:**
40
-
41
- ```bash
42
- syncenv auth signup
43
- # or
44
- syncenv auth login
45
- ```
35
+ ```bash
36
+ syncenv setup
37
+ ```
46
38
 
47
- 3. **Create a project on the server:**
39
+ The `setup` wizard will:
40
+ 1. Check if you're authenticated (prompt for login/signup if not)
41
+ 2. Guide you through account creation if needed
42
+ 3. Automatically generate your encryption keys
43
+ 4. Save keys to server and cache locally
48
44
 
49
- ```bash
50
- syncenv project create
51
- ```
45
+ ### Then Initialize Your Project
52
46
 
53
- 4. **Push your .env file:**
47
+ ```bash
48
+ cd ~/my-project
49
+ syncenv init
50
+ syncenv env push
51
+ ```
54
52
 
55
- ```bash
56
- syncenv env push
57
- ```
53
+ ## Commands
58
54
 
59
- 5. **Pull environment variables:**
60
- ```bash
61
- syncenv env pull
62
- ```
55
+ ### Setup
63
56
 
64
- ## Commands
57
+ - `syncenv setup` - First-time setup wizard (login + encryption keys)
58
+ - `--debug` - Enable debug mode with verbose logging
65
59
 
66
60
  ### Authentication
67
61
 
68
- - `syncenv auth signup` - Create a new account
69
62
  - `syncenv auth login` - Login to your account
63
+ - `--debug` - Enable debug mode
70
64
  - `syncenv auth logout` - Logout and clear session
71
65
  - `syncenv auth status` - Check authentication status
66
+ - `syncenv auth signup` - Open browser to create account
72
67
 
73
68
  ### Project Management
74
69
 
75
- - `syncenv init` - Initialize project configuration
70
+ - `syncenv init` - Initialize project configuration (creates `.syncenvrc` and optionally creates project on server)
71
+ - `-y, --yes` - Skip prompts and use defaults
72
+ - `--debug` - Enable debug mode
73
+
74
+ **Note:** Environments are created automatically on first push. Run `syncenv env push -e <env>` to create and upload to an environment.
76
75
  - `syncenv project list` - List all projects
76
+ - `-s, --search <query>` - Search projects by name
77
+ - `-l, --limit <number>` - Number of projects to show (default: 20)
78
+ - `--cursor <cursor>` - Cursor for pagination
79
+ - `--debug` - Enable debug mode
77
80
  - `syncenv project create [name]` - Create a new project
78
- - `syncenv project get <id>` - Get project details
79
- - `syncenv project delete <id>` - Delete a project
80
- - `syncenv project use <id>` - Set default project
81
+ - `-d, --description <description>` - Project description
82
+ - `--debug` - Enable debug mode
83
+ - `syncenv project get <identifier>` - Get project details. Identifier can be project ID (`proj_xxx`) or project name
84
+ - `--debug` - Enable debug mode
85
+ - `syncenv project delete <identifier>` - Delete a project. Identifier can be project ID (`proj_xxx`) or project name
86
+ - `-f, --force` - Skip confirmation
87
+ - `--debug` - Enable debug mode
88
+ - `syncenv project use <identifier>` - Set default project for current directory (updates `.syncenvrc`). Identifier can be project ID (`proj_xxx`) or project name
89
+ - `--debug` - Enable debug mode
90
+
91
+ ### Configuration Management
92
+
93
+ - `syncenv config list` - List all configuration values
94
+ - `--debug` - Enable debug mode
95
+ - `syncenv config get <key>` - Get a configuration value
96
+ - `--debug` - Enable debug mode
97
+ - `syncenv config set <key> [value]` - Set a configuration value
98
+ - `--debug` - Enable debug mode
99
+ - `syncenv config delete <key>` - Delete a configuration value (reset to default)
100
+ - `--debug` - Enable debug mode
101
+ - `syncenv config reset` - Reset all configuration to defaults
102
+ - `-f, --force` - Skip confirmation
103
+ - `--debug` - Enable debug mode
104
+ - `syncenv config path` - Show configuration file path
81
105
 
82
106
  ### Environment Variables
83
107
 
84
108
  - `syncenv env push` - Push .env file to server
85
- - `-p, --project <id>` - Project ID
86
- - `-e, --env <name>` - Environment name
109
+ - `-p, --project <id>` - Project ID (or use `.syncenvrc` project.id)
110
+ - `-e, --env <name>` - Environment name (default: dev)
87
111
  - `-f, --file <path>` - File path
88
112
  - `-m, --message <message>` - Change description
89
-
113
+ - `--force` - Force push without conflict check
114
+ - `--strategy <strategy>` - Merge strategy on conflict
115
+ - `--debug` - Enable debug mode
90
116
  - `syncenv env pull` - Pull .env file from server
91
117
  - `-p, --project <id>` - Project ID
92
118
  - `-e, --env <name>` - Environment name
93
119
  - `-f, --file <path>` - Output file path
94
120
  - `-v, --version <number>` - Specific version to pull
95
121
  - `-m, --merge` - Merge with existing file
96
-
122
+ - `--debug` - Enable debug mode
123
+ - `syncenv env sync` - Smart sync (pull + merge + push)
124
+ - `--strategy <strategy>` - Conflict resolution: `interactive`, `local-wins`, `remote-wins`, `fail-on-conflict`
125
+ - `--dry-run` - Preview changes without applying
126
+ - `-y, --yes` - Skip confirmation prompts
127
+ - `--debug` - Enable debug mode
97
128
  - `syncenv env history` - Show version history
98
129
  - `-l, --limit <number>` - Number of versions to show
99
-
130
+ - `--debug` - Enable debug mode
100
131
  - `syncenv env diff <v1> <v2>` - Compare two versions
101
-
132
+ - `--debug` - Enable debug mode
102
133
  - `syncenv env rollback <version>` - Rollback to a specific version
103
-
104
- - `syncenv env sync` - Smart sync (pull + merge + push)
105
- - `--dry-run` - Preview changes without applying
106
- - `--strategy <strategy>` - Conflict resolution: `local-wins`, `remote-wins`, `interactive`
107
- - `-y, --yes` - Skip confirmation prompts
134
+ - `-f, --force` - Skip confirmation
135
+ - `--debug` - Enable debug mode
136
+
137
+ ### User Keys (Encryption Management)
138
+
139
+ - `syncenv user-keys setup` - Initialize encryption keys (first time)
140
+ - `--debug` - Enable debug mode
141
+ - `syncenv user-keys unlock` - Unlock encryption keys for session
142
+ - `--no-remember` - Do not store in keychain
143
+ - `--debug` - Enable debug mode
144
+ - `syncenv user-keys lock` - Lock keys from memory
145
+ - `--forget` - Also remove from keychain
146
+ - `--debug` - Enable debug mode
147
+ - `syncenv user-keys status` - Check encryption key status
148
+ - `--debug` - Enable debug mode
149
+ - `syncenv user-keys rotate` - Re-encrypt keys with new password
150
+ - `--debug` - Enable debug mode
108
151
 
109
152
  ### Device Management
110
153
 
111
154
  - `syncenv device list` - List all devices
155
+ - `--debug` - Enable debug mode
112
156
  - `syncenv device authorize <id>` - Authorize a pending device
113
157
  - `syncenv device revoke <id>` - Revoke a device
114
158
  - `syncenv device remove <id>` - Remove a device
@@ -117,6 +161,7 @@ curl -sL https://syncenv-files.uagents.app/cli/install.sh | INSTALL_DIR=~/.local
117
161
  ### Diagnostics
118
162
 
119
163
  - `syncenv doctor` - Diagnose configuration and connectivity issues
164
+ - `--debug` - Enable debug mode
120
165
 
121
166
  ## Smart Merge
122
167
 
@@ -151,14 +196,18 @@ syncenv env push --strategy=fail-on-conflict
151
196
 
152
197
  ### CLI Config
153
198
 
154
- Stored in OS-specific config directory (e.g., `~/.config/syncenv/` on Linux).
199
+ Stored in OS-specific config directory:
200
+ - **macOS:** `~/Library/Application Support/syncenv/`
201
+ - **Linux:** `~/.config/syncenv/`
202
+ - **Windows:** `%APPDATA%/syncenv/`
155
203
 
156
- - `apiUrl` - API base URL
157
- - `authToken` - Authentication token
204
+ Contains:
205
+ - `apiUrl` - API base URL (default: https://syncenv-api.uagents.app)
206
+ - `autoLockMinutes` - Auto-lock timeout in minutes (0 to disable, default: 30)
158
207
  - `userId` - User ID
159
- - `deviceId` - Device ID
208
+ - `userEmail` - User email
160
209
 
161
- ### Project Config (`.envsyncrc`)
210
+ ### Project Config (`.syncenvrc`)
162
211
 
163
212
  Example:
164
213
 
@@ -188,16 +237,125 @@ environments:
188
237
 
189
238
  ## Environment Variables
190
239
 
191
- - `SYNCENV_API_URL` - API base URL (default: http://localhost:8787)
240
+ - `SYNCENV_API_URL` - API base URL (default: https://syncenv-api.uagents.app)
241
+ - `SYNCENV_DEBUG` - Enable debug mode (set to `true`)
192
242
  - `SYNCENV_VERBOSE` - Enable verbose logging
193
243
 
244
+ ## Common Workflows
245
+
246
+ ### First-Time Setup
247
+
248
+ ```bash
249
+ # Install CLI
250
+ curl -sL https://syncenv-files.uagents.app/cli/install.sh | bash
251
+
252
+ # Run setup wizard
253
+ syncenv setup
254
+
255
+ # Initialize your project
256
+ cd ~/my-project
257
+ syncenv init
258
+
259
+ # Push your first environment
260
+ syncenv env push -m "Initial setup"
261
+ ```
262
+
263
+ ### Daily Development
264
+
265
+ ```bash
266
+ # Unlock keys (cached in keychain by default)
267
+ syncenv user-keys unlock
268
+
269
+ # Sync environment (pull + merge + push)
270
+ syncenv env sync
271
+
272
+ # Make changes to .env...
273
+
274
+ # Push with message
275
+ syncenv env push -m "Added Stripe API keys"
276
+
277
+ # Lock when done (optional on personal machine)
278
+ syncenv user-keys lock
279
+ ```
280
+
281
+ ### Working with Multiple Environments
282
+
283
+ ```bash
284
+ # Push to specific environment
285
+ syncenv env push -e production -m "Database migration config"
286
+
287
+ # Pull from staging
288
+ syncenv env pull -e staging
289
+
290
+ # Sync with specific strategy
291
+ syncenv env sync -e production --strategy=interactive
292
+ ```
293
+
294
+ ### CI/CD Pipeline
295
+
296
+ ```bash
297
+ # Non-interactive pull
298
+ syncenv env pull -e production -y
299
+
300
+ # Or with specific strategy
301
+ syncenv env sync -e production --strategy=local-wins -y
302
+ ```
303
+
304
+ ## Troubleshooting
305
+
306
+ ### Debug Mode
307
+
308
+ Most commands support `--debug` flag:
309
+
310
+ ```bash
311
+ syncenv setup --debug
312
+ syncenv auth login --debug
313
+ syncenv env push --debug
314
+ ```
315
+
316
+ ### Common Issues
317
+
318
+ **"Not authenticated" error:**
319
+ ```bash
320
+ syncenv auth login
321
+ # or
322
+ syncenv setup
323
+ ```
324
+
325
+ **"Encryption keys are locked" error:**
326
+ ```bash
327
+ syncenv user-keys unlock
328
+ ```
329
+
330
+ **"No project specified" error:**
331
+ ```bash
332
+ # Option 1: Set project in .syncenvrc (supports ID or name)
333
+ syncenv project use my-project-name
334
+ # or
335
+ syncenv project use proj_xxx
336
+
337
+ # Option 2: Use --project flag
338
+ syncenv env push --project proj_xxx
339
+ ```
340
+
341
+ **Merge conflicts:**
342
+ ```bash
343
+ # Interactive resolution
344
+ syncenv env sync --strategy=interactive
345
+
346
+ # Or force local changes
347
+ syncenv env push --force
348
+ ```
349
+
194
350
  ## Security
195
351
 
196
352
  - All encryption/decryption happens locally on your device
197
- - Master password never leaves your device
353
+ - Your login password never leaves your device
198
354
  - Data Encryption Keys (DEK) are encrypted with your Key Encryption Key (KEK)
199
- - KEK is derived from your master password using Argon2id
355
+ - KEK is derived from your login password using PBKDF2
200
356
  - Server only stores encrypted data and cannot decrypt your environment variables
357
+ - Keys are cached in system keychain by default (use `--no-remember` to disable)
358
+ - Always run `syncenv user-keys lock` when done on shared machines
201
359
 
202
360
  ## License
203
361
 
@@ -2,7 +2,7 @@ import {
2
2
  deleteConfig,
3
3
  getConfig,
4
4
  setConfig
5
- } from "./chunk-OVEYHV4C.js";
5
+ } from "./chunk-YXE467TO.js";
6
6
 
7
7
  // src/secure-storage.ts
8
8
  var SERVICE_NAME = "syncenv-cli";
@@ -6,6 +6,7 @@ import chalk from "chalk";
6
6
  import Conf from "conf";
7
7
  import yaml from "js-yaml";
8
8
  import { z } from "zod";
9
+ import inquirer from "inquirer";
9
10
  var ProjectConfigSchema = z.object({
10
11
  project: z.object({
11
12
  name: z.string(),
@@ -28,7 +29,7 @@ var ProjectConfigSchema = z.object({
28
29
  )
29
30
  });
30
31
  var defaultConfig = {
31
- apiUrl: process.env.SYNCENV_API_URL || "https://syncenv.uagents.app"
32
+ apiUrl: process.env.SYNCENV_API_URL || "https://syncenv-api.uagents.app"
32
33
  };
33
34
  var conf;
34
35
  try {
@@ -57,6 +58,9 @@ function setConfig(key, value) {
57
58
  function deleteConfig(key) {
58
59
  conf.delete(key);
59
60
  }
61
+ function clearConfig() {
62
+ conf.clear();
63
+ }
60
64
  function getConfigPath() {
61
65
  return conf.path;
62
66
  }
@@ -65,7 +69,7 @@ function isAuthenticated() {
65
69
  return !!cookieHeader && cookieHeader.includes("better-auth");
66
70
  }
67
71
  async function loadProjectConfig(cwd = process.cwd()) {
68
- const configPath = path.join(cwd, ".envsyncrc");
72
+ const configPath = path.join(cwd, ".syncenvrc");
69
73
  try {
70
74
  const content = await fs.readFile(configPath, "utf-8");
71
75
  const parsed = yaml.load(content);
@@ -74,17 +78,17 @@ async function loadProjectConfig(cwd = process.cwd()) {
74
78
  if (error.code === "ENOENT") {
75
79
  return null;
76
80
  }
77
- throw new Error(`Failed to parse .envsyncrc: ${error.message}`);
81
+ throw new Error(`Failed to parse .syncenvrc: ${error.message}`);
78
82
  }
79
83
  }
80
84
  async function saveProjectConfig(config, cwd = process.cwd()) {
81
- const configPath = path.join(cwd, ".envsyncrc");
85
+ const configPath = path.join(cwd, ".syncenvrc");
82
86
  const content = yaml.dump(config, { indent: 2 });
83
87
  await fs.writeFile(configPath, content, "utf-8");
84
88
  }
85
89
  async function hasProjectConfig(cwd = process.cwd()) {
86
90
  try {
87
- await fs.access(path.join(cwd, ".envsyncrc"));
91
+ await fs.access(path.join(cwd, ".syncenvrc"));
88
92
  return true;
89
93
  } catch {
90
94
  return false;
@@ -99,10 +103,15 @@ async function loadConfig() {
99
103
  };
100
104
  }
101
105
  async function fetchWithCookies(input, init) {
106
+ const apiUrl = conf.get("apiUrl") || defaultConfig.apiUrl;
107
+ const webOrigin = apiUrl.replace("-api", "");
108
+ const origin = apiUrl.startsWith("http://localhost") ? apiUrl : webOrigin;
102
109
  if (input instanceof Request) {
103
110
  const url2 = input.url;
104
111
  const cookieHeader2 = getCookieHeader();
105
- const headers2 = {};
112
+ const headers2 = {
113
+ Origin: origin
114
+ };
106
115
  input.headers.forEach((value, key) => {
107
116
  headers2[key] = value;
108
117
  });
@@ -123,7 +132,8 @@ async function fetchWithCookies(input, init) {
123
132
  const url = input.toString();
124
133
  const cookieHeader = getCookieHeader();
125
134
  const headers = {
126
- ...init?.headers
135
+ ...init?.headers,
136
+ Origin: origin
127
137
  };
128
138
  if (cookieHeader) {
129
139
  headers["Cookie"] = cookieHeader;
@@ -149,7 +159,7 @@ function createClient() {
149
159
  }
150
160
  var client = createClient();
151
161
  async function clearAuthState() {
152
- const { clearCachedKEK, deleteKEKPassword, deleteEncryptedKEK } = await import("./secure-storage-UEK3LD5L.js");
162
+ const { clearCachedKEK, deleteKEKPassword, deleteEncryptedKEK } = await import("./secure-storage-AR7HZFTA.js");
153
163
  clearCachedKEK();
154
164
  await deleteKEKPassword();
155
165
  deleteEncryptedKEK();
@@ -181,7 +191,7 @@ function handleAuthError(_error) {
181
191
  process.exit(1);
182
192
  }
183
193
  async function getOrUnlockUserKEK() {
184
- const { getCachedKEK, cacheKEKInMemory, getKEKPassword, saveEncryptedKEK } = await import("./secure-storage-UEK3LD5L.js");
194
+ const { getCachedKEK, cacheKEKInMemory, getKEKPassword, saveEncryptedKEK } = await import("./secure-storage-AR7HZFTA.js");
185
195
  const cached = getCachedKEK();
186
196
  if (cached) return cached;
187
197
  const password = await getKEKPassword();
@@ -196,7 +206,7 @@ async function getOrUnlockUserKEK() {
196
206
  userKeys.kekSalt
197
207
  );
198
208
  if (!kek) {
199
- const { deleteKEKPassword } = await import("./secure-storage-UEK3LD5L.js");
209
+ const { deleteKEKPassword } = await import("./secure-storage-AR7HZFTA.js");
200
210
  await deleteKEKPassword();
201
211
  return null;
202
212
  }
@@ -225,6 +235,34 @@ async function withAuthGuard(apiCall) {
225
235
  throw error;
226
236
  }
227
237
  }
238
+ async function resolveProjectIdentifier(identifier) {
239
+ if (identifier.startsWith("proj_")) {
240
+ try {
241
+ return await withAuthGuard(() => client.projects.get(identifier));
242
+ } catch (err) {
243
+ if (err instanceof ApiError && err.statusCode === 404) {
244
+ throw new Error(`Project not found: ${identifier}`);
245
+ }
246
+ throw err;
247
+ }
248
+ }
249
+ const result = await withAuthGuard(() => client.projects.getByName(identifier));
250
+ if ("error" in result && "projects" in result) {
251
+ const { selectedId } = await inquirer.prompt([
252
+ {
253
+ type: "list",
254
+ name: "selectedId",
255
+ message: `Multiple projects match "${identifier}". Select one:`,
256
+ choices: result.projects.map((p) => ({
257
+ name: `${p.name} (ID: ${p.id}, Updated: ${new Date(p.updatedAt).toLocaleDateString()})`,
258
+ value: p.id
259
+ }))
260
+ }
261
+ ]);
262
+ return result.projects.find((p) => p.id === selectedId);
263
+ }
264
+ return result;
265
+ }
228
266
 
229
267
  // src/cookie-store.ts
230
268
  function parseSetCookie(setCookieValue) {
@@ -320,6 +358,7 @@ export {
320
358
  getConfig,
321
359
  setConfig,
322
360
  deleteConfig,
361
+ clearConfig,
323
362
  getConfigPath,
324
363
  isAuthenticated,
325
364
  loadProjectConfig,
@@ -329,5 +368,6 @@ export {
329
368
  client,
330
369
  clearAuthState,
331
370
  getOrUnlockUserKEK,
332
- withAuthGuard
371
+ withAuthGuard,
372
+ resolveProjectIdentifier
333
373
  };
@@ -5,7 +5,7 @@ import {
5
5
  loadCookies,
6
6
  saveCookies,
7
7
  storeCookies
8
- } from "./chunk-OVEYHV4C.js";
8
+ } from "./chunk-YXE467TO.js";
9
9
  export {
10
10
  clearCookies,
11
11
  getCookieHeader,