kaven-cli 0.4.1-alpha.0 → 0.4.2-alpha.2

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 (68) hide show
  1. package/README.md +181 -207
  2. package/dist/EnvManager-NMS3NMIE.js +15 -0
  3. package/dist/MarketplaceClient-YCFH2VU4.js +1 -0
  4. package/dist/chunk-JHLQ46NG.js +1 -0
  5. package/dist/index.d.ts +4 -0
  6. package/dist/index.js +248 -304
  7. package/dist/tier-table-DQMPQSI2.js +6 -0
  8. package/package.json +26 -11
  9. package/dist/EnvManager-GQMEZ6NV.js +0 -158
  10. package/dist/MarketplaceClient-IJGRQRC4.js +0 -7
  11. package/dist/chunk-3RG5ZIWI.js +0 -10
  12. package/dist/chunk-GHZX5OAA.js +0 -455
  13. package/dist/commands/aiox/index.js +0 -20
  14. package/dist/commands/auth/login.js +0 -122
  15. package/dist/commands/auth/logout.js +0 -23
  16. package/dist/commands/auth/whoami.js +0 -36
  17. package/dist/commands/cache/index.js +0 -43
  18. package/dist/commands/config/features.js +0 -161
  19. package/dist/commands/config/index.js +0 -95
  20. package/dist/commands/index.js +0 -2
  21. package/dist/commands/init/aiox-bootstrap.js +0 -83
  22. package/dist/commands/init/index.js +0 -210
  23. package/dist/commands/init-ci/index.js +0 -153
  24. package/dist/commands/license/index.js +0 -10
  25. package/dist/commands/license/status.js +0 -44
  26. package/dist/commands/license/tier-table.js +0 -46
  27. package/dist/commands/marketplace/browse.js +0 -186
  28. package/dist/commands/marketplace/install.js +0 -263
  29. package/dist/commands/marketplace/list.js +0 -122
  30. package/dist/commands/module/activate.js +0 -245
  31. package/dist/commands/module/add.js +0 -69
  32. package/dist/commands/module/doctor.js +0 -175
  33. package/dist/commands/module/list.js +0 -51
  34. package/dist/commands/module/publish.js +0 -258
  35. package/dist/commands/module/remove.js +0 -58
  36. package/dist/commands/telemetry/view.js +0 -27
  37. package/dist/commands/upgrade/check.js +0 -162
  38. package/dist/commands/upgrade/index.js +0 -185
  39. package/dist/core/AuthService.js +0 -222
  40. package/dist/core/CacheManager.js +0 -154
  41. package/dist/core/ConfigManager.js +0 -166
  42. package/dist/core/EnvManager.js +0 -196
  43. package/dist/core/ErrorRecovery.js +0 -192
  44. package/dist/core/LicenseService.js +0 -83
  45. package/dist/core/ManifestParser.js +0 -52
  46. package/dist/core/MarkerService.js +0 -62
  47. package/dist/core/ModuleDoctor.js +0 -451
  48. package/dist/core/ModuleInstaller.js +0 -169
  49. package/dist/core/ProjectInitializer.js +0 -183
  50. package/dist/core/RegistryResolver.js +0 -95
  51. package/dist/core/SchemaActivator.js +0 -278
  52. package/dist/core/ScriptRunner.js +0 -73
  53. package/dist/core/SignatureVerifier.js +0 -75
  54. package/dist/core/index.js +0 -2
  55. package/dist/infrastructure/Container.js +0 -37
  56. package/dist/infrastructure/MarketplaceClient.js +0 -425
  57. package/dist/infrastructure/TelemetryBuffer.js +0 -73
  58. package/dist/infrastructure/TransactionalFileSystem.js +0 -77
  59. package/dist/infrastructure/errors.js +0 -63
  60. package/dist/infrastructure/index.js +0 -2
  61. package/dist/lib/capabilities-catalog.js +0 -73
  62. package/dist/lib/module-registry.js +0 -47
  63. package/dist/lib/schema-modifier.js +0 -40
  64. package/dist/tier-table-LAL6PAVW.js +0 -52
  65. package/dist/types/auth.js +0 -2
  66. package/dist/types/manifest.js +0 -45
  67. package/dist/types/markers.js +0 -10
  68. package/dist/types/marketplace.js +0 -2
@@ -1,75 +0,0 @@
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.computeFileChecksum = computeFileChecksum;
7
- exports.verifyEd25519Signature = verifyEd25519Signature;
8
- exports.verifyDownload = verifyDownload;
9
- const crypto_1 = __importDefault(require("crypto"));
10
- const fs_extra_1 = __importDefault(require("fs-extra"));
11
- const errors_1 = require("../infrastructure/errors");
12
- /**
13
- * Compute SHA-256 hex checksum of a file.
14
- */
15
- async function computeFileChecksum(filePath) {
16
- const data = await fs_extra_1.default.readFile(filePath);
17
- return crypto_1.default.createHash("sha256").update(data).digest("hex");
18
- }
19
- const HEX_PATTERN = /^[0-9a-fA-F]+$/;
20
- /**
21
- * Decode a signature string that may be hex or base64 encoded.
22
- * Ed25519 signatures are always 64 bytes:
23
- * - hex: 128 chars, only [0-9a-fA-F]
24
- * - base64: 88 chars, may contain +/=
25
- */
26
- function decodeSignature(encoded) {
27
- if (HEX_PATTERN.test(encoded) && encoded.length === 128) {
28
- return Buffer.from(encoded, "hex");
29
- }
30
- return Buffer.from(encoded, "base64");
31
- }
32
- /**
33
- * Verify Ed25519 signature against a SHA-256 checksum.
34
- *
35
- * Accepts signature in either hex or base64 encoding.
36
- * Tolerates signatures made over checksum with trailing newline
37
- * (common when signing via `echo checksum > file && openssl sign`).
38
- */
39
- function verifyEd25519Signature(checksum, signature, publicKeyBase64) {
40
- try {
41
- const publicKey = crypto_1.default.createPublicKey({
42
- key: Buffer.from(publicKeyBase64, "base64"),
43
- type: "spki",
44
- format: "der",
45
- });
46
- const sigBuffer = decodeSignature(signature);
47
- if (crypto_1.default.verify(null, Buffer.from(checksum), publicKey, sigBuffer)) {
48
- return true;
49
- }
50
- // Tolerate trailing newline from shell-based signing
51
- return crypto_1.default.verify(null, Buffer.from(checksum + "\n"), publicKey, sigBuffer);
52
- }
53
- catch {
54
- return false;
55
- }
56
- }
57
- /**
58
- * Verify a downloaded module tarball:
59
- * 1. Compute SHA-256 checksum and compare to expected
60
- * 2. Verify Ed25519 signature of checksum with publisher key
61
- *
62
- * Throws SignatureVerificationError on failure.
63
- */
64
- async function verifyDownload(opts) {
65
- const actualChecksum = await computeFileChecksum(opts.filePath);
66
- if (actualChecksum !== opts.expectedChecksum) {
67
- throw new errors_1.SignatureVerificationError(`Checksum mismatch: expected ${opts.expectedChecksum.substring(0, 16)}..., ` +
68
- `got ${actualChecksum.substring(0, 16)}...`);
69
- }
70
- const valid = verifyEd25519Signature(opts.expectedChecksum, opts.signature, opts.publicKeyBase64);
71
- if (!valid) {
72
- throw new errors_1.SignatureVerificationError("Ed25519 signature verification failed. " +
73
- "The package may have been tampered with.");
74
- }
75
- }
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,37 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.container = exports.Container = void 0;
4
- class Container {
5
- services = new Map();
6
- factories = new Map();
7
- register(name, factory) {
8
- this.factories.set(name, factory);
9
- }
10
- registerSingleton(name, instance) {
11
- this.services.set(name, instance);
12
- }
13
- resolve(name) {
14
- // Check singletons first
15
- if (this.services.has(name)) {
16
- return this.services.get(name);
17
- }
18
- // Check factories
19
- if (this.factories.has(name)) {
20
- const factory = this.factories.get(name);
21
- if (factory) {
22
- const instance = factory();
23
- // Cache as singleton
24
- this.services.set(name, instance);
25
- return instance;
26
- }
27
- }
28
- throw new Error(`Service not found: ${name}`);
29
- }
30
- clear() {
31
- this.services.clear();
32
- this.factories.clear();
33
- }
34
- }
35
- exports.Container = Container;
36
- // Global container instance
37
- exports.container = new Container();
@@ -1,425 +0,0 @@
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.MarketplaceClient = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const path_1 = __importDefault(require("path"));
9
- const os_1 = __importDefault(require("os"));
10
- const errors_1 = require("./errors");
11
- const DEFAULT_BASE_URL = "https://marketplace.kaven.site";
12
- const REQUEST_TIMEOUT_MS = 30_000;
13
- const MAX_RETRIES = 3;
14
- const INITIAL_RETRY_DELAY_MS = 1_000;
15
- function debug(message) {
16
- if (process.env.KAVEN_DEBUG === "1") {
17
- console.debug(`[kaven:debug] ${message}`);
18
- }
19
- }
20
- /** Load the apiUrl from ~/.kaven/config.json if present. */
21
- async function loadConfigApiUrl() {
22
- try {
23
- const configPath = path_1.default.join(os_1.default.homedir(), ".kaven", "config.json");
24
- if (await fs_extra_1.default.pathExists(configPath)) {
25
- const config = await fs_extra_1.default.readJson(configPath);
26
- if (typeof config.apiUrl === "string" && config.apiUrl) {
27
- return config.apiUrl;
28
- }
29
- }
30
- }
31
- catch {
32
- // Ignore config read errors
33
- }
34
- return null;
35
- }
36
- /**
37
- * Load a service token for agent-to-service auth.
38
- * Resolution order: KAVEN_SERVICE_TOKEN env → ~/.kaven/config.json#serviceToken
39
- */
40
- async function loadServiceToken() {
41
- if (process.env.KAVEN_SERVICE_TOKEN) {
42
- return process.env.KAVEN_SERVICE_TOKEN;
43
- }
44
- try {
45
- const configPath = path_1.default.join(os_1.default.homedir(), ".kaven", "config.json");
46
- if (await fs_extra_1.default.pathExists(configPath)) {
47
- const config = await fs_extra_1.default.readJson(configPath);
48
- if (typeof config.serviceToken === "string" && config.serviceToken) {
49
- return config.serviceToken;
50
- }
51
- }
52
- }
53
- catch {
54
- // Ignore config read errors
55
- }
56
- return null;
57
- }
58
- /** Resolve base URL from env → config file → default. */
59
- async function resolveBaseUrl() {
60
- if (process.env.KAVEN_API_URL) {
61
- return process.env.KAVEN_API_URL.replace(/\/$/, "");
62
- }
63
- const configUrl = await loadConfigApiUrl();
64
- if (configUrl) {
65
- return configUrl.replace(/\/$/, "");
66
- }
67
- return DEFAULT_BASE_URL;
68
- }
69
- /** Sleep helper for retry backoff. */
70
- function sleep(ms) {
71
- return new Promise((resolve) => setTimeout(resolve, ms));
72
- }
73
- /** Determine if the error code / HTTP status is retryable. */
74
- function isRetryable(status) {
75
- return status >= 500;
76
- }
77
- class MarketplaceClient {
78
- baseURLPromise;
79
- authService;
80
- constructor(authService) {
81
- this.authService = authService ?? null;
82
- this.baseURLPromise = resolveBaseUrl();
83
- }
84
- /** Resolve a relative API path to an absolute URL. */
85
- async resolveUrl(path) {
86
- const baseURL = await this.baseURLPromise;
87
- return `${baseURL}${path}`;
88
- }
89
- // ──────────────────────────────────────────────────────────
90
- // Core HTTP helpers
91
- // ──────────────────────────────────────────────────────────
92
- /**
93
- * Make an HTTP request with retry logic and typed error mapping.
94
- * `authenticated` controls whether Authorization header is attached.
95
- */
96
- async request(method, endpoint, options = {}) {
97
- const baseURL = await this.baseURLPromise;
98
- const url = `${baseURL}${endpoint}`;
99
- const { body, authenticated = false } = options;
100
- let lastError = null;
101
- for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
102
- if (attempt > 0) {
103
- const delay = INITIAL_RETRY_DELAY_MS * Math.pow(2, attempt - 1);
104
- debug(`Retry attempt ${attempt} for ${method} ${endpoint} (delay ${delay}ms)`);
105
- await sleep(delay);
106
- }
107
- try {
108
- const headers = {
109
- "Content-Type": "application/json",
110
- Accept: "application/json",
111
- };
112
- if (authenticated && this.authService) {
113
- const token = await this.authService.getValidToken();
114
- headers["Authorization"] = `Bearer ${token}`;
115
- }
116
- const serviceToken = await loadServiceToken();
117
- if (serviceToken) {
118
- headers["X-Service-Token"] = serviceToken;
119
- }
120
- debug(`${method} ${url}`);
121
- const controller = new AbortController();
122
- const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
123
- let response;
124
- try {
125
- response = await fetch(url, {
126
- method,
127
- headers,
128
- body: body !== undefined ? JSON.stringify(body) : undefined,
129
- signal: controller.signal,
130
- });
131
- }
132
- finally {
133
- clearTimeout(timeoutId);
134
- }
135
- debug(`Response: ${response.status} ${response.statusText}`);
136
- // Map HTTP errors to typed exceptions
137
- if (!response.ok) {
138
- const errorText = await response.text().catch(() => "");
139
- let errorMessage = errorText;
140
- try {
141
- const errorJson = JSON.parse(errorText);
142
- errorMessage = errorJson.message || errorJson.error || errorText;
143
- }
144
- catch {
145
- // keep raw text
146
- }
147
- switch (response.status) {
148
- case 401:
149
- throw new errors_1.AuthenticationError(errorMessage || "Authentication required");
150
- case 403: {
151
- let requiredTier = "pro";
152
- try {
153
- const parsed = JSON.parse(errorText);
154
- requiredTier = parsed.requiredTier || requiredTier;
155
- }
156
- catch {
157
- // ignore
158
- }
159
- throw new errors_1.LicenseRequiredError(requiredTier, errorMessage || "License required");
160
- }
161
- case 404:
162
- throw new errors_1.NotFoundError(errorMessage || "Resource not found");
163
- case 429: {
164
- const retryAfter = parseInt(response.headers.get("retry-after") ?? "60", 10);
165
- throw new errors_1.RateLimitError(isNaN(retryAfter) ? 60 : retryAfter);
166
- }
167
- default:
168
- if (isRetryable(response.status)) {
169
- lastError = new errors_1.ServerError(errorMessage || `Server error: ${response.status}`);
170
- continue; // retry
171
- }
172
- throw new errors_1.ServerError(errorMessage || `Server error: ${response.status}`);
173
- }
174
- }
175
- // Parse successful response
176
- const contentType = response.headers.get("content-type") ?? "";
177
- if (contentType.includes("application/json")) {
178
- return (await response.json());
179
- }
180
- // Return empty object for no-body responses (204, etc.)
181
- return {};
182
- }
183
- catch (error) {
184
- // Re-throw our typed errors without retrying on 4xx
185
- if (error instanceof errors_1.AuthenticationError ||
186
- error instanceof errors_1.LicenseRequiredError ||
187
- error instanceof errors_1.NotFoundError ||
188
- error instanceof errors_1.RateLimitError) {
189
- throw error;
190
- }
191
- // Network / timeout errors
192
- if (error instanceof TypeError ||
193
- (error instanceof Error && error.name === "AbortError")) {
194
- const networkError = new errors_1.NetworkError(error.name === "AbortError"
195
- ? "Request timed out after 30s"
196
- : `Network error: ${error.message}`);
197
- lastError = networkError;
198
- // Retry on network errors too
199
- if (attempt < MAX_RETRIES)
200
- continue;
201
- throw networkError;
202
- }
203
- // ServerError already stored in lastError, will retry
204
- if (error instanceof errors_1.ServerError) {
205
- lastError = error;
206
- if (attempt < MAX_RETRIES)
207
- continue;
208
- throw error;
209
- }
210
- // Unknown error — don't retry
211
- throw error;
212
- }
213
- }
214
- // All retries exhausted
215
- throw lastError ?? new errors_1.NetworkError("Request failed after retries");
216
- }
217
- // ──────────────────────────────────────────────────────────
218
- // Auth endpoints (unauthenticated)
219
- // ──────────────────────────────────────────────────────────
220
- /**
221
- * Step 1 of Device Code Flow: Request device code from marketplace.
222
- */
223
- async requestDeviceCode() {
224
- return this.request("POST", "/auth/device-code", {
225
- body: { client_id: "kaven-cli" },
226
- authenticated: false,
227
- });
228
- }
229
- /**
230
- * Step 2 of Device Code Flow: Poll for access token.
231
- */
232
- async pollDeviceToken(deviceCode) {
233
- try {
234
- const baseURL = await this.baseURLPromise;
235
- const url = `${baseURL}/auth/token`;
236
- const controller = new AbortController();
237
- const timeoutId = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
238
- let response;
239
- try {
240
- response = await fetch(url, {
241
- method: "POST",
242
- headers: { "Content-Type": "application/json" },
243
- body: JSON.stringify({
244
- device_code: deviceCode,
245
- grant_type: "urn:ietf:params:oauth:grant-type:device_code",
246
- }),
247
- signal: controller.signal,
248
- });
249
- }
250
- finally {
251
- clearTimeout(timeoutId);
252
- }
253
- if (response.ok) {
254
- const tokens = await response.json();
255
- return { status: "success", tokens };
256
- }
257
- const errorData = await response.json().catch(() => ({}));
258
- const errorCode = errorData.error ?? "unknown_error";
259
- switch (errorCode) {
260
- case "authorization_pending":
261
- return { status: "authorization_pending" };
262
- case "slow_down":
263
- return { status: "slow_down" };
264
- case "access_denied":
265
- return { status: "access_denied" };
266
- case "expired_token":
267
- return { status: "expired_token" };
268
- default:
269
- throw new Error(`Unexpected error: ${errorCode}`);
270
- }
271
- }
272
- catch (error) {
273
- const nodeError = error;
274
- if (nodeError.code === "ECONNREFUSED" ||
275
- nodeError.code === "ENOTFOUND") {
276
- throw new errors_1.NetworkError("Network error. Check your connection and try again.");
277
- }
278
- throw error;
279
- }
280
- }
281
- /**
282
- * Refresh access token using refresh token.
283
- */
284
- async refreshToken(refreshToken) {
285
- return this.request("POST", "/auth/refresh", {
286
- body: { refresh_token: refreshToken },
287
- authenticated: false,
288
- });
289
- }
290
- // ──────────────────────────────────────────────────────────
291
- // Module endpoints (authenticated)
292
- // ──────────────────────────────────────────────────────────
293
- /**
294
- * List available modules with optional filters.
295
- */
296
- async listModules(filters) {
297
- const params = new URLSearchParams();
298
- if (filters?.category)
299
- params.set("category", filters.category);
300
- if (filters?.tier)
301
- params.set("tier", filters.tier);
302
- if (filters?.q)
303
- params.set("q", filters.q);
304
- if (filters?.page)
305
- params.set("page", String(filters.page));
306
- if (filters?.pageSize)
307
- params.set("pageSize", String(filters.pageSize));
308
- const query = params.toString();
309
- const endpoint = `/modules${query ? `?${query}` : ""}`;
310
- return this.request("GET", endpoint);
311
- }
312
- /**
313
- * Get a single module by slug.
314
- */
315
- async getModule(slug) {
316
- return this.request("GET", `/modules/${slug}`);
317
- }
318
- /**
319
- * Get a module's manifest for a specific version.
320
- */
321
- async getManifest(slug, version) {
322
- return this.request("GET", `/modules/${slug}/versions/${version}/manifest`);
323
- }
324
- /**
325
- * Create a download token for a module release.
326
- */
327
- async createDownloadToken(moduleSlug, version) {
328
- return this.request("POST", "/download-tokens", {
329
- body: { moduleSlug, version },
330
- authenticated: true,
331
- });
332
- }
333
- // ──────────────────────────────────────────────────────────
334
- // License endpoints
335
- // ──────────────────────────────────────────────────────────
336
- /**
337
- * Validate a license key against the required tier.
338
- */
339
- async validateLicense(licenseKey, requiredTier) {
340
- const response = await this.request('POST', '/licenses/validate', {
341
- body: { licenseKey, requiredTier },
342
- });
343
- return response;
344
- }
345
- /**
346
- * Get full license status including expiry information.
347
- */
348
- async getLicenseStatus(licenseKey) {
349
- return this.request('GET', `/licenses/status?key=${encodeURIComponent(licenseKey)}`);
350
- }
351
- // ──────────────────────────────────────────────────────────
352
- // Legacy / backward-compat methods
353
- // ──────────────────────────────────────────────────────────
354
- /**
355
- * @deprecated Use getManifest(slug, version) instead.
356
- */
357
- async getModuleManifest(moduleId) {
358
- try {
359
- // Try to get latest manifest — for backward-compat we use "latest"
360
- return await this.getManifest(moduleId, "latest");
361
- }
362
- catch (error) {
363
- if (error instanceof errors_1.NotFoundError)
364
- return null;
365
- throw error;
366
- }
367
- }
368
- /**
369
- * Get release details for a specific module version.
370
- * Returns checksum, signature, and publicKey for verification.
371
- */
372
- async getReleaseInfo(slug, version) {
373
- return this.request("GET", `/modules/${slug}/versions/${version}`);
374
- }
375
- // ──────────────────────────────────────────────────────────
376
- // Publish endpoints (authenticated)
377
- // ──────────────────────────────────────────────────────────
378
- /**
379
- * Get a presigned S3 upload URL for a new module release.
380
- */
381
- async getUploadUrl(moduleSlug, version, size) {
382
- return this.request("POST", "/releases/upload-url", {
383
- body: { moduleSlug, version, size },
384
- authenticated: true,
385
- });
386
- }
387
- /**
388
- * Create a release record after uploading the artifact.
389
- */
390
- async createRelease(data) {
391
- return this.request("POST", "/releases", {
392
- body: data,
393
- authenticated: true,
394
- });
395
- }
396
- // ──────────────────────────────────────────────────────────
397
- // Checkout / upgrade endpoints (authenticated)
398
- // ──────────────────────────────────────────────────────────
399
- /**
400
- * Create a Paddle checkout session for tier upgrade.
401
- */
402
- async createCheckoutSession(tier, licenseKey) {
403
- return this.request("POST", "/checkout/session", {
404
- body: { tier, licenseKey },
405
- authenticated: true,
406
- });
407
- }
408
- /**
409
- * Poll the status of an ongoing Paddle checkout session.
410
- */
411
- async getCheckoutStatus(sessionId) {
412
- return this.request("GET", `/checkout/session/${sessionId}/status`, { authenticated: true });
413
- }
414
- // ──────────────────────────────────────────────────────────
415
- // Categories endpoint (authenticated)
416
- // ──────────────────────────────────────────────────────────
417
- /**
418
- * Get all available module categories.
419
- */
420
- async getCategories() {
421
- const result = await this.request("GET", "/modules/categories", { authenticated: true });
422
- return result.categories;
423
- }
424
- }
425
- exports.MarketplaceClient = MarketplaceClient;
@@ -1,73 +0,0 @@
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.TelemetryBuffer = void 0;
7
- const fs_extra_1 = __importDefault(require("fs-extra"));
8
- const path_1 = __importDefault(require("path"));
9
- const os_1 = __importDefault(require("os"));
10
- class TelemetryBuffer {
11
- static instance;
12
- logPath;
13
- buffer = [];
14
- constructor() {
15
- this.logPath = path_1.default.join(os_1.default.homedir(), ".kaven", "telemetry.log");
16
- }
17
- static getInstance() {
18
- if (!TelemetryBuffer.instance) {
19
- TelemetryBuffer.instance = new TelemetryBuffer();
20
- }
21
- return TelemetryBuffer.instance;
22
- }
23
- /**
24
- * Captura um evento de telemetria
25
- */
26
- capture(event, metadata, duration) {
27
- const telemetryEvent = {
28
- event,
29
- timestamp: new Date().toISOString(),
30
- metadata,
31
- duration,
32
- };
33
- this.buffer.push(telemetryEvent);
34
- }
35
- /**
36
- * Persiste os eventos do buffer no arquivo local
37
- */
38
- async flush() {
39
- if (this.buffer.length === 0)
40
- return;
41
- try {
42
- const logDir = path_1.default.dirname(this.logPath);
43
- await fs_extra_1.default.ensureDir(logDir);
44
- const lines = this.buffer.map((e) => JSON.stringify(e)).join("\n") + "\n";
45
- await fs_extra_1.default.appendFile(this.logPath, lines, "utf8");
46
- this.buffer = [];
47
- }
48
- catch (error) {
49
- // Falha silenciosa na telemetria para não interromper fluxo principal
50
- console.debug("Erro ao gravar telemetria:", error);
51
- }
52
- }
53
- /**
54
- * Recupera os últimos eventos registrados
55
- */
56
- async getRecentEvents(limit = 20) {
57
- if (!(await fs_extra_1.default.pathExists(this.logPath)))
58
- return [];
59
- try {
60
- const content = await fs_extra_1.default.readFile(this.logPath, "utf8");
61
- return content
62
- .trim()
63
- .split("\n")
64
- .reverse()
65
- .slice(0, limit)
66
- .map((line) => JSON.parse(line));
67
- }
68
- catch {
69
- return [];
70
- }
71
- }
72
- }
73
- exports.TelemetryBuffer = TelemetryBuffer;