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
  };
@@ -9,51 +42,222 @@ const ora_1 = __importDefault(require("ora"));
9
42
  const fs_extra_1 = __importDefault(require("fs-extra"));
10
43
  const path_1 = __importDefault(require("path"));
11
44
  const os_1 = __importDefault(require("os"));
45
+ const tar = __importStar(require("tar"));
12
46
  const MarketplaceClient_1 = require("../../infrastructure/MarketplaceClient");
13
47
  const AuthService_1 = require("../../core/AuthService");
14
- const add_1 = require("../module/add");
48
+ const ModuleInstaller_1 = require("../../core/ModuleInstaller");
49
+ const MarkerService_1 = require("../../core/MarkerService");
15
50
  const TelemetryBuffer_1 = require("../../infrastructure/TelemetryBuffer");
16
- async function marketplaceInstall(moduleId) {
51
+ const errors_1 = require("../../infrastructure/errors");
52
+ const SignatureVerifier_1 = require("../../core/SignatureVerifier");
53
+ /** Create a unique temp directory for this install session. */
54
+ async function makeTempDir() {
55
+ const base = path_1.default.join(os_1.default.tmpdir(), "kaven-install-");
56
+ return fs_extra_1.default.mkdtemp(base);
57
+ }
58
+ /** Format bytes into a human-readable string like "245 KB". */
59
+ function formatBytes(bytes) {
60
+ if (bytes < 1024)
61
+ return `${bytes} B`;
62
+ if (bytes < 1024 * 1024)
63
+ return `${Math.round(bytes / 1024)} KB`;
64
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
65
+ }
66
+ async function marketplaceInstall(slug, options = {}) {
17
67
  const telemetry = TelemetryBuffer_1.TelemetryBuffer.getInstance();
18
68
  const startTime = Date.now();
19
- telemetry.capture("cli.marketplace.install.start", { moduleId });
69
+ telemetry.capture("cli.marketplace.install.start", { slug });
20
70
  const authService = new AuthService_1.AuthService();
21
- const client = new MarketplaceClient_1.MarketplaceClient();
22
- // 1. Verificar Autenticação
23
- if (!(await authService.isAuthenticated())) {
24
- console.log(chalk_1.default.red("\n❌ Erro: Autenticação necessária."));
25
- console.log(chalk_1.default.yellow("O Marketplace da Kaven exige que você esteja logado para instalar módulos."));
26
- console.log(chalk_1.default.gray("\nExecute: kaven auth login\n"));
71
+ const client = new MarketplaceClient_1.MarketplaceClient(authService);
72
+ const projectRoot = process.cwd();
73
+ // 1. Verify authentication — required for downloads
74
+ let accessToken;
75
+ try {
76
+ accessToken = await authService.getValidToken();
77
+ }
78
+ catch {
79
+ console.error(chalk_1.default.red("Authentication required. Run: kaven auth login"));
80
+ telemetry.capture("cli.marketplace.install.error", { slug, error: "not_authenticated" }, Date.now() - startTime);
81
+ await telemetry.flush();
27
82
  process.exit(1);
83
+ return; // Unreachable but satisfies TS control flow
28
84
  }
29
- const spinner = (0, ora_1.default)(`Preparando instalação de '${moduleId}'...`).start();
85
+ void accessToken; // used implicitly via authService in client
86
+ const spinner = (0, ora_1.default)(`Preparing installation of '${slug}'...`).start();
87
+ let tempDir = null;
30
88
  try {
31
- // 2. Buscar Manifest Remoto
32
- spinner.text = `Buscando manifest de '${moduleId}' no Marketplace...`;
33
- const manifest = await client.getModuleManifest(moduleId);
34
- if (!manifest) {
35
- spinner.fail(chalk_1.default.red(`Módulo '${moduleId}' não encontrado no Marketplace.`));
89
+ // 2. Get module metadata
90
+ spinner.text = `Fetching module '${slug}' from Marketplace...`;
91
+ let moduleData;
92
+ try {
93
+ moduleData = await client.getModule(slug);
94
+ }
95
+ catch (error) {
96
+ if (error instanceof errors_1.NotFoundError) {
97
+ spinner.fail(chalk_1.default.red(`Module '${slug}' not found in Marketplace.`));
98
+ process.exit(1);
99
+ return;
100
+ }
101
+ throw error;
102
+ }
103
+ const latestVersion = moduleData.latestVersion
104
+ ?? moduleData.releases?.[0]?.version;
105
+ const installVersion = options.version ?? latestVersion;
106
+ if (!installVersion) {
107
+ spinner.fail(chalk_1.default.red(`No published version found for '${slug}'.`));
36
108
  process.exit(1);
109
+ return;
110
+ }
111
+ // 3. Check for conflict before downloading
112
+ const markerService = new MarkerService_1.MarkerService();
113
+ const installer = new ModuleInstaller_1.ModuleInstaller(projectRoot, markerService);
114
+ // Check if already installed by looking at marker files
115
+ const isInstalled = await installer.isModuleInstalled(slug);
116
+ if (isInstalled && !options.force) {
117
+ spinner.stop();
118
+ console.log(chalk_1.default.yellow(`Module '${slug}' is already installed. Use --force to overwrite.`));
119
+ telemetry.capture("cli.marketplace.install.skipped", { slug, reason: "already_installed" }, Date.now() - startTime);
120
+ await telemetry.flush();
121
+ return;
122
+ }
123
+ // 4. Create download token
124
+ spinner.text = `Creating download token for '${slug}@${installVersion}'...`;
125
+ const downloadToken = await client.createDownloadToken(slug, installVersion);
126
+ // 5. Download the tarball to a temp directory
127
+ tempDir = await makeTempDir();
128
+ const tarPath = path_1.default.join(tempDir, "module.tar.gz");
129
+ const extractDir = path_1.default.join(tempDir, "extracted");
130
+ await fs_extra_1.default.ensureDir(extractDir);
131
+ // Fetch with size reporting
132
+ spinner.text = `Downloading ${slug} v${installVersion}...`;
133
+ const absoluteUrl = await client.resolveUrl(downloadToken.downloadUrl);
134
+ const response = await fetch(absoluteUrl);
135
+ if (!response.ok) {
136
+ throw new Error(`Download failed: ${response.status} ${response.statusText}`);
137
+ }
138
+ const contentLength = response.headers.get("content-length");
139
+ const sizeStr = contentLength
140
+ ? ` (${formatBytes(parseInt(contentLength, 10))})`
141
+ : "";
142
+ spinner.text = `Downloading ${slug} v${installVersion}${sizeStr}...`;
143
+ if (!response.body) {
144
+ throw new Error("No response body received for download");
145
+ }
146
+ // Stream download to file
147
+ const fileStream = fs_extra_1.default.createWriteStream(tarPath);
148
+ const reader = response.body.getReader();
149
+ await new Promise((resolve, reject) => {
150
+ const pump = async () => {
151
+ try {
152
+ let reading = true;
153
+ while (reading) {
154
+ const { done, value } = await reader.read();
155
+ if (done) {
156
+ fileStream.end();
157
+ reading = false;
158
+ }
159
+ else {
160
+ if (!fileStream.write(value)) {
161
+ await new Promise((r) => fileStream.once("drain", r));
162
+ }
163
+ }
164
+ }
165
+ fileStream.once("finish", resolve);
166
+ fileStream.once("error", reject);
167
+ }
168
+ catch (err) {
169
+ reject(err);
170
+ }
171
+ };
172
+ pump();
173
+ });
174
+ // 6. Verify Ed25519 signature and SHA-256 checksum
175
+ const stat = await fs_extra_1.default.stat(tarPath);
176
+ if (stat.size === 0) {
177
+ throw new Error("Downloaded file is empty");
37
178
  }
38
- spinner.succeed(chalk_1.default.green(`Módulo '${moduleId}' encontrado.`));
39
- // 3. Salvar manifest temporário para usar o moduleAdd existente
40
- // Em uma versão futura, o moduleAdd poderia aceitar um objeto de manifest diretamente
41
- const tempDir = path_1.default.join(os_1.default.tmpdir(), "kaven-mkt-install");
42
- await fs_extra_1.default.ensureDir(tempDir);
43
- const tempManifestPath = path_1.default.join(tempDir, `${moduleId}-manifest.json`);
44
- await fs_extra_1.default.writeJson(tempManifestPath, manifest);
45
- // 4. Executar instalação (reutilizando lógica transacional do moduleAdd)
46
- await (0, add_1.moduleAdd)(tempManifestPath);
47
- // 5. Cleanup
48
- await fs_extra_1.default.remove(tempDir);
49
- telemetry.capture("cli.marketplace.install.success", { moduleId }, Date.now() - startTime);
179
+ if (!options.skipVerify) {
180
+ spinner.text = `Verifying signature for ${slug} v${installVersion}...`;
181
+ const releaseInfo = await client.getReleaseInfo(slug, installVersion);
182
+ if (releaseInfo.checksum &&
183
+ releaseInfo.signature &&
184
+ releaseInfo.publicKey) {
185
+ await (0, SignatureVerifier_1.verifyDownload)({
186
+ filePath: tarPath,
187
+ expectedChecksum: releaseInfo.checksum,
188
+ signature: releaseInfo.signature,
189
+ publicKeyBase64: releaseInfo.publicKey,
190
+ });
191
+ spinner.text = `Signature verified for ${slug} v${installVersion}`;
192
+ }
193
+ else {
194
+ spinner.text = `No signature data for ${slug} v${installVersion} — skipping verification`;
195
+ }
196
+ }
197
+ // 7. Extract tarball
198
+ spinner.text = `Extracting ${slug} v${installVersion}...`;
199
+ await tar.x({ file: tarPath, cwd: extractDir });
200
+ // 8. Read module.json manifest from extracted dir
201
+ const manifestPath = path_1.default.join(extractDir, "module.json");
202
+ const manifestExists = await fs_extra_1.default.pathExists(manifestPath);
203
+ if (!manifestExists) {
204
+ throw new Error(`module.json not found in extracted archive for '${slug}'`);
205
+ }
206
+ const manifest = await fs_extra_1.default.readJson(manifestPath);
207
+ // 9. Delegate to ModuleInstaller
208
+ spinner.text = `Installing ${slug} v${installVersion}...`;
209
+ await installer.install(manifest);
210
+ spinner.succeed(chalk_1.default.green(`Module '${slug}@${installVersion}' installed successfully.`));
211
+ console.log(chalk_1.default.gray(`Use 'kaven module doctor' to verify the installation.`));
212
+ telemetry.capture("cli.marketplace.install.success", { slug, version: installVersion }, Date.now() - startTime);
50
213
  await telemetry.flush();
51
214
  }
52
215
  catch (error) {
53
- telemetry.capture("cli.marketplace.install.error", { moduleId, error: error.message }, Date.now() - startTime);
216
+ spinner.stop();
217
+ if (error instanceof errors_1.SignatureVerificationError) {
218
+ console.error(chalk_1.default.red(`Signature verification failed for '${slug}': ${error.message}`));
219
+ console.error(chalk_1.default.yellow("Use --skip-verify to bypass (not recommended)."));
220
+ telemetry.capture("cli.marketplace.install.error", { slug, error: "signature_verification_failed" }, Date.now() - startTime);
221
+ await telemetry.flush();
222
+ process.exit(1);
223
+ return;
224
+ }
225
+ if (error instanceof errors_1.LicenseRequiredError) {
226
+ console.error(chalk_1.default.red(`License required: '${slug}' requires a '${error.requiredTier}' license.`));
227
+ console.error(chalk_1.default.yellow(`Run: kaven upgrade ${error.requiredTier}`));
228
+ telemetry.capture("cli.marketplace.install.error", { slug, error: "license_required", tier: error.requiredTier }, Date.now() - startTime);
229
+ await telemetry.flush();
230
+ process.exit(1);
231
+ return;
232
+ }
233
+ if (error instanceof errors_1.AuthenticationError ||
234
+ (error instanceof Error &&
235
+ error.message.includes("Not authenticated"))) {
236
+ console.error(chalk_1.default.red("Authentication required. Run: kaven auth login"));
237
+ telemetry.capture("cli.marketplace.install.error", { slug, error: "auth_error" }, Date.now() - startTime);
238
+ await telemetry.flush();
239
+ process.exit(1);
240
+ return;
241
+ }
242
+ if (error instanceof errors_1.NetworkError) {
243
+ console.error(chalk_1.default.red("Could not reach marketplace. Check your connection."));
244
+ telemetry.capture("cli.marketplace.install.error", { slug, error: "network_error" }, Date.now() - startTime);
245
+ await telemetry.flush();
246
+ process.exit(1);
247
+ return;
248
+ }
249
+ telemetry.capture("cli.marketplace.install.error", { slug, error: error.message }, Date.now() - startTime);
54
250
  await telemetry.flush();
55
- spinner.fail(chalk_1.default.red(`Falha na instalação do módulo '${moduleId}'.`));
251
+ spinner.fail(chalk_1.default.red(`Failed to install module '${slug}'.`));
56
252
  console.error(error);
57
253
  process.exit(1);
58
254
  }
255
+ finally {
256
+ // 10. Always cleanup temp dir
257
+ if (tempDir) {
258
+ await fs_extra_1.default.remove(tempDir).catch(() => {
259
+ // Ignore cleanup errors
260
+ });
261
+ }
262
+ }
59
263
  }
@@ -6,38 +6,116 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.marketplaceList = marketplaceList;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
8
  const ora_1 = __importDefault(require("ora"));
9
+ const cli_table3_1 = __importDefault(require("cli-table3"));
9
10
  const MarketplaceClient_1 = require("../../infrastructure/MarketplaceClient");
11
+ const AuthService_1 = require("../../core/AuthService");
12
+ const errors_1 = require("../../infrastructure/errors");
10
13
  const TelemetryBuffer_1 = require("../../infrastructure/TelemetryBuffer");
11
- async function marketplaceList() {
14
+ function colorTier(tier) {
15
+ switch (tier.toLowerCase()) {
16
+ case "starter":
17
+ return chalk_1.default.green(tier.toLowerCase());
18
+ case "complete":
19
+ return chalk_1.default.yellow(tier.toLowerCase());
20
+ case "pro":
21
+ return chalk_1.default.magenta(tier.toLowerCase());
22
+ case "enterprise":
23
+ return chalk_1.default.blue(tier.toLowerCase());
24
+ default:
25
+ return tier;
26
+ }
27
+ }
28
+ async function marketplaceList(options = {}) {
12
29
  const telemetry = TelemetryBuffer_1.TelemetryBuffer.getInstance();
13
30
  const startTime = Date.now();
14
31
  telemetry.capture("cli.marketplace.list.start");
15
- const client = new MarketplaceClient_1.MarketplaceClient();
16
- const spinner = (0, ora_1.default)("Buscando módulos no Marketplace...").start();
32
+ const authService = new AuthService_1.AuthService();
33
+ const client = new MarketplaceClient_1.MarketplaceClient(authService);
34
+ const page = options.page ?? 1;
35
+ const pageSize = Math.min(options.limit ?? 20, 100);
36
+ const sort = options.sort ?? "newest";
37
+ // Check auth — warn but don't block
38
+ const isAuth = await authService.isAuthenticated();
39
+ if (!isAuth) {
40
+ console.log(chalk_1.default.yellow("Warning: Not authenticated. Only public modules will be shown. Run: kaven auth login"));
41
+ }
42
+ const spinner = (0, ora_1.default)("Fetching modules from Marketplace...").start();
17
43
  try {
18
- const modules = await client.listModules();
44
+ const result = await client.listModules({
45
+ category: options.category,
46
+ page,
47
+ pageSize,
48
+ q: sort === "newest" ? undefined : undefined,
49
+ });
19
50
  spinner.stop();
51
+ const modules = result.data;
52
+ const meta = result.meta;
53
+ const total = result.total ?? meta?.total ?? modules.length;
54
+ if (options.json) {
55
+ console.log(JSON.stringify(result, null, 2));
56
+ telemetry.capture("cli.marketplace.list.success", { count: modules.length, json: true }, Date.now() - startTime);
57
+ await telemetry.flush();
58
+ return;
59
+ }
20
60
  if (modules.length === 0) {
21
- console.log(chalk_1.default.yellow("Nenhum módulo encontrado no Marketplace no momento."));
61
+ console.log(chalk_1.default.yellow("No modules found matching your criteria."));
62
+ telemetry.capture("cli.marketplace.list.success", { count: 0 }, Date.now() - startTime);
63
+ await telemetry.flush();
22
64
  return;
23
65
  }
24
- console.log(chalk_1.default.blue.bold("\n📦 Módulos Disponíveis no Marketplace:\n"));
25
- // Header (manual alignment)
26
- console.log(chalk_1.default.gray(`${"ID".padEnd(20)} ${"NOME".padEnd(20)} ${"VERSÃO".padEnd(10)} ${"AUTOR"}`));
27
- console.log(chalk_1.default.gray("-".repeat(70)));
28
- modules.forEach((mod) => {
29
- console.log(`${chalk_1.default.cyan(mod.id.padEnd(20))} ${chalk_1.default.white(mod.name.padEnd(20))} ${chalk_1.default.green(mod.version.padEnd(10))} ${chalk_1.default.gray(mod.author)}`);
30
- console.log(chalk_1.default.italic.gray(` > ${mod.description}\n`));
66
+ const table = new cli_table3_1.default({
67
+ head: [
68
+ chalk_1.default.white.bold("Slug"),
69
+ chalk_1.default.white.bold("Name"),
70
+ chalk_1.default.white.bold("Version"),
71
+ chalk_1.default.white.bold("Tier"),
72
+ chalk_1.default.white.bold("Installs"),
73
+ ],
74
+ style: {
75
+ head: [],
76
+ border: ["gray"],
77
+ },
31
78
  });
32
- console.log(chalk_1.default.gray(`Total: ${modules.length} módulos encontrados.\n`));
33
- console.log(chalk_1.default.gray("Use 'kaven marketplace install <id>' para instalar um módulo."));
34
- telemetry.capture("cli.marketplace.list.success", { count: modules.length }, Date.now() - startTime);
79
+ // Sort modules client-side based on sort option
80
+ const sorted = [...modules];
81
+ if (sort === "popular") {
82
+ sorted.sort((a, b) => b.installCount - a.installCount);
83
+ }
84
+ else if (sort === "name") {
85
+ sorted.sort((a, b) => a.name.localeCompare(b.name));
86
+ }
87
+ // newest = default server-side order
88
+ for (const mod of sorted) {
89
+ table.push([
90
+ chalk_1.default.cyan(mod.slug),
91
+ chalk_1.default.white(mod.name),
92
+ chalk_1.default.green(mod.latestVersion ?? "—"),
93
+ colorTier(mod.requiredTier ?? mod.tier ?? ""),
94
+ chalk_1.default.gray(String(mod.installCount)),
95
+ ]);
96
+ }
97
+ console.log(chalk_1.default.blue.bold("\nAvailable Marketplace Modules:\n"));
98
+ console.log(table.toString());
99
+ // Pagination footer
100
+ const totalPages = Math.ceil(total / pageSize);
101
+ const startItem = (page - 1) * pageSize + 1;
102
+ const endItem = Math.min(page * pageSize, total);
103
+ console.log(chalk_1.default.gray(`\nShowing ${startItem}-${endItem} of ${total} modules (page ${page}/${totalPages})`));
104
+ console.log(chalk_1.default.gray("Use 'kaven marketplace install <slug>' to install a module."));
105
+ telemetry.capture("cli.marketplace.list.success", { count: modules.length, total }, Date.now() - startTime);
35
106
  await telemetry.flush();
36
107
  }
37
108
  catch (error) {
109
+ spinner.stop();
110
+ if (error instanceof errors_1.NetworkError) {
111
+ console.error(chalk_1.default.red("Could not reach marketplace. Check your connection."));
112
+ telemetry.capture("cli.marketplace.list.error", { error: "network_error" }, Date.now() - startTime);
113
+ await telemetry.flush();
114
+ process.exit(1);
115
+ }
38
116
  telemetry.capture("cli.marketplace.list.error", { error: error.message }, Date.now() - startTime);
39
117
  await telemetry.flush();
40
- spinner.fail(chalk_1.default.red("Erro ao buscar módulos no Marketplace."));
118
+ console.error(chalk_1.default.red("Error fetching modules from Marketplace."));
41
119
  console.error(error);
42
120
  process.exit(1);
43
121
  }
@@ -5,11 +5,103 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.moduleDoctor = moduleDoctor;
7
7
  const chalk_1 = __importDefault(require("chalk"));
8
+ const child_process_1 = require("child_process");
9
+ const path_1 = __importDefault(require("path"));
10
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
11
  const ModuleDoctor_1 = require("../../core/ModuleDoctor");
9
12
  const MarkerService_1 = require("../../core/MarkerService");
10
13
  const ManifestParser_1 = require("../../core/ManifestParser");
14
+ function severityPrefix(severity) {
15
+ switch (severity) {
16
+ case "error":
17
+ return chalk_1.default.red("[ERROR]");
18
+ case "warning":
19
+ return chalk_1.default.yellow("[WARN] ");
20
+ case "info":
21
+ return chalk_1.default.cyan("[INFO] ");
22
+ default:
23
+ return chalk_1.default.green("[OK] ");
24
+ }
25
+ }
26
+ /** Attempt auto-fixes for fixable issues. */
27
+ async function applyFixes(results, projectRoot) {
28
+ const fixable = results.filter((r) => r.fixable);
29
+ if (fixable.length === 0) {
30
+ console.log(chalk_1.default.gray(" No automatically fixable issues found."));
31
+ return;
32
+ }
33
+ for (const result of fixable) {
34
+ const msg = result.message.toLowerCase();
35
+ // Missing npm deps: run pnpm install
36
+ if (msg.includes("missing npm dependency") || msg.includes("pnpm install")) {
37
+ console.log(chalk_1.default.blue(` Fixing: ${result.message}`));
38
+ try {
39
+ (0, child_process_1.execSync)("pnpm install", { cwd: projectRoot, stdio: "inherit" });
40
+ console.log(chalk_1.default.green(" ✓ pnpm install completed"));
41
+ }
42
+ catch {
43
+ console.log(chalk_1.default.red(" ✗ pnpm install failed"));
44
+ }
45
+ continue;
46
+ }
47
+ // Stale Prisma: run prisma generate
48
+ if (msg.includes("prisma")) {
49
+ console.log(chalk_1.default.blue(` Fixing: ${result.message}`));
50
+ try {
51
+ (0, child_process_1.execSync)("npx prisma generate", { cwd: projectRoot, stdio: "inherit" });
52
+ console.log(chalk_1.default.green(" ✓ npx prisma generate completed"));
53
+ }
54
+ catch {
55
+ console.log(chalk_1.default.red(" ✗ npx prisma generate failed"));
56
+ }
57
+ continue;
58
+ }
59
+ // Missing env vars: append placeholder
60
+ if (msg.includes("missing env vars") && result.file === ".env") {
61
+ const envPath = path_1.default.join(projectRoot, ".env");
62
+ const envExamplePath = path_1.default.join(projectRoot, ".env.example");
63
+ console.log(chalk_1.default.blue(` Fixing: ${result.message}`));
64
+ try {
65
+ const exampleContent = await fs_extra_1.default.readFile(envExamplePath, "utf-8");
66
+ const envContent = (await fs_extra_1.default.pathExists(envPath))
67
+ ? await fs_extra_1.default.readFile(envPath, "utf-8")
68
+ : "";
69
+ const parseKeys = (content) => {
70
+ const keys = new Set();
71
+ for (const line of content.split("\n")) {
72
+ const trimmed = line.trim();
73
+ if (trimmed.startsWith("#") || !trimmed.includes("="))
74
+ continue;
75
+ const key = trimmed.split("=")[0].trim();
76
+ if (key)
77
+ keys.add(key);
78
+ }
79
+ return keys;
80
+ };
81
+ const exampleKeys = parseKeys(exampleContent);
82
+ const envKeys = parseKeys(envContent);
83
+ let appendContent = "\n# Added by kaven doctor --fix\n";
84
+ for (const key of exampleKeys) {
85
+ if (!envKeys.has(key)) {
86
+ appendContent += `${key}=PLACEHOLDER\n`;
87
+ }
88
+ }
89
+ await fs_extra_1.default.appendFile(envPath, appendContent);
90
+ console.log(chalk_1.default.green(" ✓ Placeholder env vars appended to .env"));
91
+ }
92
+ catch {
93
+ console.log(chalk_1.default.red(" ✗ Could not append env vars"));
94
+ }
95
+ continue;
96
+ }
97
+ // Generic fallback
98
+ console.log(chalk_1.default.yellow(` Manual action required: ${result.message}`));
99
+ }
100
+ }
11
101
  async function moduleDoctor(options) {
12
- console.log(chalk_1.default.blue("🔍 Running module doctor...\n"));
102
+ if (!options.json) {
103
+ console.log(chalk_1.default.blue("Running module doctor...\n"));
104
+ }
13
105
  const markerService = new MarkerService_1.MarkerService();
14
106
  const manifestParser = new ManifestParser_1.ManifestParser();
15
107
  const doctor = new ModuleDoctor_1.ModuleDoctor(process.cwd(), markerService, manifestParser);
@@ -18,53 +110,66 @@ async function moduleDoctor(options) {
18
110
  results = await doctor.checkAll();
19
111
  }
20
112
  catch (error) {
21
- console.error(chalk_1.default.red(`❌ Heavy failure during doctor audit: ${error instanceof Error ? error.message : String(error)}`));
113
+ if (options.json) {
114
+ console.log(JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error), results: [] }));
115
+ }
116
+ else {
117
+ console.error(chalk_1.default.red(`[ERROR] Heavy failure during doctor audit: ${error instanceof Error ? error.message : String(error)}`));
118
+ }
22
119
  process.exit(1);
120
+ return;
23
121
  }
24
- if (results.length === 0) {
25
- console.log(chalk_1.default.green("✅ All checks passed! Your modules are healthy.\n"));
122
+ // JSON output mode
123
+ if (options.json) {
124
+ const errors = results.filter((r) => r.severity === "error");
125
+ const warnings = results.filter((r) => r.severity === "warning");
126
+ console.log(JSON.stringify({
127
+ success: errors.length === 0,
128
+ errors: errors.length,
129
+ warnings: warnings.length,
130
+ results,
131
+ }, null, 2));
132
+ process.exit(errors.length > 0 ? 1 : warnings.length > 0 ? 2 : 0);
26
133
  return;
27
134
  }
28
- // Grupar por severidade
135
+ // Human-readable output
136
+ for (const result of results) {
137
+ const prefix = severityPrefix(result.severity);
138
+ console.log(`${prefix} ${result.message}`);
139
+ if (result.file) {
140
+ console.log(chalk_1.default.gray(` file: ${result.file}`));
141
+ }
142
+ if (result.fixable && !options.fix) {
143
+ console.log(chalk_1.default.gray(` (fixable: run with --fix)`));
144
+ }
145
+ }
29
146
  const errors = results.filter((r) => r.severity === "error");
30
147
  const warnings = results.filter((r) => r.severity === "warning");
31
- if (errors.length > 0) {
32
- console.log(chalk_1.default.red(`❌ Found ${errors.length} error(s):\n`));
33
- for (const err of errors) {
34
- console.log(chalk_1.default.red(` ${err.message}`));
35
- if (err.file)
36
- console.log(chalk_1.default.gray(` file: ${err.file}`));
37
- if (err.fixable)
38
- console.log(chalk_1.default.yellow(` (💡 tip: run with --fix to attempt repair)`));
39
- console.log();
40
- }
148
+ console.log();
149
+ if (errors.length === 0 && warnings.length === 0) {
150
+ console.log(chalk_1.default.green("[OK] All checks passed! Your project is healthy."));
41
151
  }
42
- if (warnings.length > 0) {
43
- console.log(chalk_1.default.yellow(`⚠️ Found ${warnings.length} warning(s):\n`));
44
- for (const warn of warnings) {
45
- console.log(chalk_1.default.yellow(` ${warn.message}`));
46
- if (warn.file)
47
- console.log(chalk_1.default.gray(` file: ${warn.file}`));
48
- console.log();
152
+ else {
153
+ if (errors.length > 0) {
154
+ console.log(chalk_1.default.red(`[ERROR] Found ${errors.length} error(s)`));
49
155
  }
50
- }
51
- if (options.fix) {
52
- console.log(chalk_1.default.blue("🔧 Attempting to fix issues...\n"));
53
- const fixable = results.filter((r) => r.fixable);
54
- if (fixable.length === 0) {
55
- console.log(chalk_1.default.gray(" No issues are automatically fixable yet."));
156
+ if (warnings.length > 0) {
157
+ console.log(chalk_1.default.yellow(`[WARN] Found ${warnings.length} warning(s)`));
56
158
  }
57
- for (const result of fixable) {
58
- // Nota: No roadmap Week 2, a reparação real pode ser um placeholder
59
- // ou apenas o log de tentativa, dependendo da complexidade.
60
- // Por enquanto, apenas reportamos que a reparação automática
61
- // será implementada conforme o ModuleInstaller for refinado.
62
- console.log(chalk_1.default.yellow(` 🚧 Manual repair required for: ${result.message}. Autofix coming in future release.`));
159
+ if (!options.fix) {
160
+ console.log(chalk_1.default.gray("\nTip: Run with --fix to attempt automatic repairs"));
161
+ console.log(chalk_1.default.gray("Try: kaven module doctor --fix"));
63
162
  }
64
- console.log(chalk_1.default.green("\n✅ Audit complete. Fixes were logged for manual action.\n"));
65
163
  }
66
- else {
67
- console.log(chalk_1.default.blue("💡 Tip: Run with --fix to see potential repairs\n"));
164
+ if (options.fix) {
165
+ console.log();
166
+ console.log(chalk_1.default.blue("Applying auto-fixes...\n"));
167
+ await applyFixes(results, process.cwd());
168
+ console.log(chalk_1.default.green("\nAuto-fix completed."));
68
169
  }
69
- process.exit(errors.length > 0 ? 1 : 0);
170
+ // Exit codes: 0=all pass, 1=errors, 2=warnings only
171
+ if (errors.length > 0)
172
+ process.exit(1);
173
+ if (warnings.length > 0)
174
+ process.exit(2);
70
175
  }