@uagents/syncenv-cli 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +204 -0
- package/dist/chunk-F7ZZUTRW.js +403 -0
- package/dist/chunk-JBMZAAVP.js +176 -0
- package/dist/chunk-NV6H5OGL.js +218 -0
- package/dist/chunk-OVEYHV4C.js +333 -0
- package/dist/cookie-store-Z6DNTUGS.js +16 -0
- package/dist/crypto-X7MZU7DV.js +58 -0
- package/dist/index.js +2091 -0
- package/dist/interactive-GOIXZ6UH.js +6 -0
- package/dist/secure-storage-UEK3LD5L.js +35 -0
- package/package.json +53 -0
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
// src/config/index.ts
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import { SyncEnvClient, ApiError } from "@uagents/syncenv-client";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import Conf from "conf";
|
|
7
|
+
import yaml from "js-yaml";
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
var ProjectConfigSchema = z.object({
|
|
10
|
+
project: z.object({
|
|
11
|
+
name: z.string(),
|
|
12
|
+
id: z.string()
|
|
13
|
+
}),
|
|
14
|
+
defaults: z.object({
|
|
15
|
+
environment: z.string().default("dev"),
|
|
16
|
+
pushOnChange: z.boolean().default(false),
|
|
17
|
+
confirmOverwrite: z.boolean().default(true)
|
|
18
|
+
}),
|
|
19
|
+
encryption: z.object({
|
|
20
|
+
algorithm: z.string().default("AES-256-GCM"),
|
|
21
|
+
keyDerivation: z.string().default("Argon2id")
|
|
22
|
+
}),
|
|
23
|
+
environments: z.record(
|
|
24
|
+
z.object({
|
|
25
|
+
file: z.string(),
|
|
26
|
+
requireConfirmation: z.boolean().optional()
|
|
27
|
+
})
|
|
28
|
+
)
|
|
29
|
+
});
|
|
30
|
+
var defaultConfig = {
|
|
31
|
+
apiUrl: process.env.SYNCENV_API_URL || "https://syncenv.uagents.app"
|
|
32
|
+
};
|
|
33
|
+
var conf;
|
|
34
|
+
try {
|
|
35
|
+
conf = new Conf({
|
|
36
|
+
projectName: "syncenv",
|
|
37
|
+
defaults: defaultConfig
|
|
38
|
+
});
|
|
39
|
+
} catch {
|
|
40
|
+
conf = {
|
|
41
|
+
get: (key) => defaultConfig[key],
|
|
42
|
+
set: () => {
|
|
43
|
+
},
|
|
44
|
+
delete: () => {
|
|
45
|
+
},
|
|
46
|
+
clear: () => {
|
|
47
|
+
},
|
|
48
|
+
path: ""
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function getConfig(key) {
|
|
52
|
+
return conf.get(key);
|
|
53
|
+
}
|
|
54
|
+
function setConfig(key, value) {
|
|
55
|
+
conf.set(key, value);
|
|
56
|
+
}
|
|
57
|
+
function deleteConfig(key) {
|
|
58
|
+
conf.delete(key);
|
|
59
|
+
}
|
|
60
|
+
function getConfigPath() {
|
|
61
|
+
return conf.path;
|
|
62
|
+
}
|
|
63
|
+
function isAuthenticated() {
|
|
64
|
+
const cookieHeader = getCookieHeader();
|
|
65
|
+
return !!cookieHeader && cookieHeader.includes("better-auth");
|
|
66
|
+
}
|
|
67
|
+
async function loadProjectConfig(cwd = process.cwd()) {
|
|
68
|
+
const configPath = path.join(cwd, ".envsyncrc");
|
|
69
|
+
try {
|
|
70
|
+
const content = await fs.readFile(configPath, "utf-8");
|
|
71
|
+
const parsed = yaml.load(content);
|
|
72
|
+
return ProjectConfigSchema.parse(parsed);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
if (error.code === "ENOENT") {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
throw new Error(`Failed to parse .envsyncrc: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
async function saveProjectConfig(config, cwd = process.cwd()) {
|
|
81
|
+
const configPath = path.join(cwd, ".envsyncrc");
|
|
82
|
+
const content = yaml.dump(config, { indent: 2 });
|
|
83
|
+
await fs.writeFile(configPath, content, "utf-8");
|
|
84
|
+
}
|
|
85
|
+
async function hasProjectConfig(cwd = process.cwd()) {
|
|
86
|
+
try {
|
|
87
|
+
await fs.access(path.join(cwd, ".envsyncrc"));
|
|
88
|
+
return true;
|
|
89
|
+
} catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function loadConfig() {
|
|
94
|
+
return {
|
|
95
|
+
apiUrl: conf.get("apiUrl") || defaultConfig.apiUrl,
|
|
96
|
+
authToken: conf.get("authToken"),
|
|
97
|
+
userId: conf.get("userId"),
|
|
98
|
+
userEmail: conf.get("userEmail")
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
async function fetchWithCookies(input, init) {
|
|
102
|
+
if (input instanceof Request) {
|
|
103
|
+
const url2 = input.url;
|
|
104
|
+
const cookieHeader2 = getCookieHeader();
|
|
105
|
+
const headers2 = {};
|
|
106
|
+
input.headers.forEach((value, key) => {
|
|
107
|
+
headers2[key] = value;
|
|
108
|
+
});
|
|
109
|
+
if (cookieHeader2) {
|
|
110
|
+
headers2["Cookie"] = cookieHeader2;
|
|
111
|
+
}
|
|
112
|
+
const response2 = await fetch(url2, {
|
|
113
|
+
method: input.method,
|
|
114
|
+
headers: headers2,
|
|
115
|
+
body: input.body
|
|
116
|
+
});
|
|
117
|
+
const setCookie2 = response2.headers.getSetCookie?.() || (response2.headers.get("set-cookie") ? [response2.headers.get("set-cookie")] : []);
|
|
118
|
+
if (setCookie2.length > 0) {
|
|
119
|
+
storeCookies(setCookie2);
|
|
120
|
+
}
|
|
121
|
+
return response2;
|
|
122
|
+
}
|
|
123
|
+
const url = input.toString();
|
|
124
|
+
const cookieHeader = getCookieHeader();
|
|
125
|
+
const headers = {
|
|
126
|
+
...init?.headers
|
|
127
|
+
};
|
|
128
|
+
if (cookieHeader) {
|
|
129
|
+
headers["Cookie"] = cookieHeader;
|
|
130
|
+
}
|
|
131
|
+
const response = await fetch(url, {
|
|
132
|
+
...init,
|
|
133
|
+
headers
|
|
134
|
+
});
|
|
135
|
+
const setCookie = response.headers.getSetCookie?.() || (response.headers.get("set-cookie") ? [response.headers.get("set-cookie")] : []);
|
|
136
|
+
if (setCookie.length > 0) {
|
|
137
|
+
storeCookies(setCookie);
|
|
138
|
+
}
|
|
139
|
+
return response;
|
|
140
|
+
}
|
|
141
|
+
function createClient() {
|
|
142
|
+
const apiUrl = conf.get("apiUrl") || defaultConfig.apiUrl;
|
|
143
|
+
return new SyncEnvClient({
|
|
144
|
+
baseUrl: apiUrl,
|
|
145
|
+
token: "",
|
|
146
|
+
// Cookie-based auth, no token needed
|
|
147
|
+
fetch: fetchWithCookies
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
var client = createClient();
|
|
151
|
+
async function clearAuthState() {
|
|
152
|
+
const { clearCachedKEK, deleteKEKPassword, deleteEncryptedKEK } = await import("./secure-storage-UEK3LD5L.js");
|
|
153
|
+
clearCachedKEK();
|
|
154
|
+
await deleteKEKPassword();
|
|
155
|
+
deleteEncryptedKEK();
|
|
156
|
+
clearCookies();
|
|
157
|
+
deleteConfig("userId");
|
|
158
|
+
deleteConfig("userEmail");
|
|
159
|
+
deleteConfig("authToken");
|
|
160
|
+
deleteConfig("refreshToken");
|
|
161
|
+
deleteConfig("deviceId");
|
|
162
|
+
deleteConfig("deviceName");
|
|
163
|
+
deleteConfig("devicePublicKey");
|
|
164
|
+
deleteConfig("encryptedDevicePrivateKey");
|
|
165
|
+
client.setToken("");
|
|
166
|
+
}
|
|
167
|
+
function handleAuthError(_error) {
|
|
168
|
+
clearAuthState().catch(() => {
|
|
169
|
+
});
|
|
170
|
+
console.error(chalk.red("\n\u2717 Session expired or invalid"));
|
|
171
|
+
console.error(chalk.yellow("Your authentication session has expired or was revoked."));
|
|
172
|
+
console.error(chalk.yellow("This can happen when:"));
|
|
173
|
+
console.error(chalk.yellow(" \u2022 You changed your password"));
|
|
174
|
+
console.error(chalk.yellow(" \u2022 You logged out from another device"));
|
|
175
|
+
console.error(chalk.yellow(" \u2022 Your session expired"));
|
|
176
|
+
console.error(chalk.yellow(" \u2022 Your account was disabled"));
|
|
177
|
+
console.error();
|
|
178
|
+
console.error(chalk.cyan("Please login again:"));
|
|
179
|
+
console.error(chalk.bold(" syncenv auth login"));
|
|
180
|
+
console.error();
|
|
181
|
+
process.exit(1);
|
|
182
|
+
}
|
|
183
|
+
async function getOrUnlockUserKEK() {
|
|
184
|
+
const { getCachedKEK, cacheKEKInMemory, getKEKPassword, saveEncryptedKEK } = await import("./secure-storage-UEK3LD5L.js");
|
|
185
|
+
const cached = getCachedKEK();
|
|
186
|
+
if (cached) return cached;
|
|
187
|
+
const password = await getKEKPassword();
|
|
188
|
+
if (!password) return null;
|
|
189
|
+
try {
|
|
190
|
+
const userKeys = await withAuthGuard(() => client.userKeys.get());
|
|
191
|
+
const { unlockUserKEK } = await import("./crypto-X7MZU7DV.js");
|
|
192
|
+
const kek = await unlockUserKEK(
|
|
193
|
+
password,
|
|
194
|
+
userKeys.encryptedUserKek,
|
|
195
|
+
userKeys.kekIv,
|
|
196
|
+
userKeys.kekSalt
|
|
197
|
+
);
|
|
198
|
+
if (!kek) {
|
|
199
|
+
const { deleteKEKPassword } = await import("./secure-storage-UEK3LD5L.js");
|
|
200
|
+
await deleteKEKPassword();
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
saveEncryptedKEK({
|
|
204
|
+
encryptedUserKek: userKeys.encryptedUserKek,
|
|
205
|
+
kekIv: userKeys.kekIv,
|
|
206
|
+
kekSalt: userKeys.kekSalt,
|
|
207
|
+
version: userKeys.version
|
|
208
|
+
});
|
|
209
|
+
cacheKEKInMemory(kek);
|
|
210
|
+
return kek;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (err instanceof ApiError && err.statusCode === 404) {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async function withAuthGuard(apiCall) {
|
|
219
|
+
try {
|
|
220
|
+
return await apiCall();
|
|
221
|
+
} catch (error) {
|
|
222
|
+
if (error instanceof ApiError && error.statusCode === 401) {
|
|
223
|
+
handleAuthError(error);
|
|
224
|
+
}
|
|
225
|
+
throw error;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// src/cookie-store.ts
|
|
230
|
+
function parseSetCookie(setCookieValue) {
|
|
231
|
+
const parts = setCookieValue.split(";").map((p) => p.trim());
|
|
232
|
+
if (parts.length === 0) return null;
|
|
233
|
+
const [nameValue, ...attrParts] = parts;
|
|
234
|
+
const eqIndex = nameValue.indexOf("=");
|
|
235
|
+
if (eqIndex === -1) return null;
|
|
236
|
+
const name = nameValue.slice(0, eqIndex);
|
|
237
|
+
const value = nameValue.slice(eqIndex + 1);
|
|
238
|
+
const attributes = {};
|
|
239
|
+
for (const attr of attrParts) {
|
|
240
|
+
const [attrName, attrValue] = attr.split("=");
|
|
241
|
+
attributes[attrName.toLowerCase()] = attrValue ?? true;
|
|
242
|
+
}
|
|
243
|
+
return { name, value, attributes };
|
|
244
|
+
}
|
|
245
|
+
function loadCookies() {
|
|
246
|
+
const stored = getConfig("cookies");
|
|
247
|
+
if (!stored) return [];
|
|
248
|
+
try {
|
|
249
|
+
const parsed = JSON.parse(stored);
|
|
250
|
+
return parsed.map((c) => ({
|
|
251
|
+
...c,
|
|
252
|
+
expires: c.expires ? new Date(c.expires) : void 0
|
|
253
|
+
}));
|
|
254
|
+
} catch {
|
|
255
|
+
return [];
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
function saveCookies(cookies) {
|
|
259
|
+
if (cookies.length === 0) {
|
|
260
|
+
deleteConfig("cookies");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
setConfig("cookies", JSON.stringify(cookies));
|
|
264
|
+
}
|
|
265
|
+
function storeCookies(setCookieHeaders) {
|
|
266
|
+
if (!setCookieHeaders) return;
|
|
267
|
+
const headers = Array.isArray(setCookieHeaders) ? setCookieHeaders : [setCookieHeaders];
|
|
268
|
+
const cookies = loadCookies();
|
|
269
|
+
for (const header of headers) {
|
|
270
|
+
const parsed = parseSetCookie(header);
|
|
271
|
+
if (!parsed) continue;
|
|
272
|
+
const existingIndex = cookies.findIndex((c) => c.name === parsed.name);
|
|
273
|
+
if (existingIndex !== -1) {
|
|
274
|
+
cookies.splice(existingIndex, 1);
|
|
275
|
+
}
|
|
276
|
+
const maxAge = parsed.attributes["max-age"];
|
|
277
|
+
if (parsed.value === "" || typeof maxAge === "string" && parseInt(maxAge, 10) <= 0) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const cookie = {
|
|
281
|
+
name: parsed.name,
|
|
282
|
+
value: parsed.value,
|
|
283
|
+
path: typeof parsed.attributes.path === "string" ? parsed.attributes.path : "/",
|
|
284
|
+
httpOnly: parsed.attributes.httponly === true,
|
|
285
|
+
secure: parsed.attributes.secure === true,
|
|
286
|
+
sameSite: typeof parsed.attributes.samesite === "string" ? parsed.attributes.samesite : void 0
|
|
287
|
+
};
|
|
288
|
+
if (parsed.attributes.expires) {
|
|
289
|
+
cookie.expires = new Date(parsed.attributes.expires);
|
|
290
|
+
}
|
|
291
|
+
cookies.push(cookie);
|
|
292
|
+
}
|
|
293
|
+
saveCookies(cookies);
|
|
294
|
+
}
|
|
295
|
+
function getCookieHeader() {
|
|
296
|
+
const cookies = loadCookies();
|
|
297
|
+
const now = /* @__PURE__ */ new Date();
|
|
298
|
+
const validCookies = cookies.filter((c) => !c.expires || c.expires > now);
|
|
299
|
+
if (validCookies.length !== cookies.length) {
|
|
300
|
+
saveCookies(validCookies);
|
|
301
|
+
}
|
|
302
|
+
if (validCookies.length === 0) return void 0;
|
|
303
|
+
return validCookies.map((c) => `${c.name}=${c.value}`).join("; ");
|
|
304
|
+
}
|
|
305
|
+
function clearCookies() {
|
|
306
|
+
deleteConfig("cookies");
|
|
307
|
+
}
|
|
308
|
+
function hasValidSession() {
|
|
309
|
+
const header = getCookieHeader();
|
|
310
|
+
return !!header && header.includes("better-auth");
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export {
|
|
314
|
+
loadCookies,
|
|
315
|
+
saveCookies,
|
|
316
|
+
storeCookies,
|
|
317
|
+
getCookieHeader,
|
|
318
|
+
clearCookies,
|
|
319
|
+
hasValidSession,
|
|
320
|
+
getConfig,
|
|
321
|
+
setConfig,
|
|
322
|
+
deleteConfig,
|
|
323
|
+
getConfigPath,
|
|
324
|
+
isAuthenticated,
|
|
325
|
+
loadProjectConfig,
|
|
326
|
+
saveProjectConfig,
|
|
327
|
+
hasProjectConfig,
|
|
328
|
+
loadConfig,
|
|
329
|
+
client,
|
|
330
|
+
clearAuthState,
|
|
331
|
+
getOrUnlockUserKEK,
|
|
332
|
+
withAuthGuard
|
|
333
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clearCookies,
|
|
3
|
+
getCookieHeader,
|
|
4
|
+
hasValidSession,
|
|
5
|
+
loadCookies,
|
|
6
|
+
saveCookies,
|
|
7
|
+
storeCookies
|
|
8
|
+
} from "./chunk-OVEYHV4C.js";
|
|
9
|
+
export {
|
|
10
|
+
clearCookies,
|
|
11
|
+
getCookieHeader,
|
|
12
|
+
hasValidSession,
|
|
13
|
+
loadCookies,
|
|
14
|
+
saveCookies,
|
|
15
|
+
storeCookies
|
|
16
|
+
};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import {
|
|
2
|
+
base64ToBuffer,
|
|
3
|
+
bufferToBase64,
|
|
4
|
+
decryptContent,
|
|
5
|
+
decryptDEK,
|
|
6
|
+
decryptDek,
|
|
7
|
+
decryptEnvFile,
|
|
8
|
+
decryptUserKEK,
|
|
9
|
+
deriveKEK,
|
|
10
|
+
deriveKeys,
|
|
11
|
+
encryptContent,
|
|
12
|
+
encryptDEK,
|
|
13
|
+
encryptDek,
|
|
14
|
+
encryptEnvFile,
|
|
15
|
+
encryptUserKEK,
|
|
16
|
+
exportDEK,
|
|
17
|
+
exportKEK,
|
|
18
|
+
generateContentHash,
|
|
19
|
+
generateDEK,
|
|
20
|
+
generateDek,
|
|
21
|
+
generateSalt,
|
|
22
|
+
generateUserKEK,
|
|
23
|
+
hashContent,
|
|
24
|
+
importDEK,
|
|
25
|
+
importKEK,
|
|
26
|
+
reencryptUserKEK,
|
|
27
|
+
setupUserKEK,
|
|
28
|
+
unlockUserKEK
|
|
29
|
+
} from "./chunk-NV6H5OGL.js";
|
|
30
|
+
export {
|
|
31
|
+
base64ToBuffer,
|
|
32
|
+
bufferToBase64,
|
|
33
|
+
decryptContent,
|
|
34
|
+
decryptDEK,
|
|
35
|
+
decryptDek,
|
|
36
|
+
decryptEnvFile,
|
|
37
|
+
decryptUserKEK,
|
|
38
|
+
deriveKEK,
|
|
39
|
+
deriveKeys,
|
|
40
|
+
encryptContent,
|
|
41
|
+
encryptDEK,
|
|
42
|
+
encryptDek,
|
|
43
|
+
encryptEnvFile,
|
|
44
|
+
encryptUserKEK,
|
|
45
|
+
exportDEK,
|
|
46
|
+
exportKEK,
|
|
47
|
+
generateContentHash,
|
|
48
|
+
generateDEK,
|
|
49
|
+
generateDek,
|
|
50
|
+
generateSalt,
|
|
51
|
+
generateUserKEK,
|
|
52
|
+
hashContent,
|
|
53
|
+
importDEK,
|
|
54
|
+
importKEK,
|
|
55
|
+
reencryptUserKEK,
|
|
56
|
+
setupUserKEK,
|
|
57
|
+
unlockUserKEK
|
|
58
|
+
};
|