kaven-cli 0.1.0-alpha.1 → 0.3.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.
Files changed (50) hide show
  1. package/README.md +284 -45
  2. package/README.pt-BR.md +334 -0
  3. package/dist/commands/auth/login.js +97 -19
  4. package/dist/commands/auth/logout.js +4 -6
  5. package/dist/commands/auth/whoami.js +12 -11
  6. package/dist/commands/cache/index.js +43 -0
  7. package/dist/commands/config/index.js +128 -0
  8. package/dist/commands/init/index.js +209 -0
  9. package/dist/commands/init-ci/index.js +153 -0
  10. package/dist/commands/license/index.js +10 -0
  11. package/dist/commands/license/status.js +44 -0
  12. package/dist/commands/license/tier-table.js +46 -0
  13. package/dist/commands/marketplace/browse.js +219 -0
  14. package/dist/commands/marketplace/install.js +233 -29
  15. package/dist/commands/marketplace/list.js +94 -16
  16. package/dist/commands/module/doctor.js +143 -38
  17. package/dist/commands/module/publish.js +291 -0
  18. package/dist/commands/upgrade/check.js +162 -0
  19. package/dist/commands/upgrade/index.js +218 -0
  20. package/dist/core/AuthService.js +207 -14
  21. package/dist/core/CacheManager.js +151 -0
  22. package/dist/core/ConfigManager.js +165 -0
  23. package/dist/core/EnvManager.js +196 -0
  24. package/dist/core/ErrorRecovery.js +191 -0
  25. package/dist/core/LicenseService.js +118 -0
  26. package/dist/core/ModuleDoctor.js +290 -4
  27. package/dist/core/ModuleInstaller.js +136 -2
  28. package/dist/core/ProjectInitializer.js +154 -0
  29. package/dist/core/RegistryResolver.js +94 -0
  30. package/dist/core/ScriptRunner.js +72 -0
  31. package/dist/core/SignatureVerifier.js +75 -0
  32. package/dist/index.js +265 -20
  33. package/dist/infrastructure/MarketplaceClient.js +388 -64
  34. package/dist/infrastructure/errors.js +61 -0
  35. package/dist/types/auth.js +2 -0
  36. package/dist/types/marketplace.js +2 -0
  37. package/package.json +23 -4
  38. package/dist/commands/modules/add.js +0 -53
  39. package/dist/commands/modules/list.js +0 -40
  40. package/dist/commands/modules/remove.js +0 -54
  41. package/dist/core/api/KavenApiClient.js +0 -61
  42. package/dist/core/auth/AuthManager.js +0 -91
  43. package/dist/core/modules/Injector.js +0 -86
  44. package/dist/core/modules/ModuleInstaller.js +0 -63
  45. package/dist/core/modules/ModuleManager.js +0 -59
  46. package/dist/core/modules/ModuleRemover.js +0 -60
  47. package/dist/lib/config.js +0 -66
  48. package/dist/lib/errors.js +0 -32
  49. package/dist/lib/logger.js +0 -70
  50. package/dist/types/module.js +0 -49
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -7,54 +40,214 @@ exports.AuthService = void 0;
7
40
  const fs_extra_1 = __importDefault(require("fs-extra"));
8
41
  const path_1 = __importDefault(require("path"));
9
42
  const os_1 = __importDefault(require("os"));
43
+ /** Decode a JWT payload without verifying the signature (server's responsibility). */
44
+ function decodeJwtPayload(token) {
45
+ try {
46
+ const parts = token.split(".");
47
+ if (parts.length !== 3)
48
+ return null;
49
+ // Base64url → Base64 → JSON
50
+ const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
51
+ const padded = base64 + "=".repeat((4 - (base64.length % 4)) % 4);
52
+ const json = Buffer.from(padded, "base64").toString("utf8");
53
+ return JSON.parse(json);
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ }
59
+ /** Format a future timestamp into a human-readable "expires in X" string. */
60
+ function formatExpiryRelative(expiresAt) {
61
+ const now = Date.now();
62
+ const expMs = new Date(expiresAt).getTime();
63
+ const diffSeconds = Math.max(0, Math.floor((expMs - now) / 1000));
64
+ if (diffSeconds === 0)
65
+ return "expired";
66
+ const hours = Math.floor(diffSeconds / 3600);
67
+ const minutes = Math.floor((diffSeconds % 3600) / 60);
68
+ if (hours > 0) {
69
+ return `expires in ${hours}h ${minutes}m`;
70
+ }
71
+ return `expires in ${minutes}m`;
72
+ }
10
73
  class AuthService {
11
74
  constructor() {
12
75
  this.configPath = path_1.default.join(os_1.default.homedir(), ".kaven", "auth.json");
13
76
  }
77
+ /**
78
+ * Store complete auth tokens (new format for C1.1+)
79
+ */
80
+ async saveTokens(tokens) {
81
+ const configDir = path_1.default.dirname(this.configPath);
82
+ await fs_extra_1.default.ensureDir(configDir);
83
+ await fs_extra_1.default.writeJson(this.configPath, tokens, { spaces: 2 });
84
+ // Set restrictive permissions (0600 - owner read/write only) on Unix-like systems
85
+ if (process.platform !== "win32") {
86
+ await fs_extra_1.default.chmod(this.configPath, 0o600);
87
+ }
88
+ }
89
+ /**
90
+ * Legacy method - kept for backwards compatibility
91
+ * @deprecated Use saveTokens() instead
92
+ */
14
93
  async storeToken(token) {
15
94
  const configDir = path_1.default.dirname(this.configPath);
16
95
  await fs_extra_1.default.ensureDir(configDir);
17
- // Salvar token em JSON
18
96
  await fs_extra_1.default.writeJson(this.configPath, { token }, { spaces: 2 });
19
- // Definir permissões restritas (0600 - leitura/escrita apenas pelo dono) no Linux
20
97
  if (process.platform !== "win32") {
21
98
  await fs_extra_1.default.chmod(this.configPath, 0o600);
22
99
  }
23
100
  }
101
+ /**
102
+ * Get stored authentication data (new format)
103
+ */
104
+ async getAuth() {
105
+ if (!(await fs_extra_1.default.pathExists(this.configPath))) {
106
+ return null;
107
+ }
108
+ try {
109
+ const data = await fs_extra_1.default.readJson(this.configPath);
110
+ if (data.access_token) {
111
+ return data;
112
+ }
113
+ return null;
114
+ }
115
+ catch {
116
+ return null;
117
+ }
118
+ }
119
+ /**
120
+ * Legacy method - kept for backwards compatibility
121
+ * @deprecated Use getAuth() instead
122
+ */
24
123
  async getToken() {
25
124
  if (!(await fs_extra_1.default.pathExists(this.configPath))) {
26
125
  return null;
27
126
  }
28
127
  try {
29
128
  const data = await fs_extra_1.default.readJson(this.configPath);
129
+ if (data.access_token) {
130
+ return data.access_token;
131
+ }
30
132
  return data.token || null;
31
133
  }
32
134
  catch {
33
135
  return null;
34
136
  }
35
137
  }
36
- async clearToken() {
138
+ /**
139
+ * Return a valid access token, auto-refreshing if it expires in <5 minutes.
140
+ * Throws if the session is expired and refresh fails.
141
+ */
142
+ async getValidToken() {
143
+ const auth = await this.getAuth();
144
+ if (!auth) {
145
+ throw new Error("Not authenticated. Run 'kaven auth login' to authenticate.");
146
+ }
147
+ const expiresAt = new Date(auth.expires_at).getTime();
148
+ const now = Date.now();
149
+ const fiveMinutesMs = 5 * 60 * 1000;
150
+ const isExpiringSoon = expiresAt - now < fiveMinutesMs;
151
+ const isExpired = now >= expiresAt;
152
+ if (!isExpiringSoon) {
153
+ // Token is still valid and not expiring soon — return as-is
154
+ return auth.access_token;
155
+ }
156
+ // Attempt refresh
157
+ try {
158
+ // Lazy import to avoid circular dependency at module level
159
+ const { MarketplaceClient } = await Promise.resolve().then(() => __importStar(require("../infrastructure/MarketplaceClient")));
160
+ const client = new MarketplaceClient(this);
161
+ const refreshed = await client.refreshToken(auth.refresh_token);
162
+ // Update stored auth with new tokens
163
+ const newAuth = {
164
+ access_token: refreshed.access_token,
165
+ refresh_token: refreshed.refresh_token,
166
+ expires_at: refreshed.expires_at,
167
+ user: auth.user,
168
+ };
169
+ await this.saveTokens(newAuth);
170
+ return refreshed.access_token;
171
+ }
172
+ catch {
173
+ // Refresh failed — use existing token if still valid, else throw
174
+ if (!isExpired) {
175
+ console.warn("[kaven] Warning: Failed to refresh token. Using existing token.");
176
+ return auth.access_token;
177
+ }
178
+ throw new Error("Session expired. Run 'kaven auth login' to re-authenticate.");
179
+ }
180
+ }
181
+ /**
182
+ * Check if the user is authenticated. Never throws.
183
+ */
184
+ async isAuthenticated() {
185
+ try {
186
+ const token = await this.getToken();
187
+ return !!token;
188
+ }
189
+ catch {
190
+ return false;
191
+ }
192
+ }
193
+ /**
194
+ * Remove auth.json (logout)
195
+ */
196
+ async logout() {
37
197
  if (await fs_extra_1.default.pathExists(this.configPath)) {
38
198
  await fs_extra_1.default.remove(this.configPath);
39
199
  }
40
200
  }
41
- async isAuthenticated() {
42
- const token = await this.getToken();
43
- return !!token;
201
+ /**
202
+ * Legacy alias for logout()
203
+ * @deprecated Use logout() instead
204
+ */
205
+ async clearToken() {
206
+ return this.logout();
44
207
  }
45
208
  /**
46
- * Mock decoding of JWT for now.
47
- * In a real implementation, we would use a library like 'jsonwebtoken'.
209
+ * Decode JWT and return user info from the access token payload.
48
210
  */
49
211
  async getUserInfo() {
50
- const token = await this.getToken();
51
- if (!token)
212
+ const auth = await this.getAuth();
213
+ if (!auth)
214
+ return null;
215
+ const payload = decodeJwtPayload(auth.access_token);
216
+ if (payload) {
217
+ return {
218
+ id: payload.sub,
219
+ email: payload.email,
220
+ name: payload.githubId,
221
+ };
222
+ }
223
+ // Fallback to stored user data if JWT decode fails
224
+ return {
225
+ id: auth.user.githubId,
226
+ email: auth.user.email,
227
+ name: undefined,
228
+ };
229
+ }
230
+ /**
231
+ * Get decoded JWT payload from stored access token.
232
+ */
233
+ async getDecodedToken() {
234
+ const auth = await this.getAuth();
235
+ if (!auth)
236
+ return null;
237
+ return decodeJwtPayload(auth.access_token);
238
+ }
239
+ /**
240
+ * Get the stored auth data along with human-readable expiry string.
241
+ */
242
+ async getWhoamiInfo() {
243
+ const auth = await this.getAuth();
244
+ if (!auth)
52
245
  return null;
53
- // Simulate decoding (mocked)
54
246
  return {
55
- id: "usr-mock-123",
56
- email: "user@example.com",
57
- name: "Kaven Explorador",
247
+ email: auth.user.email,
248
+ githubId: auth.user.githubId,
249
+ tier: auth.user.tier.charAt(0).toUpperCase() + auth.user.tier.slice(1),
250
+ sessionExpiry: formatExpiryRelative(auth.expires_at),
58
251
  };
59
252
  }
60
253
  }
@@ -0,0 +1,151 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.CacheManager = void 0;
7
+ exports.getCacheManager = getCacheManager;
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const os_1 = __importDefault(require("os"));
11
+ class CacheManager {
12
+ constructor(cacheDir, maxSizeBytes) {
13
+ this.cacheDir = cacheDir ?? path_1.default.join(os_1.default.homedir(), ".kaven", "cache");
14
+ this.maxSizeBytes = maxSizeBytes ?? 50 * 1024 * 1024; // 50MB
15
+ this.indexPath = path_1.default.join(this.cacheDir, "_index.json");
16
+ }
17
+ async ensureDir() {
18
+ await fs_extra_1.default.ensureDir(this.cacheDir);
19
+ }
20
+ keyToFileName(key) {
21
+ // Sanitize key to valid filename
22
+ return key.replace(/[^a-zA-Z0-9-_:.]/g, "_") + ".json";
23
+ }
24
+ async readIndex() {
25
+ try {
26
+ if (await fs_extra_1.default.pathExists(this.indexPath)) {
27
+ return (await fs_extra_1.default.readJson(this.indexPath));
28
+ }
29
+ }
30
+ catch {
31
+ // ignore
32
+ }
33
+ return {};
34
+ }
35
+ async writeIndex(index) {
36
+ await this.ensureDir();
37
+ await fs_extra_1.default.writeJson(this.indexPath, index, { spaces: 2 });
38
+ }
39
+ /** Get fresh data or null if expired. */
40
+ async get(key) {
41
+ const index = await this.readIndex();
42
+ const entry = index[key];
43
+ if (!entry)
44
+ return null;
45
+ if (Date.now() > entry.expiresAt)
46
+ return null;
47
+ try {
48
+ const filePath = path_1.default.join(this.cacheDir, entry.file);
49
+ const data = await fs_extra_1.default.readJson(filePath);
50
+ return data.data;
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /** Get stale data (ignore TTL expiry) — for offline fallback. */
57
+ async getStale(key) {
58
+ const index = await this.readIndex();
59
+ const entry = index[key];
60
+ if (!entry)
61
+ return null;
62
+ try {
63
+ const filePath = path_1.default.join(this.cacheDir, entry.file);
64
+ const data = await fs_extra_1.default.readJson(filePath);
65
+ return data.data;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ /** Set data with TTL. */
72
+ async set(key, data, ttlMs) {
73
+ await this.ensureDir();
74
+ const serialized = JSON.stringify(data);
75
+ const size = Buffer.byteLength(serialized, "utf8");
76
+ const now = Date.now();
77
+ const expiresAt = now + ttlMs;
78
+ const fileName = this.keyToFileName(key);
79
+ const filePath = path_1.default.join(this.cacheDir, fileName);
80
+ const entry = {
81
+ data,
82
+ expiresAt,
83
+ size,
84
+ createdAt: now,
85
+ };
86
+ await fs_extra_1.default.writeJson(filePath, entry, { spaces: 2 });
87
+ const index = await this.readIndex();
88
+ index[key] = {
89
+ file: fileName,
90
+ expiresAt,
91
+ size,
92
+ createdAt: now,
93
+ };
94
+ await this.writeIndex(index);
95
+ // Evict if over limit
96
+ await this.evict();
97
+ }
98
+ /** Evict oldest entries if total size exceeds the limit. */
99
+ async evict() {
100
+ const index = await this.readIndex();
101
+ const entries = Object.entries(index);
102
+ let totalSize = entries.reduce((sum, [, e]) => sum + e.size, 0);
103
+ if (totalSize <= this.maxSizeBytes)
104
+ return;
105
+ // Sort by oldest first
106
+ entries.sort(([, a], [, b]) => a.createdAt - b.createdAt);
107
+ const updatedIndex = { ...index };
108
+ for (const [key, entry] of entries) {
109
+ if (totalSize <= this.maxSizeBytes)
110
+ break;
111
+ try {
112
+ const filePath = path_1.default.join(this.cacheDir, entry.file);
113
+ await fs_extra_1.default.remove(filePath);
114
+ delete updatedIndex[key];
115
+ totalSize -= entry.size;
116
+ }
117
+ catch {
118
+ // ignore individual removal errors
119
+ }
120
+ }
121
+ await this.writeIndex(updatedIndex);
122
+ }
123
+ /** Get cache statistics. */
124
+ async stats() {
125
+ const index = await this.readIndex();
126
+ const entries = Object.values(index);
127
+ if (entries.length === 0) {
128
+ return { totalSize: 0, entries: 0 };
129
+ }
130
+ const totalSize = entries.reduce((sum, e) => sum + e.size, 0);
131
+ const timestamps = entries.map((e) => e.createdAt);
132
+ const oldest = new Date(Math.min(...timestamps));
133
+ const newest = new Date(Math.max(...timestamps));
134
+ return { totalSize, entries: entries.length, oldest, newest };
135
+ }
136
+ /** Clear all cache entries. */
137
+ async clear() {
138
+ if (await fs_extra_1.default.pathExists(this.cacheDir)) {
139
+ await fs_extra_1.default.remove(this.cacheDir);
140
+ }
141
+ }
142
+ }
143
+ exports.CacheManager = CacheManager;
144
+ // Singleton instance
145
+ let _cacheManager = null;
146
+ function getCacheManager() {
147
+ if (!_cacheManager) {
148
+ _cacheManager = new CacheManager();
149
+ }
150
+ return _cacheManager;
151
+ }
@@ -0,0 +1,165 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configManager = exports.ConfigManager = exports.configSchema = void 0;
7
+ const path_1 = __importDefault(require("path"));
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const zod_1 = require("zod");
11
+ const CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".kaven");
12
+ const CONFIG_PATH = path_1.default.join(CONFIG_DIR, "config.json");
13
+ exports.configSchema = zod_1.z.object({
14
+ registry: zod_1.z.string().url().default("https://marketplace.kaven.sh"),
15
+ telemetry: zod_1.z.boolean().default(true),
16
+ theme: zod_1.z.enum(["light", "dark"]).default("dark"),
17
+ locale: zod_1.z.string().default("en-US"),
18
+ // Custom registry support
19
+ customRegistry: zod_1.z.string().url().optional(),
20
+ // For storing user preferences
21
+ lastLogin: zod_1.z.string().datetime().optional(),
22
+ projectDefaults: zod_1.z
23
+ .object({
24
+ dbUrl: zod_1.z.string().optional(),
25
+ emailProvider: zod_1.z.enum(["postmark", "resend", "ses", "smtp"]).optional(),
26
+ locale: zod_1.z.string().optional(),
27
+ currency: zod_1.z.string().optional(),
28
+ })
29
+ .optional(),
30
+ });
31
+ class ConfigManager {
32
+ constructor() {
33
+ this.config = {
34
+ registry: "https://marketplace.kaven.sh",
35
+ telemetry: true,
36
+ theme: "dark",
37
+ locale: "en-US",
38
+ };
39
+ }
40
+ async initialize() {
41
+ await fs_extra_1.default.ensureDir(CONFIG_DIR);
42
+ if (await fs_extra_1.default.pathExists(CONFIG_PATH)) {
43
+ try {
44
+ const raw = await fs_extra_1.default.readJson(CONFIG_PATH);
45
+ const parsed = exports.configSchema.safeParse(raw);
46
+ if (parsed.success) {
47
+ this.config = parsed.data;
48
+ }
49
+ else {
50
+ // If validation fails, use defaults
51
+ this.config = {
52
+ registry: "https://marketplace.kaven.sh",
53
+ telemetry: true,
54
+ theme: "dark",
55
+ locale: "en-US",
56
+ };
57
+ }
58
+ }
59
+ catch {
60
+ // If file is corrupted, start fresh
61
+ this.config = {
62
+ registry: "https://marketplace.kaven.sh",
63
+ telemetry: true,
64
+ theme: "dark",
65
+ locale: "en-US",
66
+ };
67
+ }
68
+ }
69
+ else {
70
+ // Initialize with defaults
71
+ this.config = {
72
+ registry: "https://marketplace.kaven.sh",
73
+ telemetry: true,
74
+ theme: "dark",
75
+ locale: "en-US",
76
+ };
77
+ await this.persist();
78
+ }
79
+ }
80
+ /**
81
+ * Get config value with env var override support
82
+ * Priority: ENV VAR > config file > CLI arg > default
83
+ */
84
+ get(key, envVarName) {
85
+ // Check environment variable override
86
+ if (envVarName) {
87
+ const envValue = process.env[envVarName];
88
+ if (envValue !== undefined) {
89
+ return envValue;
90
+ }
91
+ }
92
+ // Check config file
93
+ const value = this.config[key];
94
+ if (value !== undefined) {
95
+ return value;
96
+ }
97
+ // Return default from schema
98
+ const defaults = exports.configSchema.parse({});
99
+ return defaults[key];
100
+ }
101
+ /**
102
+ * Set config value and persist to disk
103
+ */
104
+ async set(key, value) {
105
+ const updateObj = { [key]: value };
106
+ const updated = exports.configSchema.safeParse({ ...this.config, ...updateObj });
107
+ if (!updated.success) {
108
+ const errors = updated.error.issues
109
+ .map((issue) => `${issue.path.join(".")}: ${issue.message}`)
110
+ .join(", ");
111
+ throw new Error(`Invalid config: ${errors}`);
112
+ }
113
+ this.config = updated.data;
114
+ await this.persist();
115
+ }
116
+ /**
117
+ * Get all config
118
+ */
119
+ getAll() {
120
+ return { ...this.config };
121
+ }
122
+ /**
123
+ * Reset config to defaults
124
+ */
125
+ async reset() {
126
+ this.config = {
127
+ registry: "https://marketplace.kaven.sh",
128
+ telemetry: true,
129
+ theme: "dark",
130
+ locale: "en-US",
131
+ };
132
+ await this.persist();
133
+ }
134
+ /**
135
+ * Persist config to disk
136
+ */
137
+ async persist() {
138
+ await fs_extra_1.default.ensureDir(CONFIG_DIR);
139
+ await fs_extra_1.default.writeJson(CONFIG_PATH, this.config, { spaces: 2 });
140
+ }
141
+ /**
142
+ * Get registry URL (custom or default)
143
+ */
144
+ getRegistry() {
145
+ return (this.config.customRegistry || this.config.registry || "https://marketplace.kaven.sh");
146
+ }
147
+ /**
148
+ * Check if telemetry is enabled (can be overridden by env var)
149
+ */
150
+ isTelemetryEnabled() {
151
+ if (process.env.KAVEN_TELEMETRY === "0") {
152
+ return false;
153
+ }
154
+ return this.config.telemetry !== false;
155
+ }
156
+ /**
157
+ * Get config directory path
158
+ */
159
+ getConfigDir() {
160
+ return CONFIG_DIR;
161
+ }
162
+ }
163
+ exports.ConfigManager = ConfigManager;
164
+ // Export singleton instance
165
+ exports.configManager = new ConfigManager();