kaven-cli 0.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +93 -0
  3. package/dist/commands/auth/login.js +44 -0
  4. package/dist/commands/auth/logout.js +25 -0
  5. package/dist/commands/auth/whoami.js +35 -0
  6. package/dist/commands/index.js +1 -0
  7. package/dist/commands/marketplace/install.js +59 -0
  8. package/dist/commands/marketplace/list.js +44 -0
  9. package/dist/commands/module/add.js +69 -0
  10. package/dist/commands/module/doctor.js +70 -0
  11. package/dist/commands/module/remove.js +58 -0
  12. package/dist/commands/modules/add.js +53 -0
  13. package/dist/commands/modules/list.js +40 -0
  14. package/dist/commands/modules/remove.js +54 -0
  15. package/dist/commands/telemetry/view.js +27 -0
  16. package/dist/core/AuthService.js +61 -0
  17. package/dist/core/ManifestParser.js +52 -0
  18. package/dist/core/MarkerService.js +62 -0
  19. package/dist/core/ModuleDoctor.js +162 -0
  20. package/dist/core/ModuleInstaller.js +66 -0
  21. package/dist/core/api/KavenApiClient.js +61 -0
  22. package/dist/core/auth/AuthManager.js +91 -0
  23. package/dist/core/index.js +1 -0
  24. package/dist/core/modules/Injector.js +86 -0
  25. package/dist/core/modules/ModuleInstaller.js +63 -0
  26. package/dist/core/modules/ModuleManager.js +59 -0
  27. package/dist/core/modules/ModuleRemover.js +60 -0
  28. package/dist/index.js +91 -0
  29. package/dist/infrastructure/Container.js +39 -0
  30. package/dist/infrastructure/MarketplaceClient.js +73 -0
  31. package/dist/infrastructure/TelemetryBuffer.js +71 -0
  32. package/dist/infrastructure/TransactionalFileSystem.js +74 -0
  33. package/dist/infrastructure/index.js +1 -0
  34. package/dist/lib/config.js +66 -0
  35. package/dist/lib/errors.js +32 -0
  36. package/dist/lib/logger.js +70 -0
  37. package/dist/types/manifest.js +45 -0
  38. package/dist/types/markers.js +10 -0
  39. package/dist/types/module.js +49 -0
  40. package/package.json +64 -0
@@ -0,0 +1,71 @@
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
+ constructor() {
12
+ this.buffer = [];
13
+ this.logPath = path_1.default.join(os_1.default.homedir(), ".kaven", "telemetry.log");
14
+ }
15
+ static getInstance() {
16
+ if (!TelemetryBuffer.instance) {
17
+ TelemetryBuffer.instance = new TelemetryBuffer();
18
+ }
19
+ return TelemetryBuffer.instance;
20
+ }
21
+ /**
22
+ * Captura um evento de telemetria
23
+ */
24
+ capture(event, metadata, duration) {
25
+ const telemetryEvent = {
26
+ event,
27
+ timestamp: new Date().toISOString(),
28
+ metadata,
29
+ duration,
30
+ };
31
+ this.buffer.push(telemetryEvent);
32
+ }
33
+ /**
34
+ * Persiste os eventos do buffer no arquivo local
35
+ */
36
+ async flush() {
37
+ if (this.buffer.length === 0)
38
+ return;
39
+ try {
40
+ const logDir = path_1.default.dirname(this.logPath);
41
+ await fs_extra_1.default.ensureDir(logDir);
42
+ const lines = this.buffer.map((e) => JSON.stringify(e)).join("\n") + "\n";
43
+ await fs_extra_1.default.appendFile(this.logPath, lines, "utf8");
44
+ this.buffer = [];
45
+ }
46
+ catch (error) {
47
+ // Falha silenciosa na telemetria para não interromper fluxo principal
48
+ console.debug("Erro ao gravar telemetria:", error);
49
+ }
50
+ }
51
+ /**
52
+ * Recupera os últimos eventos registrados
53
+ */
54
+ async getRecentEvents(limit = 20) {
55
+ if (!(await fs_extra_1.default.pathExists(this.logPath)))
56
+ return [];
57
+ try {
58
+ const content = await fs_extra_1.default.readFile(this.logPath, "utf8");
59
+ return content
60
+ .trim()
61
+ .split("\n")
62
+ .reverse()
63
+ .slice(0, limit)
64
+ .map((line) => JSON.parse(line));
65
+ }
66
+ catch {
67
+ return [];
68
+ }
69
+ }
70
+ }
71
+ exports.TelemetryBuffer = TelemetryBuffer;
@@ -0,0 +1,74 @@
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.TransactionalFileSystem = void 0;
7
+ const fs_extra_1 = __importDefault(require("fs-extra"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const glob_1 = require("glob");
10
+ class TransactionalFileSystem {
11
+ constructor(projectRoot, backupDir = ".agent/backups") {
12
+ this.projectRoot = projectRoot;
13
+ this.filesToBackup = [];
14
+ this.backupDir = path_1.default.join(projectRoot, backupDir);
15
+ this.backupId = `backup_${Date.now()}`;
16
+ }
17
+ async backup(filePaths) {
18
+ const backupPath = path_1.default.join(this.backupDir, this.backupId);
19
+ await fs_extra_1.default.ensureDir(backupPath);
20
+ for (const file of filePaths) {
21
+ const absolutePath = path_1.default.resolve(this.projectRoot, file);
22
+ if (!(await fs_extra_1.default.pathExists(absolutePath))) {
23
+ throw new Error(`File not found for backup: ${file}`);
24
+ }
25
+ const relativePath = path_1.default.relative(this.projectRoot, absolutePath);
26
+ const backupFile = path_1.default.join(backupPath, relativePath);
27
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(backupFile));
28
+ await fs_extra_1.default.copy(absolutePath, backupFile);
29
+ this.filesToBackup.push(absolutePath);
30
+ }
31
+ console.log(`📦 Backup created: ${this.backupId}`);
32
+ }
33
+ async rollback() {
34
+ const backupPath = path_1.default.join(this.backupDir, this.backupId);
35
+ if (!(await fs_extra_1.default.pathExists(backupPath))) {
36
+ throw new Error(`Backup not found: ${this.backupId}`);
37
+ }
38
+ const files = await (0, glob_1.glob)(`${backupPath}/**/*`, { nodir: true });
39
+ for (const backupFile of files) {
40
+ const relativePath = path_1.default.relative(backupPath, backupFile);
41
+ const targetFile = path_1.default.join(this.projectRoot, relativePath);
42
+ await fs_extra_1.default.ensureDir(path_1.default.dirname(targetFile));
43
+ await fs_extra_1.default.copy(backupFile, targetFile, { overwrite: true });
44
+ }
45
+ console.log(`♻️ Rollback complete: ${this.backupId}`);
46
+ }
47
+ async commit() {
48
+ const backupPath = path_1.default.join(this.backupDir, this.backupId);
49
+ if (await fs_extra_1.default.pathExists(backupPath)) {
50
+ await fs_extra_1.default.remove(backupPath);
51
+ }
52
+ console.log(`✅ Transaction committed`);
53
+ }
54
+ getBackupId() {
55
+ return this.backupId;
56
+ }
57
+ async cleanup() {
58
+ if (!(await fs_extra_1.default.pathExists(this.backupDir)))
59
+ return;
60
+ const backups = await fs_extra_1.default.readdir(this.backupDir);
61
+ const now = Date.now();
62
+ const weekInMs = 7 * 24 * 60 * 60 * 1000;
63
+ for (const backup of backups) {
64
+ const match = backup.match(/backup_(\d+)/);
65
+ if (match) {
66
+ const timestamp = parseInt(match[1]);
67
+ if (now - timestamp > weekInMs) {
68
+ await fs_extra_1.default.remove(path_1.default.join(this.backupDir, backup));
69
+ }
70
+ }
71
+ }
72
+ }
73
+ }
74
+ exports.TransactionalFileSystem = TransactionalFileSystem;
@@ -0,0 +1 @@
1
+ "use strict";
@@ -0,0 +1,66 @@
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.KavenConfigSchema = void 0;
7
+ const zod_1 = require("zod");
8
+ const fs_extra_1 = __importDefault(require("fs-extra"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const errors_1 = require("./errors");
11
+ exports.KavenConfigSchema = zod_1.z.object({
12
+ name: zod_1.z.string(),
13
+ version: zod_1.z.string(),
14
+ kaven: zod_1.z.object({
15
+ version: zod_1.z.string(),
16
+ features: zod_1.z.object({
17
+ multiTenant: zod_1.z.boolean().default(true),
18
+ database: zod_1.z.enum(['postgresql', 'mysql']).default('postgresql'),
19
+ payment: zod_1.z.enum(['stripe', 'paddle', 'pix', 'none']).default('none').optional(),
20
+ }),
21
+ modules: zod_1.z.object({
22
+ core: zod_1.z.record(zod_1.z.boolean()).default({}),
23
+ optional: zod_1.z.record(zod_1.z.boolean()).default({}),
24
+ }),
25
+ customizations: zod_1.z.object({
26
+ addedModules: zod_1.z.array(zod_1.z.string()).default([]),
27
+ removedModules: zod_1.z.array(zod_1.z.string()).default([]),
28
+ }).default({
29
+ addedModules: [],
30
+ removedModules: [],
31
+ }),
32
+ }),
33
+ });
34
+ const CONFIG_FILENAME = 'kaven.config.json';
35
+ class ConfigManager {
36
+ static async load(projectRoot) {
37
+ const configPath = path_1.default.join(projectRoot, CONFIG_FILENAME);
38
+ if (!await fs_extra_1.default.pathExists(configPath)) {
39
+ throw new errors_1.ConfigError(`Configuration file not found at ${configPath}`);
40
+ }
41
+ try {
42
+ const content = await fs_extra_1.default.readJson(configPath);
43
+ return exports.KavenConfigSchema.parse(content);
44
+ }
45
+ catch (error) {
46
+ if (error instanceof zod_1.z.ZodError) {
47
+ throw new errors_1.ConfigError(`Invalid configuration: ${error.issues.map(e => `${e.path.join('.')}: ${e.message}`).join(', ')}`);
48
+ }
49
+ throw new errors_1.ConfigError(`Failed to read configuration: ${error}`);
50
+ }
51
+ }
52
+ static async save(projectRoot, config) {
53
+ const configPath = path_1.default.join(projectRoot, CONFIG_FILENAME);
54
+ try {
55
+ const validated = exports.KavenConfigSchema.parse(config);
56
+ await fs_extra_1.default.writeJson(configPath, validated, { spaces: 2 });
57
+ }
58
+ catch (error) {
59
+ throw new errors_1.ConfigError(`Failed to save configuration: ${error}`);
60
+ }
61
+ }
62
+ static async exists(projectRoot) {
63
+ return fs_extra_1.default.pathExists(path_1.default.join(projectRoot, CONFIG_FILENAME));
64
+ }
65
+ }
66
+ exports.ConfigManager = ConfigManager;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthError = exports.ModuleError = exports.ConfigError = exports.KavenError = void 0;
4
+ class KavenError extends Error {
5
+ constructor(message, code = 'KAVEN_ERROR') {
6
+ super(message);
7
+ this.code = code;
8
+ this.name = 'KavenError';
9
+ }
10
+ }
11
+ exports.KavenError = KavenError;
12
+ class ConfigError extends KavenError {
13
+ constructor(message) {
14
+ super(message, 'CONFIG_ERROR');
15
+ this.name = 'ConfigError';
16
+ }
17
+ }
18
+ exports.ConfigError = ConfigError;
19
+ class ModuleError extends KavenError {
20
+ constructor(message) {
21
+ super(message, 'MODULE_ERROR');
22
+ this.name = 'ModuleError';
23
+ }
24
+ }
25
+ exports.ModuleError = ModuleError;
26
+ class AuthError extends KavenError {
27
+ constructor(message) {
28
+ super(message, 'AUTH_ERROR');
29
+ this.name = 'AuthError';
30
+ }
31
+ }
32
+ exports.AuthError = AuthError;
@@ -0,0 +1,70 @@
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.Logger = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const ora_1 = __importDefault(require("ora"));
9
+ class Logger {
10
+ static info(message) {
11
+ console.log(chalk_1.default.blue('ℹ'), message);
12
+ }
13
+ static succeedSpinner(message) {
14
+ this.success(message);
15
+ }
16
+ static success(message) {
17
+ if (this.spinner) {
18
+ this.spinner.succeed(chalk_1.default.green(message));
19
+ this.spinner = null;
20
+ }
21
+ else {
22
+ console.log(chalk_1.default.green('✔'), message);
23
+ }
24
+ }
25
+ static warn(message) {
26
+ console.log(chalk_1.default.yellow('⚠'), message);
27
+ }
28
+ static error(message, error) {
29
+ if (this.spinner) {
30
+ this.spinner.fail(chalk_1.default.red(message));
31
+ this.spinner = null;
32
+ }
33
+ else {
34
+ console.error(chalk_1.default.red('✖'), message);
35
+ }
36
+ if (error instanceof Error) {
37
+ console.error(chalk_1.default.dim(error.stack));
38
+ }
39
+ else if (error) {
40
+ console.error(chalk_1.default.dim(JSON.stringify(error)));
41
+ }
42
+ }
43
+ static startSpinner(text) {
44
+ if (this.spinner) {
45
+ this.spinner.text = text;
46
+ }
47
+ else {
48
+ this.spinner = (0, ora_1.default)(text).start();
49
+ }
50
+ }
51
+ static stopSpinner() {
52
+ if (this.spinner) {
53
+ this.spinner.stop();
54
+ this.spinner = null;
55
+ }
56
+ }
57
+ static box(title, lines) {
58
+ const width = Math.max(title.length + 4, ...lines.map(l => l.length + 4));
59
+ const border = '─'.repeat(width);
60
+ console.log(chalk_1.default.cyan(`┌${border}┐`));
61
+ console.log(chalk_1.default.cyan(`│ ${chalk_1.default.bold(title.padEnd(width - 2))} │`));
62
+ console.log(chalk_1.default.cyan(`│${border}│`));
63
+ lines.forEach(line => {
64
+ console.log(chalk_1.default.cyan(`│ ${line.padEnd(width - 2)} │`));
65
+ });
66
+ console.log(chalk_1.default.cyan(`└${border}┘`));
67
+ }
68
+ }
69
+ exports.Logger = Logger;
70
+ Logger.spinner = null;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ModuleManifestSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ const DependencySchema = zod_1.z.object({
6
+ npm: zod_1.z.array(zod_1.z.string()).default([]),
7
+ peerModules: zod_1.z.array(zod_1.z.string()).default([]),
8
+ kavenVersion: zod_1.z.string().default(">=0.1.0"),
9
+ });
10
+ const FileSetSchema = zod_1.z.object({
11
+ source: zod_1.z.string(),
12
+ dest: zod_1.z.string(),
13
+ });
14
+ const FilesSchema = zod_1.z.object({
15
+ backend: zod_1.z.array(FileSetSchema).default([]),
16
+ frontend: zod_1.z.array(FileSetSchema).default([]),
17
+ database: zod_1.z.array(FileSetSchema).default([]),
18
+ });
19
+ const InjectionSchema = zod_1.z.object({
20
+ file: zod_1.z.string(),
21
+ anchor: zod_1.z.string(),
22
+ code: zod_1.z.string(),
23
+ moduleName: zod_1.z.string().optional(),
24
+ });
25
+ const ScriptsSchema = zod_1.z.object({
26
+ postInstall: zod_1.z.string().nullable().default(null),
27
+ preRemove: zod_1.z.string().nullable().default(null),
28
+ });
29
+ const EnvVarSchema = zod_1.z.object({
30
+ key: zod_1.z.string(),
31
+ required: zod_1.z.boolean().default(false),
32
+ example: zod_1.z.string().optional(),
33
+ });
34
+ exports.ModuleManifestSchema = zod_1.z.object({
35
+ name: zod_1.z.string().min(1),
36
+ version: zod_1.z.string().regex(/^\d+\.\d+\.\d+$/),
37
+ description: zod_1.z.string().optional(),
38
+ author: zod_1.z.string().default("Kaven"),
39
+ license: zod_1.z.string().default("Proprietary"),
40
+ dependencies: DependencySchema,
41
+ files: FilesSchema,
42
+ injections: zod_1.z.array(InjectionSchema),
43
+ scripts: ScriptsSchema,
44
+ env: zod_1.z.array(EnvVarSchema).default([]),
45
+ });
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMarker = createMarker;
4
+ function createMarker(moduleName) {
5
+ return {
6
+ moduleName,
7
+ beginMarker: `// [KAVEN_MODULE:${moduleName} BEGIN]`,
8
+ endMarker: `// [KAVEN_MODULE:${moduleName} END]`,
9
+ };
10
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ModuleManifestSchema = exports.InjectionSchema = void 0;
4
+ const zod_1 = require("zod");
5
+ exports.InjectionSchema = zod_1.z.object({
6
+ targetFile: zod_1.z.string(), // relative to project root
7
+ anchor: zod_1.z.string(), // e.g. "KAVEN_MODULE_IMPORTS"
8
+ content: zod_1.z.string(), // content to inject
9
+ strategy: zod_1.z.enum(['append', 'prepend', 'replace']).default('append'),
10
+ });
11
+ exports.ModuleManifestSchema = zod_1.z.object({
12
+ id: zod_1.z.string().uuid().optional(), // optional for local dev
13
+ slug: zod_1.z.string().min(3).regex(/^[a-z0-9-]+$/),
14
+ version: zod_1.z.string().regex(/^\d+\.\d+\.\d+.*$/), // SemVer
15
+ displayName: zod_1.z.string(),
16
+ description: zod_1.z.string(),
17
+ category: zod_1.z.string().default('features'),
18
+ publisher: zod_1.z.string().default('kaven'),
19
+ // Compatibility
20
+ compat: zod_1.z.object({
21
+ kaven: zod_1.z.string(), // SemVer range e.g. "^2.0.0"
22
+ node: zod_1.z.string().optional(),
23
+ }),
24
+ // Dependencies
25
+ dependencies: zod_1.z.record(zod_1.z.string()).optional(), // npm deps
26
+ devDependencies: zod_1.z.record(zod_1.z.string()).optional(),
27
+ peerModules: zod_1.z.record(zod_1.z.string()).optional(), // other kaven modules
28
+ // Configuration Requirements (Env Vars)
29
+ env: zod_1.z.array(zod_1.z.object({
30
+ key: zod_1.z.string(),
31
+ description: zod_1.z.string(),
32
+ required: zod_1.z.boolean().default(true),
33
+ defaultValue: zod_1.z.string().optional(),
34
+ })).default([]),
35
+ // File Copying mappings (TO-BE: flexible mappings)
36
+ // For AS-IS compatibility, we assume standard structure (api/admin)
37
+ // Injections
38
+ injections: zod_1.z.array(exports.InjectionSchema).default([]),
39
+ // Database
40
+ database: zod_1.z.object({
41
+ schemaPath: zod_1.z.string().optional(), // path to schema.prisma fragment
42
+ migrations: zod_1.z.boolean().default(false),
43
+ }).optional(),
44
+ // Tasks (hooks)
45
+ hooks: zod_1.z.object({
46
+ postInstall: zod_1.z.array(zod_1.z.string()).default([]),
47
+ preRemove: zod_1.z.array(zod_1.z.string()).default([]),
48
+ }).optional(),
49
+ });
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "kaven-cli",
3
+ "version": "0.1.0-alpha.1",
4
+ "description": "Kaven CLI - The official command line tool for Kaven",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "kaven": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "prepublishOnly": "pnpm run build && pnpm run quality",
16
+ "lint": "eslint 'src/**/*.{ts,tsx}' --max-warnings 0",
17
+ "lint:fix": "eslint 'src/**/*.{ts,tsx}' --fix",
18
+ "test": "vitest run",
19
+ "test:watch": "vitest watch",
20
+ "test:coverage": "vitest run --coverage",
21
+ "typecheck": "tsc --noEmit",
22
+ "quality": "pnpm run lint && pnpm run typecheck && pnpm run test",
23
+ "quality:gate": "./.agent/scripts/quality-gate.sh",
24
+ "bootstrap": "./.agent/scripts/bootstrap.sh",
25
+ "evidence": "./.agent/scripts/evidence-bundle.sh",
26
+ "validate:evidence": "./.agent/scripts/validate-evidence.sh",
27
+ "create:pr": "./.agent/scripts/create-pr.sh",
28
+ "telemetry:view": "tail -f ~/.kaven/telemetry.log",
29
+ "telemetry:clear": "rm -f ~/.kaven/telemetry.log && touch ~/.kaven/telemetry.log"
30
+ },
31
+ "keywords": [
32
+ "kaven",
33
+ "cli",
34
+ "developer-tools",
35
+ "saas",
36
+ "boilerplate"
37
+ ],
38
+ "author": "Kaven",
39
+ "license": "Apache-2.0",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/KavenCompany/kaven-cli"
43
+ },
44
+ "devDependencies": {
45
+ "@types/fs-extra": "^11.0.4",
46
+ "@types/node": "^20.0.0",
47
+ "@typescript-eslint/eslint-plugin": "^8.53.1",
48
+ "@typescript-eslint/parser": "^8.53.1",
49
+ "eslint": "^8.0.0",
50
+ "typescript": "^5.0.0",
51
+ "vitest": "^1.0.0"
52
+ },
53
+ "engines": {
54
+ "node": ">=20"
55
+ },
56
+ "dependencies": {
57
+ "chalk": "^5.6.2",
58
+ "commander": "^14.0.2",
59
+ "fs-extra": "^11.3.3",
60
+ "glob": "^13.0.0",
61
+ "ora": "^9.1.0",
62
+ "zod": "^4.3.6"
63
+ }
64
+ }