kaven-cli 0.1.0-alpha.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -45
- package/dist/commands/auth/login.js +97 -19
- package/dist/commands/auth/logout.js +4 -6
- package/dist/commands/auth/whoami.js +12 -11
- package/dist/commands/cache/index.js +43 -0
- package/dist/commands/config/index.js +128 -0
- package/dist/commands/init/index.js +209 -0
- package/dist/commands/init-ci/index.js +153 -0
- package/dist/commands/license/index.js +10 -0
- package/dist/commands/license/status.js +44 -0
- package/dist/commands/license/tier-table.js +46 -0
- package/dist/commands/marketplace/browse.js +219 -0
- package/dist/commands/marketplace/install.js +233 -29
- package/dist/commands/marketplace/list.js +94 -16
- package/dist/commands/module/doctor.js +143 -38
- package/dist/commands/module/publish.js +291 -0
- package/dist/commands/upgrade/check.js +162 -0
- package/dist/commands/upgrade/index.js +218 -0
- package/dist/core/AuthService.js +207 -14
- package/dist/core/CacheManager.js +151 -0
- package/dist/core/ConfigManager.js +165 -0
- package/dist/core/EnvManager.js +196 -0
- package/dist/core/ErrorRecovery.js +191 -0
- package/dist/core/LicenseService.js +118 -0
- package/dist/core/ModuleDoctor.js +286 -0
- package/dist/core/ModuleInstaller.js +136 -2
- package/dist/core/ProjectInitializer.js +117 -0
- package/dist/core/RegistryResolver.js +94 -0
- package/dist/core/ScriptRunner.js +72 -0
- package/dist/core/SignatureVerifier.js +72 -0
- package/dist/index.js +265 -20
- package/dist/infrastructure/MarketplaceClient.js +388 -64
- package/dist/infrastructure/errors.js +61 -0
- package/dist/types/auth.js +2 -0
- package/dist/types/marketplace.js +2 -0
- package/package.json +15 -2
|
@@ -0,0 +1,128 @@
|
|
|
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
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.configSet = configSet;
|
|
40
|
+
exports.configGet = configGet;
|
|
41
|
+
exports.configView = configView;
|
|
42
|
+
exports.configReset = configReset;
|
|
43
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
+
const ConfigManager_1 = require("../../core/ConfigManager");
|
|
45
|
+
/**
|
|
46
|
+
* C2.4: Config set — Persist value to ~/.kaven/config.json
|
|
47
|
+
*/
|
|
48
|
+
async function configSet(key, value) {
|
|
49
|
+
if (!key || !value) {
|
|
50
|
+
console.error(chalk_1.default.red("Error: Both key and value are required"));
|
|
51
|
+
console.error(chalk_1.default.gray("Usage: kaven config set KEY VALUE"));
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
54
|
+
await ConfigManager_1.configManager.initialize();
|
|
55
|
+
try {
|
|
56
|
+
await ConfigManager_1.configManager.set(key, value);
|
|
57
|
+
console.log(chalk_1.default.green(`✅ Set ${chalk_1.default.bold(key)} = ${chalk_1.default.bold(value)}`));
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* C2.4: Config get — Read with defaults
|
|
66
|
+
*/
|
|
67
|
+
async function configGet(key, options) {
|
|
68
|
+
if (!key) {
|
|
69
|
+
console.error(chalk_1.default.red("Error: Key is required"));
|
|
70
|
+
console.error(chalk_1.default.gray("Usage: kaven config get KEY"));
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
await ConfigManager_1.configManager.initialize();
|
|
74
|
+
try {
|
|
75
|
+
const value = ConfigManager_1.configManager.get(key);
|
|
76
|
+
if (options.json) {
|
|
77
|
+
console.log(JSON.stringify({ [key]: value }, null, 2));
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
console.log(value);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* C2.4: Config view — Show all configuration
|
|
90
|
+
*/
|
|
91
|
+
async function configView(options) {
|
|
92
|
+
await ConfigManager_1.configManager.initialize();
|
|
93
|
+
const config = ConfigManager_1.configManager.getAll();
|
|
94
|
+
if (options.json) {
|
|
95
|
+
console.log(JSON.stringify(config, null, 2));
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(chalk_1.default.bold("Kaven Configuration:"));
|
|
99
|
+
console.log(chalk_1.default.gray(`Location: ${ConfigManager_1.configManager.getConfigDir()}/config.json`));
|
|
100
|
+
console.log();
|
|
101
|
+
const entries = Object.entries(config);
|
|
102
|
+
const maxKeyLen = Math.max(...entries.map(([k]) => k.length));
|
|
103
|
+
for (const [key, value] of entries) {
|
|
104
|
+
const padding = " ".repeat(maxKeyLen - key.length);
|
|
105
|
+
const displayValue = typeof value === "object"
|
|
106
|
+
? JSON.stringify(value, null, 2).replace(/\n/g, "\n" + " ".repeat(maxKeyLen + 3))
|
|
107
|
+
: value;
|
|
108
|
+
console.log(` ${key}${padding} ${chalk_1.default.cyan(displayValue)}`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* C2.4: Config reset — Reset to defaults
|
|
114
|
+
*/
|
|
115
|
+
async function configReset() {
|
|
116
|
+
const { confirm } = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
|
|
117
|
+
const confirmed = await confirm({
|
|
118
|
+
message: "Are you sure you want to reset config to defaults?",
|
|
119
|
+
default: false,
|
|
120
|
+
});
|
|
121
|
+
if (!confirmed) {
|
|
122
|
+
console.log(chalk_1.default.yellow("Cancelled."));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
await ConfigManager_1.configManager.initialize();
|
|
126
|
+
await ConfigManager_1.configManager.reset();
|
|
127
|
+
console.log(chalk_1.default.green("✅ Config reset to defaults"));
|
|
128
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
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
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.initProject = initProject;
|
|
40
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
41
|
+
const ora_1 = __importDefault(require("ora"));
|
|
42
|
+
const path_1 = __importDefault(require("path"));
|
|
43
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
44
|
+
const ProjectInitializer_1 = require("../../core/ProjectInitializer");
|
|
45
|
+
const ConfigManager_1 = require("../../core/ConfigManager");
|
|
46
|
+
async function promptAnswers(projectName) {
|
|
47
|
+
// Dynamic import to keep startup fast and avoid issues if not installed
|
|
48
|
+
const { input, select } = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
|
|
49
|
+
// Load defaults from config if available
|
|
50
|
+
await ConfigManager_1.configManager.initialize();
|
|
51
|
+
const existingDefaults = ConfigManager_1.configManager.getAll().projectDefaults || {};
|
|
52
|
+
const dbUrl = await input({
|
|
53
|
+
message: "Database URL (PostgreSQL):",
|
|
54
|
+
default: existingDefaults.dbUrl ||
|
|
55
|
+
`postgresql://user:password@localhost:5432/${projectName}`,
|
|
56
|
+
});
|
|
57
|
+
const emailProvider = await select({
|
|
58
|
+
message: "Email provider:",
|
|
59
|
+
choices: [
|
|
60
|
+
{ name: "Postmark", value: "postmark" },
|
|
61
|
+
{ name: "Resend", value: "resend" },
|
|
62
|
+
{ name: "AWS SES", value: "ses" },
|
|
63
|
+
{ name: "SMTP", value: "smtp" },
|
|
64
|
+
],
|
|
65
|
+
default: existingDefaults.emailProvider || "postmark",
|
|
66
|
+
});
|
|
67
|
+
const locale = await input({
|
|
68
|
+
message: "Default locale:",
|
|
69
|
+
default: existingDefaults.locale || "en-US",
|
|
70
|
+
});
|
|
71
|
+
const currency = await input({
|
|
72
|
+
message: "Default currency:",
|
|
73
|
+
default: existingDefaults.currency || "USD",
|
|
74
|
+
});
|
|
75
|
+
return { dbUrl, emailProvider, locale, currency };
|
|
76
|
+
}
|
|
77
|
+
async function initProject(projectName, options) {
|
|
78
|
+
const initializer = new ProjectInitializer_1.ProjectInitializer();
|
|
79
|
+
// Determine project name interactively if not provided
|
|
80
|
+
let resolvedName = projectName ?? "";
|
|
81
|
+
if (!resolvedName) {
|
|
82
|
+
if (options.defaults) {
|
|
83
|
+
resolvedName = "my-kaven-app";
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const { input } = await Promise.resolve().then(() => __importStar(require("@inquirer/prompts")));
|
|
87
|
+
resolvedName = await input({
|
|
88
|
+
message: "Project name:",
|
|
89
|
+
default: "my-kaven-app",
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Validate name
|
|
94
|
+
const validation = initializer.validateName(resolvedName);
|
|
95
|
+
if (!validation.valid) {
|
|
96
|
+
console.error(chalk_1.default.red(`Error: ${validation.reason}`));
|
|
97
|
+
console.error(chalk_1.default.gray("Try: kaven init my-project-name (use lowercase letters and hyphens)"));
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
const name = resolvedName;
|
|
101
|
+
const targetDir = path_1.default.resolve(process.cwd(), name);
|
|
102
|
+
// Check if directory already exists
|
|
103
|
+
if ((await fs_extra_1.default.pathExists(targetDir)) && !options.force) {
|
|
104
|
+
console.error(chalk_1.default.red(`Error: Directory "${name}" already exists. Use --force to overwrite.`));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
// Get prompt answers or use defaults
|
|
108
|
+
let answers;
|
|
109
|
+
if (options.defaults) {
|
|
110
|
+
// Try to load from config, then fallback to options
|
|
111
|
+
await ConfigManager_1.configManager.initialize();
|
|
112
|
+
const configDefaults = ConfigManager_1.configManager.getAll().projectDefaults || {};
|
|
113
|
+
answers = {
|
|
114
|
+
dbUrl: options.dbUrl ||
|
|
115
|
+
configDefaults.dbUrl ||
|
|
116
|
+
`postgresql://user:password@localhost:5432/${name}`,
|
|
117
|
+
emailProvider: options.emailProvider || configDefaults.emailProvider || "postmark",
|
|
118
|
+
locale: options.locale || configDefaults.locale || "en-US",
|
|
119
|
+
currency: options.currency || configDefaults.currency || "USD",
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
answers = await promptAnswers(name);
|
|
124
|
+
}
|
|
125
|
+
console.log();
|
|
126
|
+
// Clone template
|
|
127
|
+
const cloneSpinner = (0, ora_1.default)("Cloning kaven-template...").start();
|
|
128
|
+
try {
|
|
129
|
+
await initializer.cloneTemplate(targetDir);
|
|
130
|
+
cloneSpinner.succeed("Template cloned successfully");
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
cloneSpinner.fail("Failed to clone template");
|
|
134
|
+
console.error(chalk_1.default.red(error instanceof Error ? error.message : String(error)));
|
|
135
|
+
console.error(chalk_1.default.gray("Try: ensure git is installed and you have internet access"));
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
// Remove .git directory
|
|
139
|
+
const gitRemoveSpinner = (0, ora_1.default)("Removing .git directory...").start();
|
|
140
|
+
await initializer.removeGitDir(targetDir);
|
|
141
|
+
gitRemoveSpinner.succeed(".git directory removed");
|
|
142
|
+
// Replace placeholders
|
|
143
|
+
const placeholderSpinner = (0, ora_1.default)("Configuring project files...").start();
|
|
144
|
+
await initializer.replacePlaceholders(targetDir, {
|
|
145
|
+
...answers,
|
|
146
|
+
projectName: name,
|
|
147
|
+
});
|
|
148
|
+
placeholderSpinner.succeed("Project files configured");
|
|
149
|
+
// Run pnpm install
|
|
150
|
+
if (!options.skipInstall) {
|
|
151
|
+
const installSpinner = (0, ora_1.default)("Installing dependencies (pnpm install)...").start();
|
|
152
|
+
try {
|
|
153
|
+
await initializer.runInstall(targetDir);
|
|
154
|
+
installSpinner.succeed("Dependencies installed");
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
installSpinner.warn("Dependency installation failed — run pnpm install manually");
|
|
158
|
+
console.error(chalk_1.default.gray(error instanceof Error ? error.message : String(error)));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Init git
|
|
162
|
+
if (!options.skipGit) {
|
|
163
|
+
const gitSpinner = (0, ora_1.default)("Initializing git repository...").start();
|
|
164
|
+
try {
|
|
165
|
+
await initializer.initGit(targetDir);
|
|
166
|
+
gitSpinner.succeed("Git repository initialized");
|
|
167
|
+
}
|
|
168
|
+
catch (error) {
|
|
169
|
+
gitSpinner.warn("Git init failed — initialize manually");
|
|
170
|
+
console.error(chalk_1.default.gray(error instanceof Error ? error.message : String(error)));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// Health check
|
|
174
|
+
const healthCheckSpinner = (0, ora_1.default)("Running health check...").start();
|
|
175
|
+
const health = await initializer.healthCheck(targetDir);
|
|
176
|
+
if (health.healthy) {
|
|
177
|
+
healthCheckSpinner.succeed("Health check passed");
|
|
178
|
+
}
|
|
179
|
+
else {
|
|
180
|
+
healthCheckSpinner.warn("Health check found issues:");
|
|
181
|
+
for (const issue of health.issues) {
|
|
182
|
+
console.log(chalk_1.default.yellow(` ⚠ ${issue}`));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Success message
|
|
186
|
+
console.log();
|
|
187
|
+
console.log(chalk_1.default.green("✅ Project created successfully!"));
|
|
188
|
+
console.log();
|
|
189
|
+
console.log(chalk_1.default.bold("Next steps:"));
|
|
190
|
+
console.log(chalk_1.default.cyan(` cd ${name}`));
|
|
191
|
+
console.log(chalk_1.default.cyan(" cp .env.example .env"));
|
|
192
|
+
console.log(chalk_1.default.cyan(" npx prisma migrate dev"));
|
|
193
|
+
console.log(chalk_1.default.cyan(" pnpm dev"));
|
|
194
|
+
console.log();
|
|
195
|
+
console.log(chalk_1.default.gray("For more help, visit: https://docs.kaven.sh/getting-started"));
|
|
196
|
+
// Save project defaults to config for future use
|
|
197
|
+
await ConfigManager_1.configManager.initialize();
|
|
198
|
+
try {
|
|
199
|
+
await ConfigManager_1.configManager.set("projectDefaults", {
|
|
200
|
+
dbUrl: answers.dbUrl,
|
|
201
|
+
emailProvider: answers.emailProvider,
|
|
202
|
+
locale: answers.locale,
|
|
203
|
+
currency: answers.currency,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
// Non-critical, continue if config save fails
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
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.initCi = initCi;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const ora_1 = __importDefault(require("ora"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
11
|
+
const GITHUB_WORKFLOW_TEST = `name: Tests
|
|
12
|
+
on:
|
|
13
|
+
push:
|
|
14
|
+
branches: [main, develop]
|
|
15
|
+
pull_request:
|
|
16
|
+
branches: [main, develop]
|
|
17
|
+
|
|
18
|
+
jobs:
|
|
19
|
+
test:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
services:
|
|
22
|
+
postgres:
|
|
23
|
+
image: postgres:15
|
|
24
|
+
env:
|
|
25
|
+
POSTGRES_PASSWORD: postgres
|
|
26
|
+
options: >-
|
|
27
|
+
--health-cmd pg_isready
|
|
28
|
+
--health-interval 10s
|
|
29
|
+
--health-timeout 5s
|
|
30
|
+
--health-retries 5
|
|
31
|
+
ports:
|
|
32
|
+
- 5432:5432
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/checkout@v3
|
|
35
|
+
- uses: pnpm/action-setup@v2
|
|
36
|
+
with:
|
|
37
|
+
version: 8
|
|
38
|
+
- uses: actions/setup-node@v3
|
|
39
|
+
with:
|
|
40
|
+
node-version: 20
|
|
41
|
+
cache: 'pnpm'
|
|
42
|
+
- run: pnpm install
|
|
43
|
+
- run: pnpm typecheck
|
|
44
|
+
- run: pnpm lint
|
|
45
|
+
- run: pnpm test
|
|
46
|
+
- run: pnpm build
|
|
47
|
+
`;
|
|
48
|
+
const GITHUB_WORKFLOW_PUBLISH = `name: Publish Module
|
|
49
|
+
on:
|
|
50
|
+
push:
|
|
51
|
+
tags:
|
|
52
|
+
- 'v*'
|
|
53
|
+
|
|
54
|
+
jobs:
|
|
55
|
+
publish:
|
|
56
|
+
runs-on: ubuntu-latest
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v3
|
|
59
|
+
- uses: pnpm/action-setup@v2
|
|
60
|
+
with:
|
|
61
|
+
version: 8
|
|
62
|
+
- uses: actions/setup-node@v3
|
|
63
|
+
with:
|
|
64
|
+
node-version: 20
|
|
65
|
+
cache: 'pnpm'
|
|
66
|
+
- run: pnpm install
|
|
67
|
+
- run: pnpm build
|
|
68
|
+
- name: Publish to Kaven Marketplace
|
|
69
|
+
env:
|
|
70
|
+
KAVEN_LICENSE_KEY: \${{ secrets.KAVEN_LICENSE_KEY }}
|
|
71
|
+
run: kaven module publish
|
|
72
|
+
`;
|
|
73
|
+
const PRE_COMMIT_HOOK = `#!/bin/bash
|
|
74
|
+
# Pre-commit hook for Kaven projects
|
|
75
|
+
set -e
|
|
76
|
+
|
|
77
|
+
echo "🔍 Running pre-commit checks..."
|
|
78
|
+
|
|
79
|
+
# Lint
|
|
80
|
+
echo " Linting..."
|
|
81
|
+
pnpm lint || exit 1
|
|
82
|
+
|
|
83
|
+
# Type check
|
|
84
|
+
echo " Type checking..."
|
|
85
|
+
pnpm typecheck || exit 1
|
|
86
|
+
|
|
87
|
+
# Format check
|
|
88
|
+
if command -v pnpm format &> /dev/null; then
|
|
89
|
+
echo " Format checking..."
|
|
90
|
+
pnpm format || exit 1
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
echo "✅ All pre-commit checks passed"
|
|
94
|
+
`;
|
|
95
|
+
/**
|
|
96
|
+
* C2.6: Initialize CI/CD templates
|
|
97
|
+
*/
|
|
98
|
+
async function initCi(options) {
|
|
99
|
+
const cwd = process.cwd();
|
|
100
|
+
// Check if it's a Kaven project
|
|
101
|
+
const packageJsonPath = path_1.default.join(cwd, "package.json");
|
|
102
|
+
if (!(await fs_extra_1.default.pathExists(packageJsonPath))) {
|
|
103
|
+
console.error(chalk_1.default.red("Error: package.json not found. Run this in a Kaven project root."));
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
const spinner = (0, ora_1.default)("Setting up CI/CD configuration...").start();
|
|
107
|
+
try {
|
|
108
|
+
// Create .github/workflows directory
|
|
109
|
+
const workflowDir = path_1.default.join(cwd, ".github", "workflows");
|
|
110
|
+
await fs_extra_1.default.ensureDir(workflowDir);
|
|
111
|
+
// Create test workflow
|
|
112
|
+
const testWorkflowPath = path_1.default.join(workflowDir, "test.yml");
|
|
113
|
+
if (!options.dryRun) {
|
|
114
|
+
await fs_extra_1.default.writeFile(testWorkflowPath, GITHUB_WORKFLOW_TEST);
|
|
115
|
+
}
|
|
116
|
+
spinner.text = `Creating ${chalk_1.default.cyan(".github/workflows/test.yml")}...`;
|
|
117
|
+
// Create publish workflow
|
|
118
|
+
const publishWorkflowPath = path_1.default.join(workflowDir, "publish.yml");
|
|
119
|
+
if (!options.dryRun) {
|
|
120
|
+
await fs_extra_1.default.writeFile(publishWorkflowPath, GITHUB_WORKFLOW_PUBLISH);
|
|
121
|
+
}
|
|
122
|
+
spinner.text = `Creating ${chalk_1.default.cyan(".github/workflows/publish.yml")}...`;
|
|
123
|
+
// Create pre-commit hook
|
|
124
|
+
const hookDir = path_1.default.join(cwd, ".husky");
|
|
125
|
+
await fs_extra_1.default.ensureDir(hookDir);
|
|
126
|
+
const preCommitHookPath = path_1.default.join(hookDir, "pre-commit");
|
|
127
|
+
if (!options.dryRun) {
|
|
128
|
+
await fs_extra_1.default.writeFile(preCommitHookPath, PRE_COMMIT_HOOK);
|
|
129
|
+
await fs_extra_1.default.chmod(preCommitHookPath, 0o755);
|
|
130
|
+
}
|
|
131
|
+
spinner.text = `Creating ${chalk_1.default.cyan(".husky/pre-commit")}...`;
|
|
132
|
+
spinner.succeed("CI/CD configuration created");
|
|
133
|
+
console.log();
|
|
134
|
+
console.log(chalk_1.default.bold("Files created:"));
|
|
135
|
+
console.log(` ${chalk_1.default.cyan(".github/workflows/test.yml")} - Run tests on push/PR`);
|
|
136
|
+
console.log(` ${chalk_1.default.cyan(".github/workflows/publish.yml")} - Publish on git tags`);
|
|
137
|
+
console.log(` ${chalk_1.default.cyan(".husky/pre-commit")} - Local pre-commit validation`);
|
|
138
|
+
console.log();
|
|
139
|
+
console.log(chalk_1.default.bold("Next steps:"));
|
|
140
|
+
console.log(chalk_1.default.gray(" 1. Install husky: pnpm husky install"));
|
|
141
|
+
console.log(chalk_1.default.gray(" 2. Add GitHub secrets: KAVEN_LICENSE_KEY"));
|
|
142
|
+
console.log(chalk_1.default.gray(" 3. Push to GitHub and watch workflows run"));
|
|
143
|
+
if (options.dryRun) {
|
|
144
|
+
console.log();
|
|
145
|
+
console.log(chalk_1.default.yellow("(Dry-run: No files were actually created)"));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
spinner.fail("Failed to create CI/CD configuration");
|
|
150
|
+
console.error(chalk_1.default.red(error instanceof Error ? error.message : String(error)));
|
|
151
|
+
process.exit(1);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildLicenseCommand = buildLicenseCommand;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const status_js_1 = require("./status.js");
|
|
6
|
+
function buildLicenseCommand() {
|
|
7
|
+
const cmd = new commander_1.Command('license').description('Manage Kaven licenses');
|
|
8
|
+
cmd.addCommand((0, status_js_1.buildLicenseStatusCommand)());
|
|
9
|
+
return cmd;
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
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.buildLicenseStatusCommand = buildLicenseStatusCommand;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const LicenseService_js_1 = require("../../core/LicenseService.js");
|
|
10
|
+
function buildLicenseStatusCommand() {
|
|
11
|
+
return new commander_1.Command('status')
|
|
12
|
+
.description('Show license status and tier information')
|
|
13
|
+
.argument('[license-key]', 'License key to check (uses stored license if omitted)')
|
|
14
|
+
.action(async (licenseKey) => {
|
|
15
|
+
const service = new LicenseService_js_1.LicenseService();
|
|
16
|
+
// Resolve key from arg or environment
|
|
17
|
+
const key = licenseKey ?? process.env.KAVEN_LICENSE_KEY;
|
|
18
|
+
if (!key) {
|
|
19
|
+
console.error(chalk_1.default.red('✗ No license key provided. Pass as argument or set KAVEN_LICENSE_KEY.'));
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
console.log(chalk_1.default.dim('Checking license status...'));
|
|
24
|
+
const status = await service.getLicenseStatus(key);
|
|
25
|
+
console.log('\n' + chalk_1.default.bold('License Status') + '\n');
|
|
26
|
+
console.log(` Key: ${chalk_1.default.dim(status.key.substring(0, 16) + '...')}`);
|
|
27
|
+
console.log(` Tier: ${chalk_1.default.magenta(status.tier)}`);
|
|
28
|
+
if (status.expiresAt) {
|
|
29
|
+
const days = status.daysUntilExpiry;
|
|
30
|
+
const expiryColor = days !== null && days < 30 ? chalk_1.default.red : chalk_1.default.green;
|
|
31
|
+
console.log(` Expires: ${expiryColor(status.expiresAt)}${days !== null ? chalk_1.default.dim(` (${days} days)`) : ''}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
console.log(` Expires: ${chalk_1.default.green('Never')}`);
|
|
35
|
+
}
|
|
36
|
+
console.log();
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
const message = err instanceof Error ? err.message : 'Failed to check license status';
|
|
40
|
+
console.error(chalk_1.default.red('✗ ' + message));
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
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.printTierComparisonTable = printTierComparisonTable;
|
|
7
|
+
const cli_table3_1 = __importDefault(require("cli-table3"));
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const TIERS = [
|
|
10
|
+
{ name: 'STARTER', price: '$149', projects: '1', tenants: '10', marketplace: false },
|
|
11
|
+
{ name: 'COMPLETE', price: '$399', projects: '1', tenants: 'Unlimited', marketplace: false },
|
|
12
|
+
{ name: 'PRO', price: '$799', projects: '5', tenants: 'Unlimited', marketplace: true },
|
|
13
|
+
{ name: 'ENTERPRISE', price: 'Custom', projects: 'Unlimited', tenants: 'Unlimited', marketplace: true },
|
|
14
|
+
];
|
|
15
|
+
function colorTier(tier) {
|
|
16
|
+
switch (tier.toUpperCase()) {
|
|
17
|
+
case 'STARTER': return chalk_1.default.green(tier);
|
|
18
|
+
case 'COMPLETE': return chalk_1.default.yellow(tier);
|
|
19
|
+
case 'PRO': return chalk_1.default.magenta(tier);
|
|
20
|
+
case 'ENTERPRISE': return chalk_1.default.cyan(tier);
|
|
21
|
+
default: return tier;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function printTierComparisonTable(userTier, requiredTier) {
|
|
25
|
+
console.log(chalk_1.default.red('\n✗ License tier insufficient\n'));
|
|
26
|
+
console.log(` Your tier: ${colorTier(userTier)}`);
|
|
27
|
+
console.log(` Required tier: ${colorTier(requiredTier)}\n`);
|
|
28
|
+
const table = new cli_table3_1.default({
|
|
29
|
+
head: ['Tier', 'Price', 'Projects', 'Tenants', 'Marketplace'],
|
|
30
|
+
style: { head: ['cyan'] },
|
|
31
|
+
});
|
|
32
|
+
for (const t of TIERS) {
|
|
33
|
+
const isUser = t.name === userTier.toUpperCase();
|
|
34
|
+
const isRequired = t.name === requiredTier.toUpperCase();
|
|
35
|
+
const marker = isRequired ? chalk_1.default.yellow(' ← required') : isUser ? chalk_1.default.dim(' ← you') : '';
|
|
36
|
+
table.push([
|
|
37
|
+
colorTier(t.name) + marker,
|
|
38
|
+
t.price,
|
|
39
|
+
t.projects,
|
|
40
|
+
t.tenants,
|
|
41
|
+
t.marketplace ? chalk_1.default.green('✓') : chalk_1.default.dim('✗'),
|
|
42
|
+
]);
|
|
43
|
+
}
|
|
44
|
+
console.log(table.toString());
|
|
45
|
+
console.log(chalk_1.default.dim('\n Upgrade at: https://kaven.dev/pricing\n'));
|
|
46
|
+
}
|