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.
- package/README.md +181 -207
- package/dist/EnvManager-NMS3NMIE.js +15 -0
- package/dist/MarketplaceClient-YCFH2VU4.js +1 -0
- package/dist/chunk-JHLQ46NG.js +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +248 -304
- package/dist/tier-table-DQMPQSI2.js +6 -0
- package/package.json +26 -11
- package/dist/EnvManager-GQMEZ6NV.js +0 -158
- package/dist/MarketplaceClient-IJGRQRC4.js +0 -7
- package/dist/chunk-3RG5ZIWI.js +0 -10
- package/dist/chunk-GHZX5OAA.js +0 -455
- package/dist/commands/aiox/index.js +0 -20
- package/dist/commands/auth/login.js +0 -122
- package/dist/commands/auth/logout.js +0 -23
- package/dist/commands/auth/whoami.js +0 -36
- package/dist/commands/cache/index.js +0 -43
- package/dist/commands/config/features.js +0 -161
- package/dist/commands/config/index.js +0 -95
- package/dist/commands/index.js +0 -2
- package/dist/commands/init/aiox-bootstrap.js +0 -83
- package/dist/commands/init/index.js +0 -210
- package/dist/commands/init-ci/index.js +0 -153
- package/dist/commands/license/index.js +0 -10
- package/dist/commands/license/status.js +0 -44
- package/dist/commands/license/tier-table.js +0 -46
- package/dist/commands/marketplace/browse.js +0 -186
- package/dist/commands/marketplace/install.js +0 -263
- package/dist/commands/marketplace/list.js +0 -122
- package/dist/commands/module/activate.js +0 -245
- package/dist/commands/module/add.js +0 -69
- package/dist/commands/module/doctor.js +0 -175
- package/dist/commands/module/list.js +0 -51
- package/dist/commands/module/publish.js +0 -258
- package/dist/commands/module/remove.js +0 -58
- package/dist/commands/telemetry/view.js +0 -27
- package/dist/commands/upgrade/check.js +0 -162
- package/dist/commands/upgrade/index.js +0 -185
- package/dist/core/AuthService.js +0 -222
- package/dist/core/CacheManager.js +0 -154
- package/dist/core/ConfigManager.js +0 -166
- package/dist/core/EnvManager.js +0 -196
- package/dist/core/ErrorRecovery.js +0 -192
- package/dist/core/LicenseService.js +0 -83
- package/dist/core/ManifestParser.js +0 -52
- package/dist/core/MarkerService.js +0 -62
- package/dist/core/ModuleDoctor.js +0 -451
- package/dist/core/ModuleInstaller.js +0 -169
- package/dist/core/ProjectInitializer.js +0 -183
- package/dist/core/RegistryResolver.js +0 -95
- package/dist/core/SchemaActivator.js +0 -278
- package/dist/core/ScriptRunner.js +0 -73
- package/dist/core/SignatureVerifier.js +0 -75
- package/dist/core/index.js +0 -2
- package/dist/infrastructure/Container.js +0 -37
- package/dist/infrastructure/MarketplaceClient.js +0 -425
- package/dist/infrastructure/TelemetryBuffer.js +0 -73
- package/dist/infrastructure/TransactionalFileSystem.js +0 -77
- package/dist/infrastructure/errors.js +0 -63
- package/dist/infrastructure/index.js +0 -2
- package/dist/lib/capabilities-catalog.js +0 -73
- package/dist/lib/module-registry.js +0 -47
- package/dist/lib/schema-modifier.js +0 -40
- package/dist/tier-table-LAL6PAVW.js +0 -52
- package/dist/types/auth.js +0 -2
- package/dist/types/manifest.js +0 -45
- package/dist/types/markers.js +0 -10
- package/dist/types/marketplace.js +0 -2
|
@@ -1,183 +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.ProjectInitializer = void 0;
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const child_process_1 = require("child_process");
|
|
10
|
-
const TEMPLATE_REPO = "https://github.com/kaven-co/kaven-template.git";
|
|
11
|
-
const KAVEN_SQUAD_REPO = "https://github.com/bychrisr/kaven-squad";
|
|
12
|
-
/** Run a shell command via spawn, returning exit code. */
|
|
13
|
-
function runCommand(cmd, args, cwd, onData) {
|
|
14
|
-
return new Promise((resolve, reject) => {
|
|
15
|
-
const proc = (0, child_process_1.spawn)(cmd, args, { cwd, stdio: onData ? "pipe" : "inherit" });
|
|
16
|
-
if (onData && proc.stdout) {
|
|
17
|
-
proc.stdout.on("data", (d) => onData(d.toString()));
|
|
18
|
-
}
|
|
19
|
-
if (onData && proc.stderr) {
|
|
20
|
-
proc.stderr.on("data", (d) => onData(d.toString()));
|
|
21
|
-
}
|
|
22
|
-
proc.on("error", reject);
|
|
23
|
-
proc.on("close", (code) => resolve(code ?? 0));
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
class ProjectInitializer {
|
|
27
|
-
/** Validate that the project name only contains alphanumerics and hyphens. */
|
|
28
|
-
validateName(name) {
|
|
29
|
-
if (!name || name.trim().length === 0) {
|
|
30
|
-
return { valid: false, reason: "Project name cannot be empty" };
|
|
31
|
-
}
|
|
32
|
-
if (/\s/.test(name)) {
|
|
33
|
-
return { valid: false, reason: "Project name cannot contain spaces" };
|
|
34
|
-
}
|
|
35
|
-
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
36
|
-
return {
|
|
37
|
-
valid: false,
|
|
38
|
-
reason: "Project name must only contain lowercase letters, numbers, and hyphens",
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
return { valid: true };
|
|
42
|
-
}
|
|
43
|
-
/** Clone the template (from Git or local path) into targetDir. */
|
|
44
|
-
async cloneTemplate(targetDir, templateSource) {
|
|
45
|
-
const source = templateSource || TEMPLATE_REPO;
|
|
46
|
-
console.log(`[INIT] Clone Source: ${source}`);
|
|
47
|
-
console.log(`[INIT] Target Dir: ${targetDir}`);
|
|
48
|
-
// If it's a local path that exists, copy it instead of cloning
|
|
49
|
-
if (await fs_extra_1.default.pathExists(source) && (source.startsWith("/") || source.startsWith("./") || source.startsWith("../"))) {
|
|
50
|
-
console.log(`[INIT] Local Path Detected. Copying...`);
|
|
51
|
-
await fs_extra_1.default.copy(source, targetDir, {
|
|
52
|
-
filter: (src) => !src.includes("node_modules") && !src.includes(".git") && !src.includes(".turbo")
|
|
53
|
-
});
|
|
54
|
-
console.log(`[INIT] Local Copy Done.`);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
const exitCode = await runCommand("git", ["clone", "--depth", "1", source, targetDir], process.cwd());
|
|
58
|
-
if (exitCode !== 0) {
|
|
59
|
-
throw new Error(`git clone failed with exit code ${exitCode}`);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
/** Remove the .git directory from the cloned project. */
|
|
63
|
-
async removeGitDir(targetDir) {
|
|
64
|
-
const gitDir = path_1.default.join(targetDir, ".git");
|
|
65
|
-
if (await fs_extra_1.default.pathExists(gitDir)) {
|
|
66
|
-
await fs_extra_1.default.remove(gitDir);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
/** Replace placeholders in key project files. */
|
|
70
|
-
async replacePlaceholders(targetDir, values) {
|
|
71
|
-
const replacements = {
|
|
72
|
-
"{{PROJECT_NAME}}": values.projectName,
|
|
73
|
-
"{{DATABASE_URL}}": values.dbUrl,
|
|
74
|
-
"{{DEFAULT_LOCALE}}": values.locale,
|
|
75
|
-
"{{DEFAULT_CURRENCY}}": values.currency,
|
|
76
|
-
};
|
|
77
|
-
const filesToProcess = [
|
|
78
|
-
"package.json",
|
|
79
|
-
".env.example",
|
|
80
|
-
"packages/database/prisma/schema.prisma",
|
|
81
|
-
"apps/api/package.json",
|
|
82
|
-
"apps/admin/package.json",
|
|
83
|
-
"apps/tenant/package.json",
|
|
84
|
-
"docs/architecture/tech-stack.md",
|
|
85
|
-
"docs/architecture/source-tree.md",
|
|
86
|
-
"docs/architecture/coding-standards.md",
|
|
87
|
-
];
|
|
88
|
-
for (const relFile of filesToProcess) {
|
|
89
|
-
const filePath = path_1.default.join(targetDir, relFile);
|
|
90
|
-
if (!(await fs_extra_1.default.pathExists(filePath)))
|
|
91
|
-
continue;
|
|
92
|
-
let content = await fs_extra_1.default.readFile(filePath, "utf-8");
|
|
93
|
-
for (const [placeholder, value] of Object.entries(replacements)) {
|
|
94
|
-
content = content.split(placeholder).join(value);
|
|
95
|
-
}
|
|
96
|
-
await fs_extra_1.default.writeFile(filePath, content, "utf-8");
|
|
97
|
-
}
|
|
98
|
-
// Safety net: directly update root package.json name field regardless of placeholder
|
|
99
|
-
const pkgPath = path_1.default.join(targetDir, "package.json");
|
|
100
|
-
if (await fs_extra_1.default.pathExists(pkgPath)) {
|
|
101
|
-
const pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
102
|
-
if (pkg.name !== values.projectName) {
|
|
103
|
-
pkg.name = values.projectName;
|
|
104
|
-
await fs_extra_1.default.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
/** Run pnpm install in the target directory. */
|
|
109
|
-
async runInstall(targetDir) {
|
|
110
|
-
const exitCode = await runCommand("pnpm", ["install"], targetDir);
|
|
111
|
-
if (exitCode !== 0) {
|
|
112
|
-
throw new Error(`pnpm install failed with exit code ${exitCode}`);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
/** Initialize git and create an initial commit. */
|
|
116
|
-
async initGit(targetDir) {
|
|
117
|
-
await runCommand("git", ["init"], targetDir);
|
|
118
|
-
await runCommand("git", ["add", "."], targetDir);
|
|
119
|
-
await runCommand("git", ["commit", "-m", "chore: initial kaven setup"], targetDir);
|
|
120
|
-
}
|
|
121
|
-
/**
|
|
122
|
-
* Clone kaven-squad into squads/kaven-squad/ inside the project.
|
|
123
|
-
* Returns { installed: true } on success, { installed: false, reason } on failure.
|
|
124
|
-
* Never throws — squad installation is non-fatal.
|
|
125
|
-
*/
|
|
126
|
-
async installSquad(targetDir) {
|
|
127
|
-
const squadsDir = path_1.default.join(targetDir, "squads");
|
|
128
|
-
const squadDir = path_1.default.join(squadsDir, "kaven-squad");
|
|
129
|
-
// Squad already present — skip
|
|
130
|
-
if (await fs_extra_1.default.pathExists(squadDir)) {
|
|
131
|
-
return { installed: false, reason: "already-exists" };
|
|
132
|
-
}
|
|
133
|
-
await fs_extra_1.default.ensureDir(squadsDir);
|
|
134
|
-
const exitCode = await runCommand("git", ["clone", "--depth", "1", KAVEN_SQUAD_REPO, squadDir], process.cwd());
|
|
135
|
-
if (exitCode !== 0) {
|
|
136
|
-
return {
|
|
137
|
-
installed: false,
|
|
138
|
-
reason: `git clone exited with code ${exitCode}`,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
// Remove .git — squad history not needed in user project
|
|
142
|
-
const squadGitDir = path_1.default.join(squadDir, ".git");
|
|
143
|
-
if (await fs_extra_1.default.pathExists(squadGitDir)) {
|
|
144
|
-
await fs_extra_1.default.remove(squadGitDir);
|
|
145
|
-
}
|
|
146
|
-
return { installed: true };
|
|
147
|
-
}
|
|
148
|
-
/**
|
|
149
|
-
* Install AIOX Core runtime into the project via npx.
|
|
150
|
-
* Non-fatal — if it fails, user gets instructions to run manually.
|
|
151
|
-
*/
|
|
152
|
-
async installAIOXCore(targetDir) {
|
|
153
|
-
const exitCode = await runCommand('npx', ['aiox-core@5.0.3', 'install', '--quiet'], targetDir);
|
|
154
|
-
if (exitCode !== 0) {
|
|
155
|
-
return {
|
|
156
|
-
installed: false,
|
|
157
|
-
reason: `npx aiox-core exited with code ${exitCode}`,
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
return { installed: true };
|
|
161
|
-
}
|
|
162
|
-
/** Health check after project initialization. */
|
|
163
|
-
async healthCheck(targetDir) {
|
|
164
|
-
const issues = [];
|
|
165
|
-
// Check key files exist
|
|
166
|
-
const requiredFiles = ["package.json", ".env.example", "packages/database/prisma/schema.prisma"];
|
|
167
|
-
for (const file of requiredFiles) {
|
|
168
|
-
if (!(await fs_extra_1.default.pathExists(path_1.default.join(targetDir, file)))) {
|
|
169
|
-
issues.push(`Missing required file: ${file}`);
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
// Check node_modules exists (if install was run)
|
|
173
|
-
const hasNodeModules = await fs_extra_1.default.pathExists(path_1.default.join(targetDir, "node_modules"));
|
|
174
|
-
if (!hasNodeModules) {
|
|
175
|
-
issues.push("Dependencies not installed. Run: pnpm install");
|
|
176
|
-
}
|
|
177
|
-
return {
|
|
178
|
-
healthy: issues.length === 0,
|
|
179
|
-
issues,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
exports.ProjectInitializer = ProjectInitializer;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RegistryResolver = void 0;
|
|
4
|
-
const ConfigManager_1 = require("./ConfigManager");
|
|
5
|
-
const MarketplaceClient_1 = require("../infrastructure/MarketplaceClient");
|
|
6
|
-
const AuthService_1 = require("./AuthService");
|
|
7
|
-
/**
|
|
8
|
-
* C2.5: Registry resolver — handles both official and custom registries
|
|
9
|
-
*/
|
|
10
|
-
class RegistryResolver {
|
|
11
|
-
authService;
|
|
12
|
-
constructor(authService) {
|
|
13
|
-
this.authService = authService || new AuthService_1.AuthService();
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Get the active registry URL (custom or default)
|
|
17
|
-
*/
|
|
18
|
-
async getActiveRegistry() {
|
|
19
|
-
await ConfigManager_1.configManager.initialize();
|
|
20
|
-
return ConfigManager_1.configManager.getRegistry();
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Get marketplace client for active registry
|
|
24
|
-
*/
|
|
25
|
-
async getMarketplaceClient() {
|
|
26
|
-
const registry = await this.getActiveRegistry();
|
|
27
|
-
const client = new MarketplaceClient_1.MarketplaceClient(this.authService);
|
|
28
|
-
// Set custom registry if configured
|
|
29
|
-
if (registry !== "https://marketplace.kaven.site") {
|
|
30
|
-
client.baseUrl = registry;
|
|
31
|
-
}
|
|
32
|
-
return client;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Validate registry URL is accessible
|
|
36
|
-
*/
|
|
37
|
-
async validateRegistry(url) {
|
|
38
|
-
try {
|
|
39
|
-
const response = await fetch(`${url}/health`);
|
|
40
|
-
if (!response.ok) {
|
|
41
|
-
return {
|
|
42
|
-
valid: false,
|
|
43
|
-
error: `Registry returned ${response.status} ${response.statusText}`,
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
return { valid: true };
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
return {
|
|
50
|
-
valid: false,
|
|
51
|
-
error: `Failed to connect: ${error instanceof Error ? error.message : String(error)}`,
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
/**
|
|
56
|
-
* Set custom registry
|
|
57
|
-
*/
|
|
58
|
-
async setCustomRegistry(url) {
|
|
59
|
-
// Validate URL format
|
|
60
|
-
try {
|
|
61
|
-
new URL(url);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
throw new Error(`Invalid URL format: ${url}`);
|
|
65
|
-
}
|
|
66
|
-
// Validate registry is accessible
|
|
67
|
-
const validation = await this.validateRegistry(url);
|
|
68
|
-
if (!validation.valid) {
|
|
69
|
-
throw new Error(`Registry validation failed: ${validation.error}`);
|
|
70
|
-
}
|
|
71
|
-
// Save to config
|
|
72
|
-
await ConfigManager_1.configManager.initialize();
|
|
73
|
-
await ConfigManager_1.configManager.set("customRegistry", url);
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Reset to default registry
|
|
77
|
-
*/
|
|
78
|
-
async resetToDefaultRegistry() {
|
|
79
|
-
await ConfigManager_1.configManager.initialize();
|
|
80
|
-
await ConfigManager_1.configManager.set("customRegistry", undefined);
|
|
81
|
-
}
|
|
82
|
-
/**
|
|
83
|
-
* List all available registries (default + custom)
|
|
84
|
-
*/
|
|
85
|
-
async listRegistries() {
|
|
86
|
-
await ConfigManager_1.configManager.initialize();
|
|
87
|
-
const config = ConfigManager_1.configManager.getAll();
|
|
88
|
-
return {
|
|
89
|
-
default: config.registry || "https://marketplace.kaven.site",
|
|
90
|
-
custom: config.customRegistry,
|
|
91
|
-
active: await this.getActiveRegistry(),
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
exports.RegistryResolver = RegistryResolver;
|
|
@@ -1,278 +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.SchemaActivator = exports.KAVEN_MODULES = void 0;
|
|
7
|
-
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
8
|
-
const path_1 = __importDefault(require("path"));
|
|
9
|
-
exports.KAVEN_MODULES = [
|
|
10
|
-
{
|
|
11
|
-
id: "auth",
|
|
12
|
-
label: "Auth & Identity",
|
|
13
|
-
description: "Gestão de usuários, permissões e sessões",
|
|
14
|
-
models: ["User", "Role", "Capability", "AuthSession", "AuditLog"],
|
|
15
|
-
enums: ["UserRole"],
|
|
16
|
-
dependsOn: [],
|
|
17
|
-
},
|
|
18
|
-
{
|
|
19
|
-
id: "billing",
|
|
20
|
-
label: "Billing",
|
|
21
|
-
description: "Faturamento, assinaturas e pagamentos",
|
|
22
|
-
models: ["Invoice", "Order", "Subscription", "Plan", "Payment", "Product"],
|
|
23
|
-
enums: [],
|
|
24
|
-
dependsOn: [],
|
|
25
|
-
},
|
|
26
|
-
{
|
|
27
|
-
id: "projects",
|
|
28
|
-
label: "Projects",
|
|
29
|
-
description: "Gestão de projetos e tasks",
|
|
30
|
-
models: ["Project", "Task"],
|
|
31
|
-
enums: ["ProjectStatus", "TaskStatus", "TaskPriority"],
|
|
32
|
-
dependsOn: [],
|
|
33
|
-
},
|
|
34
|
-
{
|
|
35
|
-
id: "notifications",
|
|
36
|
-
label: "Notifications",
|
|
37
|
-
description: "Notificações e preferências de usuário",
|
|
38
|
-
models: ["Notification", "UserPreference"],
|
|
39
|
-
enums: [],
|
|
40
|
-
dependsOn: [],
|
|
41
|
-
},
|
|
42
|
-
{
|
|
43
|
-
id: "marketing-tracking",
|
|
44
|
-
label: "Marketing Tracking",
|
|
45
|
-
description: "Observabilidade de anúncios, GTM, GA4 e Meta CAPI",
|
|
46
|
-
models: ["TrackingEvent"],
|
|
47
|
-
enums: ["TrackingSource"],
|
|
48
|
-
dependsOn: [],
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
id: "service-tokens",
|
|
52
|
-
label: "Service Tokens",
|
|
53
|
-
description: "Agent authentication tokens for AIOX integration",
|
|
54
|
-
models: ["ServiceToken"],
|
|
55
|
-
enums: [],
|
|
56
|
-
dependsOn: [],
|
|
57
|
-
},
|
|
58
|
-
];
|
|
59
|
-
// ============================================================
|
|
60
|
-
// Marcadores de seção no schema
|
|
61
|
-
// ============================================================
|
|
62
|
-
const BEGIN_MARKER = (moduleId) => `// [KAVEN_MODULE:${moduleId.toUpperCase()} BEGIN]`;
|
|
63
|
-
const END_MARKER = (moduleId) => `// [KAVEN_MODULE:${moduleId.toUpperCase()} END]`;
|
|
64
|
-
// ============================================================
|
|
65
|
-
// SchemaActivator — lê/escreve schema.extended.prisma
|
|
66
|
-
// ============================================================
|
|
67
|
-
class SchemaActivator {
|
|
68
|
-
schemaPath;
|
|
69
|
-
constructor(projectRoot) {
|
|
70
|
-
this.schemaPath = path_1.default.join(projectRoot, "packages", "database", "prisma", "schema.extended.prisma");
|
|
71
|
-
}
|
|
72
|
-
/** Verifica se o schema existe no projeto */
|
|
73
|
-
async exists() {
|
|
74
|
-
return fs_extra_1.default.pathExists(this.schemaPath);
|
|
75
|
-
}
|
|
76
|
-
/** Caminho absoluto do schema */
|
|
77
|
-
get path() {
|
|
78
|
-
return this.schemaPath;
|
|
79
|
-
}
|
|
80
|
-
/** Lê o conteúdo atual do schema */
|
|
81
|
-
async readSchema() {
|
|
82
|
-
return fs_extra_1.default.readFile(this.schemaPath, "utf-8");
|
|
83
|
-
}
|
|
84
|
-
/** Persiste o conteúdo no schema */
|
|
85
|
-
async writeSchema(content) {
|
|
86
|
-
await fs_extra_1.default.writeFile(this.schemaPath, content, "utf-8");
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Detecta se um módulo está ativo no schema.
|
|
90
|
-
*
|
|
91
|
-
* Estratégia:
|
|
92
|
-
* 1. Se existirem marcadores BEGIN/END: verifica se o conteúdo dentro
|
|
93
|
-
* dos marcadores NÃO está completamente comentado.
|
|
94
|
-
* 2. Se não houver marcadores: verifica se algum dos models principais
|
|
95
|
-
* do módulo está presente e não comentado no arquivo.
|
|
96
|
-
*/
|
|
97
|
-
async getModuleStatus(def) {
|
|
98
|
-
const content = await this.readSchema();
|
|
99
|
-
const begin = BEGIN_MARKER(def.id);
|
|
100
|
-
const end = END_MARKER(def.id);
|
|
101
|
-
const hasMarkers = content.includes(begin) && content.includes(end);
|
|
102
|
-
let active = false;
|
|
103
|
-
if (hasMarkers) {
|
|
104
|
-
const block = this.extractBlock(content, def.id);
|
|
105
|
-
active = block !== null && this.isBlockActive(block);
|
|
106
|
-
}
|
|
107
|
-
else {
|
|
108
|
-
// Sem marcadores: verifica presença de pelo menos um model descomentado
|
|
109
|
-
active = def.models.some((modelName) => this.isModelActive(content, modelName));
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
id: def.id,
|
|
113
|
-
label: def.label,
|
|
114
|
-
description: def.description,
|
|
115
|
-
models: def.models,
|
|
116
|
-
dependsOn: def.dependsOn,
|
|
117
|
-
active,
|
|
118
|
-
hasMarkers,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
/** Ativa um módulo: se tem marcadores, descomenta o bloco; senão, injeta o bloco */
|
|
122
|
-
async activateModule(def) {
|
|
123
|
-
const content = await this.readSchema();
|
|
124
|
-
const begin = BEGIN_MARKER(def.id);
|
|
125
|
-
const end = END_MARKER(def.id);
|
|
126
|
-
if (content.includes(begin) && content.includes(end)) {
|
|
127
|
-
const updated = this.uncommentBlock(content, def.id);
|
|
128
|
-
await this.writeSchema(updated);
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
// Módulo não tem seção marcada — sem template para injetar
|
|
132
|
-
throw new Error(`O módulo "${def.id}" não possui uma seção marcada (BEGIN/END) no schema.\n` +
|
|
133
|
-
`Adicione o bloco do módulo manualmente com os marcadores:\n` +
|
|
134
|
-
` ${begin}\n` +
|
|
135
|
-
` ... models do módulo ...\n` +
|
|
136
|
-
` ${end}`);
|
|
137
|
-
}
|
|
138
|
-
/** Desativa um módulo: comenta todos os models do bloco */
|
|
139
|
-
async deactivateModule(def) {
|
|
140
|
-
const content = await this.readSchema();
|
|
141
|
-
const begin = BEGIN_MARKER(def.id);
|
|
142
|
-
const end = END_MARKER(def.id);
|
|
143
|
-
if (!content.includes(begin) || !content.includes(end)) {
|
|
144
|
-
throw new Error(`O módulo "${def.id}" não possui marcadores BEGIN/END no schema. ` +
|
|
145
|
-
`Não é possível desativar automaticamente.`);
|
|
146
|
-
}
|
|
147
|
-
const updated = this.commentBlock(content, def.id);
|
|
148
|
-
await this.writeSchema(updated);
|
|
149
|
-
}
|
|
150
|
-
// ──────────────────────────────────────────────
|
|
151
|
-
// Helpers de manipulação de blocos
|
|
152
|
-
// ──────────────────────────────────────────────
|
|
153
|
-
/**
|
|
154
|
-
* Extrai o conteúdo entre os marcadores BEGIN e END (exclusive).
|
|
155
|
-
* Retorna null se os marcadores não forem encontrados.
|
|
156
|
-
*/
|
|
157
|
-
extractBlock(content, moduleId) {
|
|
158
|
-
const begin = BEGIN_MARKER(moduleId);
|
|
159
|
-
const end = END_MARKER(moduleId);
|
|
160
|
-
const lines = content.split("\n");
|
|
161
|
-
let beginIdx = -1;
|
|
162
|
-
let endIdx = -1;
|
|
163
|
-
for (let i = 0; i < lines.length; i++) {
|
|
164
|
-
if (lines[i].includes(begin))
|
|
165
|
-
beginIdx = i;
|
|
166
|
-
if (lines[i].includes(end) && beginIdx !== -1) {
|
|
167
|
-
endIdx = i;
|
|
168
|
-
break;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
if (beginIdx === -1 || endIdx === -1)
|
|
172
|
-
return null;
|
|
173
|
-
return lines.slice(beginIdx + 1, endIdx).join("\n");
|
|
174
|
-
}
|
|
175
|
-
/**
|
|
176
|
-
* Verifica se um bloco tem ao menos uma linha não-comentada relevante
|
|
177
|
-
* (ignora linhas vazias e comentários simples).
|
|
178
|
-
*/
|
|
179
|
-
isBlockActive(block) {
|
|
180
|
-
return block.split("\n").some((line) => {
|
|
181
|
-
const trimmed = line.trim();
|
|
182
|
-
return (trimmed.length > 0 &&
|
|
183
|
-
!trimmed.startsWith("//") &&
|
|
184
|
-
!trimmed.startsWith("/*") &&
|
|
185
|
-
!trimmed.startsWith("*"));
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Verifica se um model específico está ativo (não comentado) no schema.
|
|
190
|
-
* Procura pela linha `model ModelName {` sem `//` antes.
|
|
191
|
-
*/
|
|
192
|
-
isModelActive(content, modelName) {
|
|
193
|
-
const lines = content.split("\n");
|
|
194
|
-
for (const line of lines) {
|
|
195
|
-
const trimmed = line.trim();
|
|
196
|
-
if (trimmed.startsWith(`model ${modelName}`) &&
|
|
197
|
-
!line.trimStart().startsWith("//")) {
|
|
198
|
-
return true;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* Comenta todas as linhas não-comentadas dentro do bloco BEGIN/END.
|
|
205
|
-
*/
|
|
206
|
-
commentBlock(content, moduleId) {
|
|
207
|
-
const begin = BEGIN_MARKER(moduleId);
|
|
208
|
-
const end = END_MARKER(moduleId);
|
|
209
|
-
const lines = content.split("\n");
|
|
210
|
-
let inBlock = false;
|
|
211
|
-
const result = [];
|
|
212
|
-
for (const line of lines) {
|
|
213
|
-
if (line.includes(begin)) {
|
|
214
|
-
inBlock = true;
|
|
215
|
-
result.push(line);
|
|
216
|
-
continue;
|
|
217
|
-
}
|
|
218
|
-
if (line.includes(end)) {
|
|
219
|
-
inBlock = false;
|
|
220
|
-
result.push(line);
|
|
221
|
-
continue;
|
|
222
|
-
}
|
|
223
|
-
if (inBlock) {
|
|
224
|
-
const trimmed = line.trim();
|
|
225
|
-
if (trimmed.length === 0) {
|
|
226
|
-
result.push(line);
|
|
227
|
-
}
|
|
228
|
-
else if (trimmed.startsWith("//")) {
|
|
229
|
-
result.push(line); // já comentado
|
|
230
|
-
}
|
|
231
|
-
else {
|
|
232
|
-
result.push(`// ${line}`);
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
else {
|
|
236
|
-
result.push(line);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
return result.join("\n");
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Remove `// ` do início das linhas dentro do bloco BEGIN/END.
|
|
243
|
-
*/
|
|
244
|
-
uncommentBlock(content, moduleId) {
|
|
245
|
-
const begin = BEGIN_MARKER(moduleId);
|
|
246
|
-
const end = END_MARKER(moduleId);
|
|
247
|
-
const lines = content.split("\n");
|
|
248
|
-
let inBlock = false;
|
|
249
|
-
const result = [];
|
|
250
|
-
for (const line of lines) {
|
|
251
|
-
if (line.includes(begin)) {
|
|
252
|
-
inBlock = true;
|
|
253
|
-
result.push(line);
|
|
254
|
-
continue;
|
|
255
|
-
}
|
|
256
|
-
if (line.includes(end)) {
|
|
257
|
-
inBlock = false;
|
|
258
|
-
result.push(line);
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
if (inBlock) {
|
|
262
|
-
// Remove exatamente um nível de comentário preservando identação
|
|
263
|
-
const match = line.match(/^(\s*)\/\/\s?(.*)$/);
|
|
264
|
-
if (match) {
|
|
265
|
-
result.push(match[1] + match[2]);
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
result.push(line);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
else {
|
|
272
|
-
result.push(line);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
return result.join("\n");
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
exports.SchemaActivator = SchemaActivator;
|
|
@@ -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.ScriptRunner = void 0;
|
|
7
|
-
const child_process_1 = require("child_process");
|
|
8
|
-
const readline_1 = __importDefault(require("readline"));
|
|
9
|
-
const chalk_1 = __importDefault(require("chalk"));
|
|
10
|
-
class ScriptRunner {
|
|
11
|
-
timeoutMs;
|
|
12
|
-
constructor(timeoutMs = 60_000) {
|
|
13
|
-
this.timeoutMs = timeoutMs;
|
|
14
|
-
}
|
|
15
|
-
async runScript(script, label, skipConfirmation = false) {
|
|
16
|
-
if (!skipConfirmation) {
|
|
17
|
-
const confirmed = await this.confirm(`Run ${label} script: ${script.command} ${(script.args ?? []).join(' ')}?`);
|
|
18
|
-
if (!confirmed) {
|
|
19
|
-
console.log(chalk_1.default.dim(` Skipping ${label} script.`));
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return new Promise((resolve, reject) => {
|
|
24
|
-
const child = (0, child_process_1.spawn)(script.command, script.args ?? [], {
|
|
25
|
-
cwd: script.cwd,
|
|
26
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
27
|
-
shell: true,
|
|
28
|
-
});
|
|
29
|
-
const prefix = chalk_1.default.dim(`[${label}] `);
|
|
30
|
-
child.stdout?.on('data', (data) => {
|
|
31
|
-
process.stdout.write(prefix + data.toString());
|
|
32
|
-
});
|
|
33
|
-
child.stderr?.on('data', (data) => {
|
|
34
|
-
process.stderr.write(prefix + chalk_1.default.yellow(data.toString()));
|
|
35
|
-
});
|
|
36
|
-
const timer = setTimeout(() => {
|
|
37
|
-
console.warn(chalk_1.default.yellow(`\n ⚠ ${label} script timed out after ${this.timeoutMs / 1000}s, sending SIGTERM...`));
|
|
38
|
-
child.kill('SIGTERM');
|
|
39
|
-
setTimeout(() => {
|
|
40
|
-
child.kill('SIGKILL');
|
|
41
|
-
}, 5_000);
|
|
42
|
-
}, this.timeoutMs);
|
|
43
|
-
child.on('close', (code) => {
|
|
44
|
-
clearTimeout(timer);
|
|
45
|
-
if (code === 0 || code === null) {
|
|
46
|
-
resolve();
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
reject(new Error(`${label} script exited with code ${code}`));
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
child.on('error', (err) => {
|
|
53
|
-
clearTimeout(timer);
|
|
54
|
-
reject(err);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
}
|
|
58
|
-
async runScripts(scripts, label, skipConfirmation = false) {
|
|
59
|
-
for (const script of scripts) {
|
|
60
|
-
await this.runScript(script, label, skipConfirmation);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
confirm(message) {
|
|
64
|
-
return new Promise((resolve) => {
|
|
65
|
-
const rl = readline_1.default.createInterface({ input: process.stdin, output: process.stdout });
|
|
66
|
-
rl.question(`\n ${message} [y/N] `, (answer) => {
|
|
67
|
-
rl.close();
|
|
68
|
-
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
exports.ScriptRunner = ScriptRunner;
|