appwrite-utils-cli 1.6.3 → 1.6.5
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/CONFIG_TODO.md +1189 -0
- package/SERVICE_IMPLEMENTATION_REPORT.md +462 -0
- package/dist/cli/commands/configCommands.js +7 -1
- package/dist/collections/attributes.js +102 -30
- package/dist/collections/indexes.js +6 -1
- package/dist/collections/methods.js +4 -5
- package/dist/config/ConfigManager.d.ts +445 -0
- package/dist/config/ConfigManager.js +625 -0
- package/dist/config/index.d.ts +8 -0
- package/dist/config/index.js +7 -0
- package/dist/config/services/ConfigDiscoveryService.d.ts +126 -0
- package/dist/config/services/ConfigDiscoveryService.js +374 -0
- package/dist/config/services/ConfigLoaderService.d.ts +105 -0
- package/dist/config/services/ConfigLoaderService.js +410 -0
- package/dist/config/services/ConfigMergeService.d.ts +208 -0
- package/dist/config/services/ConfigMergeService.js +307 -0
- package/dist/config/services/ConfigValidationService.d.ts +214 -0
- package/dist/config/services/ConfigValidationService.js +310 -0
- package/dist/config/services/SessionAuthService.d.ts +225 -0
- package/dist/config/services/SessionAuthService.js +456 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.d.ts +1 -0
- package/dist/config/services/__tests__/ConfigMergeService.test.js +271 -0
- package/dist/config/services/index.d.ts +13 -0
- package/dist/config/services/index.js +10 -0
- package/dist/interactiveCLI.js +8 -6
- package/dist/main.js +14 -19
- package/dist/migrations/yaml/YamlImportConfigLoader.d.ts +1 -1
- package/dist/shared/operationQueue.js +1 -1
- package/dist/utils/ClientFactory.d.ts +87 -0
- package/dist/utils/ClientFactory.js +164 -0
- package/dist/utils/getClientFromConfig.js +4 -3
- package/dist/utils/helperFunctions.d.ts +1 -0
- package/dist/utils/helperFunctions.js +21 -5
- package/dist/utils/yamlConverter.d.ts +2 -2
- package/dist/utils/yamlConverter.js +2 -2
- package/dist/utilsController.d.ts +18 -15
- package/dist/utilsController.js +83 -131
- package/package.json +1 -1
- package/src/cli/commands/configCommands.ts +8 -1
- package/src/collections/attributes.ts +118 -31
- package/src/collections/indexes.ts +7 -1
- package/src/collections/methods.ts +4 -6
- package/src/config/ConfigManager.ts +808 -0
- package/src/config/index.ts +10 -0
- package/src/config/services/ConfigDiscoveryService.ts +463 -0
- package/src/config/services/ConfigLoaderService.ts +560 -0
- package/src/config/services/ConfigMergeService.ts +386 -0
- package/src/config/services/ConfigValidationService.ts +394 -0
- package/src/config/services/SessionAuthService.ts +565 -0
- package/src/config/services/__tests__/ConfigMergeService.test.ts +351 -0
- package/src/config/services/index.ts +29 -0
- package/src/interactiveCLI.ts +9 -7
- package/src/main.ts +14 -24
- package/src/shared/operationQueue.ts +1 -1
- package/src/utils/ClientFactory.ts +186 -0
- package/src/utils/getClientFromConfig.ts +4 -3
- package/src/utils/helperFunctions.ts +27 -7
- package/src/utils/yamlConverter.ts +4 -4
- package/src/utilsController.ts +99 -187
@@ -0,0 +1,456 @@
|
|
1
|
+
import { existsSync, readFileSync, statSync } from "node:fs";
|
2
|
+
import { join } from "node:path";
|
3
|
+
import { homedir } from "node:os";
|
4
|
+
import { createHash } from "node:crypto";
|
5
|
+
import { MessageFormatter } from "../../shared/messageFormatter.js";
|
6
|
+
import { logger } from "../../shared/logging.js";
|
7
|
+
/**
|
8
|
+
* Service for managing Appwrite CLI session authentication with intelligent caching
|
9
|
+
*
|
10
|
+
* This service provides centralized session management with minimal file I/O through
|
11
|
+
* a multi-layered caching strategy:
|
12
|
+
* - Time-based cache (5 minute TTL)
|
13
|
+
* - File modification time validation
|
14
|
+
* - Content hash validation
|
15
|
+
*
|
16
|
+
* @example
|
17
|
+
* ```typescript
|
18
|
+
* const sessionService = new SessionAuthService();
|
19
|
+
* const session = await sessionService.findSession("https://cloud.appwrite.io/v1", "my-project-id");
|
20
|
+
*
|
21
|
+
* if (session && sessionService.isValidSession(session)) {
|
22
|
+
* // Use session for authentication
|
23
|
+
* console.log(`Authenticated as ${session.email}`);
|
24
|
+
* }
|
25
|
+
* ```
|
26
|
+
*/
|
27
|
+
export class SessionAuthService {
|
28
|
+
cache = null;
|
29
|
+
CACHE_TTL = 5 * 60 * 1000; // 5 minutes
|
30
|
+
PREFS_PATH;
|
31
|
+
/**
|
32
|
+
* Creates a new SessionAuthService instance
|
33
|
+
*
|
34
|
+
* @param prefsPath - Optional custom path to prefs.json (defaults to ~/.appwrite/prefs.json)
|
35
|
+
*/
|
36
|
+
constructor(prefsPath) {
|
37
|
+
this.PREFS_PATH = prefsPath || join(homedir(), ".appwrite", "prefs.json");
|
38
|
+
}
|
39
|
+
/**
|
40
|
+
* Load session preferences from ~/.appwrite/prefs.json with intelligent caching
|
41
|
+
*
|
42
|
+
* Caching Strategy:
|
43
|
+
* 1. Return cached data if TTL has not expired
|
44
|
+
* 2. Validate file modification time - reload if changed
|
45
|
+
* 3. Validate content hash - reload if content changed
|
46
|
+
* 4. Cache is automatically invalidated after 5 minutes
|
47
|
+
*
|
48
|
+
* @returns Session preferences object or null if file doesn't exist or is invalid
|
49
|
+
*
|
50
|
+
* @example
|
51
|
+
* ```typescript
|
52
|
+
* const prefs = await sessionService.loadSessionPrefs();
|
53
|
+
* if (prefs) {
|
54
|
+
* console.log(`Found ${Object.keys(prefs).length} stored sessions`);
|
55
|
+
* }
|
56
|
+
* ```
|
57
|
+
*/
|
58
|
+
async loadSessionPrefs() {
|
59
|
+
try {
|
60
|
+
// Check if file exists
|
61
|
+
if (!existsSync(this.PREFS_PATH)) {
|
62
|
+
logger.debug("Session prefs file does not exist", { path: this.PREFS_PATH });
|
63
|
+
return null;
|
64
|
+
}
|
65
|
+
// Get current file stats
|
66
|
+
const stats = statSync(this.PREFS_PATH);
|
67
|
+
const currentMtime = stats.mtimeMs;
|
68
|
+
const now = Date.now();
|
69
|
+
// Check if cache is valid
|
70
|
+
if (this.cache) {
|
71
|
+
const cacheAge = now - this.cache.timestamp;
|
72
|
+
const mtimeMatches = this.cache.mtime === currentMtime;
|
73
|
+
const cacheNotExpired = cacheAge < this.CACHE_TTL;
|
74
|
+
// If cache is still valid (not expired, mtime unchanged)
|
75
|
+
if (cacheNotExpired && mtimeMatches) {
|
76
|
+
logger.debug("Using cached session prefs (valid cache)", {
|
77
|
+
cacheAge: `${Math.round(cacheAge / 1000)}s`,
|
78
|
+
ttl: `${this.CACHE_TTL / 1000}s`
|
79
|
+
});
|
80
|
+
return this.cache.data;
|
81
|
+
}
|
82
|
+
// If TTL expired or mtime changed, we need to validate content
|
83
|
+
if (!cacheNotExpired) {
|
84
|
+
logger.debug("Session cache TTL expired, revalidating", {
|
85
|
+
cacheAge: `${Math.round(cacheAge / 1000)}s`
|
86
|
+
});
|
87
|
+
}
|
88
|
+
else if (!mtimeMatches) {
|
89
|
+
logger.debug("Session file modified, revalidating", {
|
90
|
+
oldMtime: this.cache.mtime,
|
91
|
+
newMtime: currentMtime
|
92
|
+
});
|
93
|
+
}
|
94
|
+
}
|
95
|
+
// Read and parse file
|
96
|
+
const prefsContent = readFileSync(this.PREFS_PATH, "utf-8");
|
97
|
+
const contentHash = this.hashContent(prefsContent);
|
98
|
+
// Check if content actually changed (even if mtime changed)
|
99
|
+
if (this.cache && this.cache.contentHash === contentHash) {
|
100
|
+
logger.debug("Session file content unchanged, updating cache timestamp");
|
101
|
+
this.cache.timestamp = now;
|
102
|
+
this.cache.mtime = currentMtime;
|
103
|
+
return this.cache.data;
|
104
|
+
}
|
105
|
+
// Parse new content
|
106
|
+
const prefs = JSON.parse(prefsContent);
|
107
|
+
// Update cache
|
108
|
+
this.cache = {
|
109
|
+
data: prefs,
|
110
|
+
mtime: currentMtime,
|
111
|
+
contentHash,
|
112
|
+
timestamp: now
|
113
|
+
};
|
114
|
+
logger.debug("Session prefs loaded and cached", {
|
115
|
+
projectCount: Object.keys(prefs).length,
|
116
|
+
path: this.PREFS_PATH
|
117
|
+
});
|
118
|
+
return prefs;
|
119
|
+
}
|
120
|
+
catch (error) {
|
121
|
+
MessageFormatter.warning("Failed to load Appwrite session preferences", { prefix: "Session" });
|
122
|
+
logger.error("Error loading session prefs", {
|
123
|
+
error: error instanceof Error ? error.message : String(error),
|
124
|
+
path: this.PREFS_PATH
|
125
|
+
});
|
126
|
+
return null;
|
127
|
+
}
|
128
|
+
}
|
129
|
+
/**
|
130
|
+
* Find session authentication info for a specific endpoint and project
|
131
|
+
*
|
132
|
+
* This method searches the session preferences for a matching endpoint and project ID.
|
133
|
+
* Both endpoint and projectId must match for a session to be returned.
|
134
|
+
*
|
135
|
+
* @param endpoint - Appwrite endpoint URL (e.g., "https://cloud.appwrite.io/v1")
|
136
|
+
* @param projectId - Appwrite project ID
|
137
|
+
* @returns Session info or null if no matching session found
|
138
|
+
*
|
139
|
+
* @example
|
140
|
+
* ```typescript
|
141
|
+
* const session = await sessionService.findSession(
|
142
|
+
* "https://cloud.appwrite.io/v1",
|
143
|
+
* "my-project-id"
|
144
|
+
* );
|
145
|
+
*
|
146
|
+
* if (session) {
|
147
|
+
* console.log(`Using session for ${session.email}`);
|
148
|
+
* }
|
149
|
+
* ```
|
150
|
+
*/
|
151
|
+
async findSession(endpoint, projectId) {
|
152
|
+
const prefs = await this.loadSessionPrefs();
|
153
|
+
if (!prefs || !prefs[projectId]) {
|
154
|
+
logger.debug("No session found for project", { projectId });
|
155
|
+
return null;
|
156
|
+
}
|
157
|
+
const sessionData = prefs[projectId];
|
158
|
+
// Validate session data structure
|
159
|
+
if (!sessionData.endpoint || !sessionData.cookie) {
|
160
|
+
MessageFormatter.warning(`Invalid session data for project ${projectId}`, { prefix: "Session" });
|
161
|
+
logger.warn("Invalid session data structure", {
|
162
|
+
projectId,
|
163
|
+
hasEndpoint: !!sessionData.endpoint,
|
164
|
+
hasCookie: !!sessionData.cookie
|
165
|
+
});
|
166
|
+
return null;
|
167
|
+
}
|
168
|
+
// Normalize endpoints for comparison (remove trailing slashes, case-insensitive)
|
169
|
+
const normalizedSessionEndpoint = this.normalizeEndpoint(sessionData.endpoint);
|
170
|
+
const normalizedRequestEndpoint = this.normalizeEndpoint(endpoint);
|
171
|
+
if (normalizedSessionEndpoint !== normalizedRequestEndpoint) {
|
172
|
+
logger.debug("Session endpoint mismatch", {
|
173
|
+
projectId,
|
174
|
+
sessionEndpoint: sessionData.endpoint,
|
175
|
+
requestedEndpoint: endpoint
|
176
|
+
});
|
177
|
+
return null;
|
178
|
+
}
|
179
|
+
// Return session info
|
180
|
+
return {
|
181
|
+
endpoint: sessionData.endpoint,
|
182
|
+
projectId,
|
183
|
+
email: sessionData.email,
|
184
|
+
cookie: sessionData.cookie,
|
185
|
+
expiresAt: sessionData.expiresAt
|
186
|
+
};
|
187
|
+
}
|
188
|
+
/**
|
189
|
+
* Validate if a session appears to be valid
|
190
|
+
*
|
191
|
+
* Performs structural validation of the session:
|
192
|
+
* - Cookie format validation (JWT-like structure)
|
193
|
+
* - Expiration check (if expiresAt is provided)
|
194
|
+
* - Basic cookie integrity checks
|
195
|
+
*
|
196
|
+
* Note: This does NOT verify the session with the server.
|
197
|
+
*
|
198
|
+
* @param session - Session info to validate
|
199
|
+
* @returns true if session appears valid, false otherwise
|
200
|
+
*
|
201
|
+
* @example
|
202
|
+
* ```typescript
|
203
|
+
* const session = await sessionService.findSession(endpoint, projectId);
|
204
|
+
* if (session && sessionService.isValidSession(session)) {
|
205
|
+
* // Session is structurally valid
|
206
|
+
* }
|
207
|
+
* ```
|
208
|
+
*/
|
209
|
+
isValidSession(session) {
|
210
|
+
if (!session || typeof session !== "object") {
|
211
|
+
return false;
|
212
|
+
}
|
213
|
+
// Validate required fields
|
214
|
+
if (!session.cookie || !session.endpoint || !session.projectId) {
|
215
|
+
logger.debug("Session missing required fields", {
|
216
|
+
hasCookie: !!session.cookie,
|
217
|
+
hasEndpoint: !!session.endpoint,
|
218
|
+
hasProjectId: !!session.projectId
|
219
|
+
});
|
220
|
+
return false;
|
221
|
+
}
|
222
|
+
// Validate cookie format
|
223
|
+
if (!this.isValidSessionCookie(session.cookie)) {
|
224
|
+
logger.debug("Session cookie failed validation", {
|
225
|
+
projectId: session.projectId
|
226
|
+
});
|
227
|
+
return false;
|
228
|
+
}
|
229
|
+
// Check expiration if provided
|
230
|
+
if (session.expiresAt) {
|
231
|
+
const expirationTime = new Date(session.expiresAt).getTime();
|
232
|
+
const now = Date.now();
|
233
|
+
if (expirationTime < now) {
|
234
|
+
logger.debug("Session expired", {
|
235
|
+
projectId: session.projectId,
|
236
|
+
expiresAt: session.expiresAt,
|
237
|
+
expiredBy: `${Math.round((now - expirationTime) / 1000)}s`
|
238
|
+
});
|
239
|
+
return false;
|
240
|
+
}
|
241
|
+
}
|
242
|
+
return true;
|
243
|
+
}
|
244
|
+
/**
|
245
|
+
* Get detailed authentication status for a given endpoint and project
|
246
|
+
*
|
247
|
+
* Provides comprehensive authentication status including:
|
248
|
+
* - Session validation
|
249
|
+
* - API key presence
|
250
|
+
* - Detailed diagnostic information
|
251
|
+
* - Actionable error messages
|
252
|
+
*
|
253
|
+
* @param endpoint - Appwrite endpoint URL
|
254
|
+
* @param projectId - Appwrite project ID
|
255
|
+
* @param apiKey - Optional API key for authentication
|
256
|
+
* @param session - Optional pre-loaded session (avoids re-loading)
|
257
|
+
* @returns Detailed authentication status
|
258
|
+
*
|
259
|
+
* @example
|
260
|
+
* ```typescript
|
261
|
+
* const status = await sessionService.getAuthenticationStatus(
|
262
|
+
* "https://cloud.appwrite.io/v1",
|
263
|
+
* "my-project-id",
|
264
|
+
* process.env.APPWRITE_API_KEY
|
265
|
+
* );
|
266
|
+
*
|
267
|
+
* console.log(status.message);
|
268
|
+
* if (status.hasValidSession) {
|
269
|
+
* console.log(`Authenticated as ${status.sessionInfo?.email}`);
|
270
|
+
* } else if (status.hasApiKey) {
|
271
|
+
* console.log("Using API key authentication");
|
272
|
+
* }
|
273
|
+
* ```
|
274
|
+
*/
|
275
|
+
async getAuthenticationStatus(endpoint, projectId, apiKey, session) {
|
276
|
+
// Load session if not provided
|
277
|
+
const sessionInfo = session !== undefined
|
278
|
+
? session
|
279
|
+
: await this.findSession(endpoint, projectId);
|
280
|
+
// Check API key presence
|
281
|
+
const hasApiKey = !!(apiKey && apiKey.trim().length > 0);
|
282
|
+
// If no session exists
|
283
|
+
if (!sessionInfo) {
|
284
|
+
if (hasApiKey) {
|
285
|
+
return {
|
286
|
+
hasValidSession: false,
|
287
|
+
hasApiKey: true,
|
288
|
+
sessionExists: false,
|
289
|
+
endpointMatches: false,
|
290
|
+
cookieValid: false,
|
291
|
+
message: "Using API key authentication (no session found)",
|
292
|
+
authMethod: "apikey"
|
293
|
+
};
|
294
|
+
}
|
295
|
+
return {
|
296
|
+
hasValidSession: false,
|
297
|
+
hasApiKey: false,
|
298
|
+
sessionExists: false,
|
299
|
+
endpointMatches: false,
|
300
|
+
cookieValid: false,
|
301
|
+
message: `No authentication found for project ${projectId}. Run 'appwrite login' or provide an API key.`,
|
302
|
+
authMethod: "none"
|
303
|
+
};
|
304
|
+
}
|
305
|
+
// Validate session
|
306
|
+
const endpointMatches = this.normalizeEndpoint(sessionInfo.endpoint) === this.normalizeEndpoint(endpoint);
|
307
|
+
const cookieValid = this.isValidSessionCookie(sessionInfo.cookie);
|
308
|
+
const hasValidSession = endpointMatches && cookieValid;
|
309
|
+
// Generate status message
|
310
|
+
let message = "";
|
311
|
+
let authMethod = "none";
|
312
|
+
if (!endpointMatches) {
|
313
|
+
message = `Session endpoint mismatch. Config: ${endpoint}, Session: ${sessionInfo.endpoint}`;
|
314
|
+
authMethod = hasApiKey ? "apikey" : "none";
|
315
|
+
}
|
316
|
+
else if (!cookieValid) {
|
317
|
+
message = `Session cookie is invalid or expired for project ${projectId}`;
|
318
|
+
authMethod = hasApiKey ? "apikey" : "none";
|
319
|
+
}
|
320
|
+
else {
|
321
|
+
message = `Valid session found for ${sessionInfo.email || "unknown user"}`;
|
322
|
+
authMethod = "session";
|
323
|
+
}
|
324
|
+
return {
|
325
|
+
hasValidSession,
|
326
|
+
hasApiKey,
|
327
|
+
sessionExists: true,
|
328
|
+
endpointMatches,
|
329
|
+
cookieValid,
|
330
|
+
sessionInfo,
|
331
|
+
message,
|
332
|
+
authMethod
|
333
|
+
};
|
334
|
+
}
|
335
|
+
/**
|
336
|
+
* Manually invalidate the cache
|
337
|
+
*
|
338
|
+
* Forces the next loadSessionPrefs() call to read from disk.
|
339
|
+
* Useful after external changes to prefs.json (e.g., after login/logout).
|
340
|
+
*
|
341
|
+
* @example
|
342
|
+
* ```typescript
|
343
|
+
* // After user logs in
|
344
|
+
* sessionService.invalidateCache();
|
345
|
+
* const newSession = await sessionService.loadSessionPrefs();
|
346
|
+
* ```
|
347
|
+
*/
|
348
|
+
invalidateCache() {
|
349
|
+
logger.debug("Session cache manually invalidated");
|
350
|
+
this.cache = null;
|
351
|
+
}
|
352
|
+
/**
|
353
|
+
* Normalize an endpoint URL for comparison
|
354
|
+
*
|
355
|
+
* @param url - Endpoint URL to normalize
|
356
|
+
* @returns Normalized URL (lowercase, no trailing slashes)
|
357
|
+
*/
|
358
|
+
normalizeEndpoint(url) {
|
359
|
+
return url.replace(/\/+$/, "").toLowerCase();
|
360
|
+
}
|
361
|
+
/**
|
362
|
+
* Validate session cookie format
|
363
|
+
*
|
364
|
+
* Performs basic validation to check if a cookie appears to be a valid Appwrite session:
|
365
|
+
* - Minimum length check
|
366
|
+
* - JWT-like structure (contains dots)
|
367
|
+
* - Valid character set
|
368
|
+
* - Multi-part structure
|
369
|
+
*
|
370
|
+
* @param cookie - Cookie string to validate
|
371
|
+
* @returns true if cookie appears valid, false otherwise
|
372
|
+
*/
|
373
|
+
isValidSessionCookie(cookie) {
|
374
|
+
if (!cookie || typeof cookie !== "string") {
|
375
|
+
return false;
|
376
|
+
}
|
377
|
+
// Trim whitespace
|
378
|
+
cookie = cookie.trim();
|
379
|
+
// Basic length check
|
380
|
+
if (cookie.length < 10) {
|
381
|
+
return false;
|
382
|
+
}
|
383
|
+
// Basic validation - Appwrite session cookies are typically JWT-like
|
384
|
+
// They should contain dots and be reasonably long
|
385
|
+
if (!cookie.includes(".")) {
|
386
|
+
return false;
|
387
|
+
}
|
388
|
+
// Check for obviously expired or malformed tokens
|
389
|
+
// JWT tokens typically have 3 parts separated by dots
|
390
|
+
const parts = cookie.split(".");
|
391
|
+
if (parts.length < 2) {
|
392
|
+
return false;
|
393
|
+
}
|
394
|
+
// Additional validation - ensure it's not obviously corrupted
|
395
|
+
// Should contain alphanumeric characters and common JWT characters
|
396
|
+
const validChars = /^[A-Za-z0-9._-]+$/;
|
397
|
+
if (!validChars.test(cookie)) {
|
398
|
+
return false;
|
399
|
+
}
|
400
|
+
return true;
|
401
|
+
}
|
402
|
+
/**
|
403
|
+
* Generate MD5 hash of content for cache validation
|
404
|
+
*
|
405
|
+
* @param content - Content to hash
|
406
|
+
* @returns MD5 hash as hex string
|
407
|
+
*/
|
408
|
+
hashContent(content) {
|
409
|
+
return createHash("md5").update(content).digest("hex");
|
410
|
+
}
|
411
|
+
/**
|
412
|
+
* Get all available sessions from prefs
|
413
|
+
*
|
414
|
+
* Returns all sessions stored in the preferences file that pass
|
415
|
+
* basic validation checks.
|
416
|
+
*
|
417
|
+
* @returns Array of valid session info objects
|
418
|
+
*
|
419
|
+
* @example
|
420
|
+
* ```typescript
|
421
|
+
* const sessions = await sessionService.getAvailableSessions();
|
422
|
+
* console.log(`Found ${sessions.length} available sessions`);
|
423
|
+
* sessions.forEach(s => console.log(` - ${s.projectId} (${s.email})`));
|
424
|
+
* ```
|
425
|
+
*/
|
426
|
+
async getAvailableSessions() {
|
427
|
+
const prefs = await this.loadSessionPrefs();
|
428
|
+
if (!prefs) {
|
429
|
+
return [];
|
430
|
+
}
|
431
|
+
const sessions = [];
|
432
|
+
for (const [projectId, sessionData] of Object.entries(prefs)) {
|
433
|
+
if (sessionData.endpoint &&
|
434
|
+
sessionData.cookie &&
|
435
|
+
this.isValidSessionCookie(sessionData.cookie)) {
|
436
|
+
sessions.push({
|
437
|
+
projectId,
|
438
|
+
endpoint: sessionData.endpoint,
|
439
|
+
cookie: sessionData.cookie,
|
440
|
+
email: sessionData.email,
|
441
|
+
expiresAt: sessionData.expiresAt
|
442
|
+
});
|
443
|
+
}
|
444
|
+
}
|
445
|
+
logger.debug(`Found ${sessions.length} available sessions`);
|
446
|
+
return sessions;
|
447
|
+
}
|
448
|
+
/**
|
449
|
+
* Get the path to the prefs.json file
|
450
|
+
*
|
451
|
+
* @returns Absolute path to prefs.json
|
452
|
+
*/
|
453
|
+
getPrefsPath() {
|
454
|
+
return this.PREFS_PATH;
|
455
|
+
}
|
456
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|