@kosdev-code/kos-ui-cli 2.0.45 → 2.0.46

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.
@@ -1,437 +0,0 @@
1
- // generators/dev/index.mjs
2
- import chalk from "chalk";
3
- import { spawn } from "child_process";
4
- import { existsSync, mkdirSync, createWriteStream } from "fs";
5
- import http from "http";
6
- import path from "path";
7
- import {
8
- getAllHosts,
9
- getHost,
10
- filterPlugins,
11
- checkProjectExists,
12
- validateDevConfig,
13
- } from "../../utils/dev-config.mjs";
14
-
15
- export const metadata = {
16
- key: "dev",
17
- name: "Development Server",
18
- description: "Start development servers for host applications and plugins",
19
- namedArguments: {
20
- host: "host",
21
- plugins: "plugins",
22
- disable: "disable",
23
- pluginsOnly: "pluginsOnly",
24
- interactive: "interactive",
25
- },
26
- };
27
-
28
- // Process management
29
- const processes = [];
30
- const verbose = process.env.VERBOSE_PLUGIN_DEV === "true";
31
-
32
- function cleanup() {
33
- console.log(chalk.yellow("\nCleaning up..."));
34
- processes.forEach((proc) => {
35
- if (proc && !proc.killed) {
36
- if (verbose) {
37
- console.log(chalk.cyan(` Stopping process ${proc.pid}`));
38
- }
39
- proc.kill();
40
- }
41
- });
42
- console.log(chalk.green("Cleanup complete"));
43
- process.exit(0);
44
- }
45
-
46
- // Register cleanup handlers
47
- process.on("SIGINT", cleanup);
48
- process.on("SIGTERM", cleanup);
49
-
50
- /**
51
- * Check if a port is healthy
52
- */
53
- async function checkPortHealth(port, endpoint = "/api/kos/ui/plugins/contexts") {
54
- return new Promise((resolve) => {
55
- const req = http.get(`http://localhost:${port}${endpoint}`, (res) => {
56
- resolve(res.statusCode === 200);
57
- });
58
- req.on("error", () => resolve(false));
59
- req.setTimeout(1000, () => {
60
- req.destroy();
61
- resolve(false);
62
- });
63
- });
64
- }
65
-
66
- /**
67
- * Wait for server to be ready
68
- */
69
- async function waitForServer(port, name, maxWaitSeconds = 120, endpoint = "/api/kos/ui/plugins/contexts") {
70
- console.log(chalk.yellow(`Waiting for ${name} to be ready...`));
71
-
72
- const startTime = Date.now();
73
- const maxWaitMs = maxWaitSeconds * 1000;
74
-
75
- while (Date.now() - startTime < maxWaitMs) {
76
- const isHealthy = await checkPortHealth(port, endpoint);
77
- if (isHealthy) {
78
- const elapsed = Math.floor((Date.now() - startTime) / 1000);
79
- console.log(chalk.green(`✓ ${name} ready on port ${port}! (${elapsed}s)`));
80
- return true;
81
- }
82
-
83
- const elapsed = Math.floor((Date.now() - startTime) / 1000);
84
- if (elapsed > 0 && elapsed % 5 === 0) {
85
- console.log(` ... still waiting (${elapsed}s)`);
86
- }
87
-
88
- await new Promise((resolve) => setTimeout(resolve, 1000));
89
- }
90
-
91
- console.log(chalk.red(`✗ Timeout waiting for ${name}`));
92
- return false;
93
- }
94
-
95
- /**
96
- * Start a dev server
97
- */
98
- function startDevServer(project, port, logFile, env = {}) {
99
- console.log(chalk.blue(`Starting ${project} on port ${port}`));
100
-
101
- const logStream = createWriteStream(logFile, { flags: "w" });
102
-
103
- const proc = spawn("npx", ["nx", "run", `${project}:serve`], {
104
- env: {
105
- ...process.env,
106
- ...env,
107
- VERBOSE_PLUGIN_DEV: verbose ? "true" : "false",
108
- },
109
- stdio: ["ignore", "pipe", "pipe"],
110
- });
111
-
112
- proc.stdout.pipe(logStream);
113
- proc.stderr.pipe(logStream);
114
-
115
- proc.on("error", (err) => {
116
- console.log(chalk.red(`Failed to start ${project}: ${err.message}`));
117
- });
118
-
119
- proc.on("exit", (code) => {
120
- if (code !== 0 && code !== null) {
121
- console.log(chalk.red(`${project} exited with code ${code}`));
122
- }
123
- });
124
-
125
- processes.push(proc);
126
-
127
- console.log(chalk.green(` ✓ PID: ${proc.pid}, Log: ${logFile}`));
128
-
129
- return proc;
130
- }
131
-
132
- /**
133
- * Start internal development mode (host + plugins)
134
- */
135
- async function startInternalDevelopment(hostConfig, plugins, logDir) {
136
- console.log(chalk.green("=================================================================="));
137
- console.log(chalk.green("Internal Development Mode"));
138
- console.log(chalk.green(`Host: ${hostConfig.name}`));
139
- console.log(chalk.green(`Plugins: ${plugins.length}`));
140
- console.log(chalk.green("=================================================================="));
141
- console.log("");
142
-
143
- // Create log directory
144
- if (!existsSync(logDir)) {
145
- mkdirSync(logDir, { recursive: true });
146
- }
147
-
148
- // Start plugin dev servers
149
- console.log(chalk.yellow("Starting plugin dev servers..."));
150
- for (const plugin of plugins) {
151
- const logFile = path.join(logDir, `${plugin.project}.log`);
152
- startDevServer(plugin.project, plugin.port, logFile);
153
- }
154
-
155
- console.log("");
156
- console.log(chalk.yellow("Waiting for plugin dev servers to start..."));
157
- await new Promise((resolve) => setTimeout(resolve, 8000));
158
-
159
- // Verify plugin servers
160
- console.log(chalk.yellow("Verifying plugin dev servers..."));
161
- let allReady = true;
162
-
163
- for (const plugin of plugins) {
164
- const pluginEndpoint = plugin.healthCheckEndpoint || "/api/kos/ui/plugins/contexts";
165
- const isReady = await checkPortHealth(plugin.port, pluginEndpoint);
166
- if (isReady) {
167
- console.log(chalk.green(` ✓ ${plugin.project} ready on port ${plugin.port}`));
168
- } else {
169
- console.log(chalk.red(` ✗ ${plugin.project} NOT ready on port ${plugin.port}`));
170
- allReady = false;
171
- }
172
- }
173
-
174
- if (!allReady) {
175
- console.log(chalk.red(`\nSome plugin servers failed to start. Check logs in ${logDir}/`));
176
- process.exit(1);
177
- }
178
-
179
- // Build environment variables
180
- console.log("");
181
- console.log(chalk.yellow("Configuring host app environment..."));
182
-
183
- const hostEnv = {
184
- USE_LOCAL_PLUGINS: "true",
185
- };
186
-
187
- // Add plugin-specific env vars
188
- for (const plugin of plugins) {
189
- const envVar = plugin.project.toUpperCase().replace(/-/g, "_") + "_DEV";
190
- hostEnv[envVar] = "true";
191
- console.log(chalk.green(` ✓ ${envVar}=true`));
192
- }
193
-
194
- // Start host app
195
- console.log("");
196
- console.log(chalk.yellow(`Starting host app (${hostConfig.project})...`));
197
- const hostLogFile = path.join(logDir, `${hostConfig.project}.log`);
198
- startDevServer(hostConfig.project, hostConfig.port, hostLogFile, hostEnv);
199
- console.log("");
200
-
201
- // Wait for host app
202
- const hostEndpoint = hostConfig.healthCheckEndpoint || "/api/kos/ui/plugins/contexts";
203
- const hostReady = await waitForServer(hostConfig.port, hostConfig.project, 120, hostEndpoint);
204
-
205
- if (!hostReady) {
206
- console.log(chalk.red("Host app failed to start"));
207
- process.exit(1);
208
- }
209
-
210
- // Print summary
211
- printSummary(hostConfig, plugins, logDir);
212
- }
213
-
214
- /**
215
- * Start third-party development mode (plugins only)
216
- */
217
- async function startThirdPartyDevelopment(hostConfig, plugins, logDir) {
218
- console.log(chalk.green("=================================================================="));
219
- console.log(chalk.green("Third-Party Development Mode"));
220
- console.log(chalk.green(`Plugin Context: ${hostConfig.pluginContext}`));
221
- console.log(chalk.green(`Plugins: ${plugins.length}`));
222
- console.log(chalk.green("=================================================================="));
223
- console.log("");
224
-
225
- // Create log directory
226
- if (!existsSync(logDir)) {
227
- mkdirSync(logDir, { recursive: true });
228
- }
229
-
230
- // Start plugin dev servers only
231
- console.log(chalk.yellow("Starting plugin dev servers..."));
232
- for (const plugin of plugins) {
233
- const logFile = path.join(logDir, `${plugin.project}.log`);
234
- startDevServer(plugin.project, plugin.port, logFile);
235
- }
236
-
237
- console.log("");
238
- console.log(chalk.yellow("Waiting for plugin dev servers to start..."));
239
- await new Promise((resolve) => setTimeout(resolve, 8000));
240
-
241
- // Verify plugin servers
242
- console.log(chalk.yellow("Verifying plugin dev servers..."));
243
- let allReady = true;
244
-
245
- for (const plugin of plugins) {
246
- const pluginEndpoint = plugin.healthCheckEndpoint || "/api/kos/ui/plugins/contexts";
247
- const isReady = await checkPortHealth(plugin.port, pluginEndpoint);
248
- if (isReady) {
249
- console.log(chalk.green(` ✓ ${plugin.project} ready on port ${plugin.port}`));
250
- } else {
251
- console.log(chalk.red(` ✗ ${plugin.project} NOT ready on port ${plugin.port}`));
252
- allReady = false;
253
- }
254
- }
255
-
256
- if (!allReady) {
257
- console.log(chalk.red(`\nSome plugin servers failed to start. Check logs in ${logDir}/`));
258
- process.exit(1);
259
- }
260
-
261
- // Print third-party instructions
262
- printThirdPartyInstructions(plugins, hostConfig, logDir);
263
- }
264
-
265
- /**
266
- * Print summary for internal development
267
- */
268
- function printSummary(hostConfig, plugins, logDir) {
269
- console.log("");
270
- console.log(chalk.green("=================================================================="));
271
- console.log(chalk.green("Development Environment Ready!"));
272
- console.log(chalk.green("=================================================================="));
273
- console.log("");
274
- console.log(chalk.blue("Plugin Dev Servers:"));
275
-
276
- for (const plugin of plugins) {
277
- console.log(` - ${plugin.project}: http://localhost:${plugin.port}`);
278
- }
279
-
280
- console.log("");
281
- console.log(chalk.blue("Host App:"));
282
- console.log(` - ${hostConfig.project}: http://localhost:${hostConfig.port}`);
283
- console.log("");
284
- console.log(chalk.blue("Logs:"));
285
- console.log(` - ${logDir}/`);
286
- console.log("");
287
- console.log(chalk.yellow("Press Ctrl+C to stop all servers"));
288
- console.log("");
289
- }
290
-
291
- /**
292
- * Print third-party connection instructions
293
- */
294
- function printThirdPartyInstructions(plugins, hostConfig, logDir) {
295
- console.log("");
296
- console.log(chalk.green("=================================================================="));
297
- console.log(chalk.green("Plugin Dev Servers Running"));
298
- console.log(chalk.green("=================================================================="));
299
- console.log("");
300
- console.log(chalk.blue("Plugin Dev Servers:"));
301
-
302
- for (const plugin of plugins) {
303
- console.log(` - ${plugin.project}: http://localhost:${plugin.port}`);
304
- }
305
-
306
- console.log("");
307
- console.log(chalk.yellow("Connection Instructions:"));
308
- console.log("");
309
- console.log(chalk.cyan("Connect to your deployed device using query parameters:"));
310
- console.log("");
311
-
312
- const yourIp = "<your-development-machine-ip>";
313
- const deviceUrl = "<device-ip:port>";
314
- const pluginServers = plugins.map((p) => `http://${yourIp}:${p.port}`).join(",");
315
-
316
- console.log(chalk.green(` ${deviceUrl}/?pluginDevServers=${pluginServers}`));
317
- console.log("");
318
- console.log(chalk.yellow("Replace:"));
319
- console.log(` - ${chalk.cyan("<your-development-machine-ip>")} with your machine's IP address`);
320
- console.log(` - ${chalk.cyan("<device-ip:port>")} with your device's address`);
321
- console.log("");
322
- console.log(chalk.yellow("Example:"));
323
- const examplePluginServers = plugins.map((p) => `http://localhost:${p.port}`).join(",");
324
- console.log(
325
- chalk.green(
326
- ` http://192.168.1.100:8080/?pluginDevServers=${examplePluginServers}`
327
- )
328
- );
329
- console.log("");
330
- console.log(chalk.blue("Logs:"));
331
- console.log(` - ${logDir}/`);
332
- console.log("");
333
- console.log(chalk.yellow("Press Ctrl+C to stop all servers"));
334
- console.log("");
335
- }
336
-
337
- export default async function (plop) {
338
- plop.setActionType("runDev", async function (answers) {
339
- // Validate configuration
340
- const validation = validateDevConfig();
341
- if (!validation.isValid) {
342
- console.log(chalk.red("\nError: Invalid development configuration"));
343
- validation.errors.forEach((err) => console.log(chalk.red(` - ${err}`)));
344
- console.log("");
345
- console.log(chalk.yellow("This workspace does not support metadata-driven development."));
346
- console.log(chalk.yellow("Run the migration generator or create a new workspace with the latest preset."));
347
- return "Failed: Invalid configuration";
348
- }
349
-
350
- const { host, plugins: pluginsArg, disable, pluginsOnly } = answers;
351
-
352
- // Get host configuration
353
- const hostConfig = getHost(host);
354
- if (!hostConfig) {
355
- console.log(chalk.red(`\nError: Host '${host}' not found in .kos.json`));
356
- return `Failed: Host not found`;
357
- }
358
-
359
- // Parse plugin filters
360
- const pluginFilter = pluginsArg ? pluginsArg.split(",") : null;
361
- const disableFilter = disable ? disable.split(",") : null;
362
-
363
- // Filter plugins
364
- const plugins = filterPlugins(hostConfig.plugins, {
365
- plugins: pluginFilter,
366
- disable: disableFilter,
367
- });
368
-
369
- if (plugins.length === 0) {
370
- console.log(chalk.yellow("Warning: No plugins selected"));
371
- console.log("");
372
- }
373
-
374
- // Determine log directory
375
- const logDir = process.env.DEV_LOG_DIR || "/tmp/kos-dev-logs";
376
-
377
- // Determine mode
378
- const hostProjectExists = checkProjectExists(hostConfig.project);
379
-
380
- if (hostProjectExists && !pluginsOnly) {
381
- // Internal Development Mode
382
- await startInternalDevelopment(hostConfig, plugins, logDir);
383
- } else {
384
- // Third-Party Development Mode
385
- if (!pluginsOnly && !hostProjectExists) {
386
- console.log(chalk.yellow(`Note: Host project '${hostConfig.project}' not found in workspace.`));
387
- console.log(chalk.yellow("Starting in third-party development mode (plugins only)."));
388
- console.log("");
389
- }
390
- await startThirdPartyDevelopment(hostConfig, plugins, logDir);
391
- }
392
-
393
- // Keep process running
394
- await new Promise(() => {});
395
- });
396
-
397
- // Get available hosts for prompts
398
- const hosts = getAllHosts();
399
-
400
- if (hosts.length === 0) {
401
- console.warn("[kos-cli] No hosts found in .kos.json development configuration");
402
- }
403
-
404
- plop.setGenerator("dev", {
405
- description: "Start development servers for host applications and plugins",
406
- prompts: [
407
- {
408
- type: "list",
409
- name: "host",
410
- message: "Select a host application:",
411
- choices: hosts.map((h) => ({
412
- name: `${h.name} (${h.type}) - ${h.plugins.length} plugin(s)`,
413
- value: h.name,
414
- })),
415
- },
416
- {
417
- type: "input",
418
- name: "plugins",
419
- message: "Specific plugins to start (comma-separated aliases, leave empty for all enabled):",
420
- default: "",
421
- },
422
- {
423
- type: "input",
424
- name: "disable",
425
- message: "Plugins to disable (comma-separated aliases, leave empty for none):",
426
- default: "",
427
- },
428
- {
429
- type: "confirm",
430
- name: "pluginsOnly",
431
- message: "Start plugins only (third-party mode)?",
432
- default: false,
433
- },
434
- ],
435
- actions: () => [{ type: "runDev" }],
436
- });
437
- }
@@ -1,82 +0,0 @@
1
- // generators/kab/index.mjs
2
- import { execSync } from "child_process";
3
- import inquirer from "inquirer";
4
- import { detectWorkspace } from "../../utils/nx-context.mjs";
5
-
6
- export const metadata = {
7
- key: "kab",
8
- name: "Run Kab Target",
9
- namedArguments: {
10
- project: "project",
11
- interactive: "interactive"
12
- },
13
- };
14
-
15
- async function getProjectsWithTarget(target) {
16
- try {
17
- const output = execSync(`npx nx show projects --with-target ${target} --json`, {
18
- encoding: "utf-8",
19
- stdio: ["pipe", "pipe", "ignore"]
20
- });
21
- return JSON.parse(output);
22
- } catch (error) {
23
- return [];
24
- }
25
- }
26
-
27
- export default async function (plop) {
28
- const projects = await getProjectsWithTarget("kab");
29
-
30
- if (projects.length === 0) {
31
- console.warn("[kos-cli] No projects found with kab target");
32
- // Still register the generator but it will fail gracefully
33
- }
34
-
35
- plop.setActionType("runKab", async function (answers) {
36
- const isWorkspace = await detectWorkspace();
37
-
38
- if (!isWorkspace) {
39
- console.warn(
40
- "[kos-cli] Not inside an Nx workspace. Cannot run kab target."
41
- );
42
- return "Skipped: Not a workspace";
43
- }
44
-
45
- const { project } = answers;
46
-
47
- if (project === "all") {
48
- console.log("[kos-cli] Running kab on all projects with kab target...");
49
- try {
50
- execSync("npx nx run-many --target=kab", {
51
- stdio: "inherit",
52
- });
53
- return "Successfully ran kab on all projects";
54
- } catch (error) {
55
- throw new Error(`Failed to run kab on all projects: ${error.message}`);
56
- }
57
- } else {
58
- console.log(`[kos-cli] Running kab on ${project}...`);
59
- try {
60
- execSync(`npx nx run ${project}:kab`, {
61
- stdio: "inherit",
62
- });
63
- return `Successfully ran kab on ${project}`;
64
- } catch (error) {
65
- throw new Error(`Failed to run kab on ${project}: ${error.message}`);
66
- }
67
- }
68
- });
69
-
70
- plop.setGenerator("kab", {
71
- description: "Run the kab target on a project",
72
- prompts: [
73
- {
74
- type: "list",
75
- name: "project",
76
- message: "Select a project to run kab:",
77
- choices: [...projects, new inquirer.Separator(), { name: "Run on all projects", value: "all" }],
78
- },
79
- ],
80
- actions: () => [{ type: "runKab" }],
81
- });
82
- }
@@ -1,74 +0,0 @@
1
- // generators/serve/index.mjs
2
- import { execSync } from "child_process";
3
- import { detectWorkspace } from "../../utils/nx-context.mjs";
4
-
5
- export const metadata = {
6
- key: "serve",
7
- name: "Run Serve Target",
8
- namedArguments: {
9
- project: "project",
10
- interactive: "interactive"
11
- },
12
- };
13
-
14
- async function getProjectsWithTarget(target) {
15
- try {
16
- const output = execSync(`npx nx show projects --with-target ${target} --json`, {
17
- encoding: "utf-8",
18
- stdio: ["pipe", "pipe", "ignore"]
19
- });
20
- return JSON.parse(output);
21
- } catch (error) {
22
- return [];
23
- }
24
- }
25
-
26
- export default async function (plop) {
27
- const projects = await getProjectsWithTarget("serve");
28
-
29
- if (projects.length === 0) {
30
- console.warn("[kos-cli] No projects found with serve target");
31
- // Still register the generator but it will fail gracefully
32
- }
33
-
34
- plop.setActionType("runServe", async function (answers) {
35
- const isWorkspace = await detectWorkspace();
36
-
37
- if (!isWorkspace) {
38
- console.warn(
39
- "[kos-cli] Not inside an Nx workspace. Cannot run serve target."
40
- );
41
- return "Skipped: Not a workspace";
42
- }
43
-
44
- const { project } = answers;
45
-
46
- console.log(`[kos-cli] Running serve on ${project}...`);
47
- try {
48
- // Run serve in foreground with stdio inherited so it stays alive
49
- execSync(`npx nx run ${project}:serve`, {
50
- stdio: "inherit",
51
- });
52
- return `Serve completed for ${project}`;
53
- } catch (error) {
54
- // Don't throw error for Ctrl+C or normal termination
55
- if (error.signal === "SIGINT") {
56
- return `Serve terminated for ${project}`;
57
- }
58
- throw new Error(`Failed to run serve on ${project}: ${error.message}`);
59
- }
60
- });
61
-
62
- plop.setGenerator("serve", {
63
- description: "Run the serve target on a project",
64
- prompts: [
65
- {
66
- type: "list",
67
- name: "project",
68
- message: "Select a project to serve:",
69
- choices: projects,
70
- },
71
- ],
72
- actions: () => [{ type: "runServe" }],
73
- });
74
- }