@mozole/cli 1.4.3 → 2.0.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/LICENSE +16 -4
- package/README.md +85 -112
- package/dist/cli.d.ts +3 -2
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +231 -181
- package/dist/cli.js.map +1 -1
- package/dist/commands/adopt.d.ts +5 -10
- package/dist/commands/adopt.d.ts.map +1 -1
- package/dist/commands/adopt.js +49 -849
- package/dist/commands/adopt.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -7
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +69 -377
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/new.d.ts +8 -10
- package/dist/commands/new.d.ts.map +1 -1
- package/dist/commands/new.js +32 -197
- package/dist/commands/new.js.map +1 -1
- package/dist/commands/phase.d.ts +11 -0
- package/dist/commands/phase.d.ts.map +1 -0
- package/dist/commands/phase.js +106 -0
- package/dist/commands/phase.js.map +1 -0
- package/dist/commands/prototype.d.ts +6 -0
- package/dist/commands/prototype.d.ts.map +1 -0
- package/dist/commands/prototype.js +28 -0
- package/dist/commands/prototype.js.map +1 -0
- package/dist/commands/repomap.d.ts +6 -0
- package/dist/commands/repomap.d.ts.map +1 -0
- package/dist/commands/repomap.js +71 -0
- package/dist/commands/repomap.js.map +1 -0
- package/dist/commands/test.d.ts +6 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +115 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/ui.d.ts +6 -41
- package/dist/commands/ui.d.ts.map +1 -1
- package/dist/commands/ui.js +132 -459
- package/dist/commands/ui.js.map +1 -1
- package/dist/commands/verify.d.ts +12 -7
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +180 -65
- package/dist/commands/verify.js.map +1 -1
- package/dist/qa/a11y.d.ts +24 -0
- package/dist/qa/a11y.d.ts.map +1 -0
- package/dist/qa/a11y.js +124 -0
- package/dist/qa/a11y.js.map +1 -0
- package/dist/qa/breakpoints.d.ts +16 -0
- package/dist/qa/breakpoints.d.ts.map +1 -0
- package/dist/qa/breakpoints.js +45 -0
- package/dist/qa/breakpoints.js.map +1 -0
- package/dist/qa/contract.d.ts +21 -0
- package/dist/qa/contract.d.ts.map +1 -0
- package/dist/qa/contract.js +116 -0
- package/dist/qa/contract.js.map +1 -0
- package/dist/qa/index.d.ts +6 -0
- package/dist/qa/index.d.ts.map +1 -0
- package/dist/qa/index.js +6 -0
- package/dist/qa/index.js.map +1 -0
- package/dist/qa/probe.d.ts +48 -0
- package/dist/qa/probe.d.ts.map +1 -0
- package/dist/qa/probe.js +206 -0
- package/dist/qa/probe.js.map +1 -0
- package/dist/qa/runner.d.ts +27 -33
- package/dist/qa/runner.d.ts.map +1 -1
- package/dist/qa/runner.js +286 -332
- package/dist/qa/runner.js.map +1 -1
- package/dist/scaffold/agents.d.ts +5 -0
- package/dist/scaffold/agents.d.ts.map +1 -0
- package/dist/scaffold/agents.js +84 -0
- package/dist/scaffold/agents.js.map +1 -0
- package/dist/scaffold/backend.d.ts +6 -8
- package/dist/scaffold/backend.d.ts.map +1 -1
- package/dist/scaffold/backend.js +318 -199
- package/dist/scaffold/backend.js.map +1 -1
- package/dist/scaffold/design.d.ts +4 -3
- package/dist/scaffold/design.d.ts.map +1 -1
- package/dist/scaffold/design.js +38 -47
- package/dist/scaffold/design.js.map +1 -1
- package/dist/scaffold/flagship.d.ts +8 -0
- package/dist/scaffold/flagship.d.ts.map +1 -0
- package/dist/scaffold/flagship.js +337 -0
- package/dist/scaffold/flagship.js.map +1 -0
- package/dist/scaffold/index.d.ts +9 -0
- package/dist/scaffold/index.d.ts.map +1 -0
- package/dist/scaffold/index.js +48 -0
- package/dist/scaffold/index.js.map +1 -0
- package/dist/scaffold/phases.d.ts +10 -0
- package/dist/scaffold/phases.d.ts.map +1 -0
- package/dist/scaffold/phases.js +165 -0
- package/dist/scaffold/phases.js.map +1 -0
- package/dist/scaffold/repomap.d.ts +6 -0
- package/dist/scaffold/repomap.d.ts.map +1 -0
- package/dist/scaffold/repomap.js +89 -0
- package/dist/scaffold/repomap.js.map +1 -0
- package/dist/scaffold/standard.d.ts +13 -0
- package/dist/scaffold/standard.d.ts.map +1 -0
- package/dist/scaffold/standard.js +371 -0
- package/dist/scaffold/standard.js.map +1 -0
- package/dist/scaffold/tokens.d.ts +3 -0
- package/dist/scaffold/tokens.d.ts.map +1 -0
- package/dist/scaffold/tokens.js +126 -0
- package/dist/scaffold/tokens.js.map +1 -0
- package/dist/server/manager.d.ts +23 -0
- package/dist/server/manager.d.ts.map +1 -0
- package/dist/server/manager.js +96 -0
- package/dist/server/manager.js.map +1 -0
- package/dist/ui/CockpitApp.d.ts +13 -0
- package/dist/ui/CockpitApp.d.ts.map +1 -0
- package/dist/ui/CockpitApp.js +292 -0
- package/dist/ui/CockpitApp.js.map +1 -0
- package/dist/utils/ai-trace.d.ts +8 -0
- package/dist/utils/ai-trace.d.ts.map +1 -0
- package/dist/utils/ai-trace.js +69 -0
- package/dist/utils/ai-trace.js.map +1 -0
- package/dist/utils/browser.d.ts +10 -0
- package/dist/utils/browser.d.ts.map +1 -0
- package/dist/utils/browser.js +127 -0
- package/dist/utils/browser.js.map +1 -0
- package/dist/utils/fs.d.ts +18 -0
- package/dist/utils/fs.d.ts.map +1 -0
- package/dist/utils/fs.js +296 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/git.d.ts +9 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +69 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/process.d.ts +21 -0
- package/dist/utils/process.d.ts.map +1 -0
- package/dist/utils/process.js +120 -0
- package/dist/utils/process.js.map +1 -0
- package/dist/version.d.ts +21 -36
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +21 -44
- package/dist/version.js.map +1 -1
- package/package.json +22 -38
- package/CHANGELOG.md +0 -38
- package/dist/commands/cce.d.ts +0 -7
- package/dist/commands/cce.d.ts.map +0 -1
- package/dist/commands/cce.js +0 -37
- package/dist/commands/cce.js.map +0 -1
- package/dist/commands/logs.d.ts +0 -3
- package/dist/commands/logs.d.ts.map +0 -1
- package/dist/commands/logs.js +0 -40
- package/dist/commands/logs.js.map +0 -1
- package/dist/commands/qa.d.ts +0 -12
- package/dist/commands/qa.d.ts.map +0 -1
- package/dist/commands/qa.js +0 -143
- package/dist/commands/qa.js.map +0 -1
- package/dist/commands/stats.d.ts +0 -37
- package/dist/commands/stats.d.ts.map +0 -1
- package/dist/commands/stats.js +0 -169
- package/dist/commands/stats.js.map +0 -1
- package/dist/commands/template.d.ts +0 -8
- package/dist/commands/template.d.ts.map +0 -1
- package/dist/commands/template.js +0 -392
- package/dist/commands/template.js.map +0 -1
- package/dist/config/project.d.ts +0 -54
- package/dist/config/project.d.ts.map +0 -1
- package/dist/config/project.js +0 -100
- package/dist/config/project.js.map +0 -1
- package/dist/context/cce.d.ts +0 -39
- package/dist/context/cce.d.ts.map +0 -1
- package/dist/context/cce.js +0 -455
- package/dist/context/cce.js.map +0 -1
- package/dist/governor/fingerprint.d.ts +0 -7
- package/dist/governor/fingerprint.d.ts.map +0 -1
- package/dist/governor/fingerprint.js +0 -120
- package/dist/governor/fingerprint.js.map +0 -1
- package/dist/governor/hook.d.ts +0 -13
- package/dist/governor/hook.d.ts.map +0 -1
- package/dist/governor/hook.js +0 -389
- package/dist/governor/hook.js.map +0 -1
- package/dist/governor/state.d.ts +0 -45
- package/dist/governor/state.d.ts.map +0 -1
- package/dist/governor/state.js +0 -60
- package/dist/governor/state.js.map +0 -1
- package/dist/integrations/antigravity.d.ts +0 -20
- package/dist/integrations/antigravity.d.ts.map +0 -1
- package/dist/integrations/antigravity.js +0 -153
- package/dist/integrations/antigravity.js.map +0 -1
- package/dist/output-safety.d.ts +0 -3
- package/dist/output-safety.d.ts.map +0 -1
- package/dist/output-safety.js +0 -44
- package/dist/output-safety.js.map +0 -1
- package/dist/output.d.ts +0 -5
- package/dist/output.d.ts.map +0 -1
- package/dist/output.js +0 -2
- package/dist/output.js.map +0 -1
- package/dist/platform/executable.d.ts +0 -11
- package/dist/platform/executable.d.ts.map +0 -1
- package/dist/platform/executable.js +0 -69
- package/dist/platform/executable.js.map +0 -1
- package/dist/platform/process.d.ts +0 -27
- package/dist/platform/process.d.ts.map +0 -1
- package/dist/platform/process.js +0 -189
- package/dist/platform/process.js.map +0 -1
- package/dist/progress.d.ts +0 -44
- package/dist/progress.d.ts.map +0 -1
- package/dist/progress.js +0 -123
- package/dist/progress.js.map +0 -1
- package/dist/qa/artifacts.d.ts +0 -3
- package/dist/qa/artifacts.d.ts.map +0 -1
- package/dist/qa/artifacts.js +0 -15
- package/dist/qa/artifacts.js.map +0 -1
- package/dist/qa/diff.d.ts +0 -10
- package/dist/qa/diff.d.ts.map +0 -1
- package/dist/qa/diff.js +0 -90
- package/dist/qa/diff.js.map +0 -1
- package/dist/qa/server.d.ts +0 -10
- package/dist/qa/server.d.ts.map +0 -1
- package/dist/qa/server.js +0 -127
- package/dist/qa/server.js.map +0 -1
- package/dist/scaffold/adoption.d.ts +0 -17
- package/dist/scaffold/adoption.d.ts.map +0 -1
- package/dist/scaffold/adoption.js +0 -279
- package/dist/scaffold/adoption.js.map +0 -1
- package/dist/scaffold/legacy.d.ts +0 -4
- package/dist/scaffold/legacy.d.ts.map +0 -1
- package/dist/scaffold/legacy.js +0 -139
- package/dist/scaffold/legacy.js.map +0 -1
- package/dist/scaffold/routes.d.ts +0 -3
- package/dist/scaffold/routes.d.ts.map +0 -1
- package/dist/scaffold/routes.js +0 -37
- package/dist/scaffold/routes.js.map +0 -1
- package/dist/scaffold/standalone.d.ts +0 -7
- package/dist/scaffold/standalone.d.ts.map +0 -1
- package/dist/scaffold/standalone.js +0 -374
- package/dist/scaffold/standalone.js.map +0 -1
- package/dist/scaffold/templates.d.ts +0 -9
- package/dist/scaffold/templates.d.ts.map +0 -1
- package/dist/scaffold/templates.js +0 -317
- package/dist/scaffold/templates.js.map +0 -1
- package/dist/scaffold/write.d.ts +0 -2
- package/dist/scaffold/write.d.ts.map +0 -1
- package/dist/scaffold/write.js +0 -38
- package/dist/scaffold/write.js.map +0 -1
- package/dist/telemetry/store.d.ts +0 -33
- package/dist/telemetry/store.d.ts.map +0 -1
- package/dist/telemetry/store.js +0 -75
- package/dist/telemetry/store.js.map +0 -1
- package/dist/template/servers.d.ts +0 -21
- package/dist/template/servers.d.ts.map +0 -1
- package/dist/template/servers.js +0 -193
- package/dist/template/servers.js.map +0 -1
- package/dist/template/workspace.d.ts +0 -23
- package/dist/template/workspace.d.ts.map +0 -1
- package/dist/template/workspace.js +0 -152
- package/dist/template/workspace.js.map +0 -1
- package/dist/verify/runner.d.ts +0 -16
- package/dist/verify/runner.d.ts.map +0 -1
- package/dist/verify/runner.js +0 -112
- package/dist/verify/runner.js.map +0 -1
- package/docs/agent-audit.md +0 -24
package/dist/commands/ui.js
CHANGED
|
@@ -1,481 +1,154 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import { adoptCommand } from "./adopt.js";
|
|
10
|
-
import { cceInstallCommand } from "./cce.js";
|
|
2
|
+
import { render } from "ink";
|
|
3
|
+
import pc from "picocolors";
|
|
4
|
+
import prompts from "prompts";
|
|
5
|
+
import React from "react";
|
|
6
|
+
import { serverManager } from "../server/manager.js";
|
|
7
|
+
import { CockpitApp } from "../ui/CockpitApp.js";
|
|
8
|
+
import { discoverWorkspaceProjects, findProjectRoot, findPrototypeRoot, } from "../utils/fs.js";
|
|
11
9
|
import { doctorCommand } from "./doctor.js";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import { statsCommand } from "./stats.js";
|
|
15
|
-
import { templateAdoptCommand } from "./template.js";
|
|
10
|
+
import { phaseCommand } from "./phase.js";
|
|
11
|
+
import { repomapCommand } from "./repomap.js";
|
|
16
12
|
import { verifyCommand } from "./verify.js";
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
run: (cwd, output, opts) => verifyCommand(cwd, output, opts),
|
|
31
|
-
},
|
|
32
|
-
{
|
|
33
|
-
label: "QA",
|
|
34
|
-
detail: "Run deterministic, screenshot-free browser probes",
|
|
35
|
-
key: "3",
|
|
36
|
-
kind: "command",
|
|
37
|
-
run: (cwd, output, opts) => qaCommand(cwd, {
|
|
38
|
-
interactive: false,
|
|
39
|
-
visual: false,
|
|
40
|
-
onProgress: opts?.onProgress,
|
|
41
|
-
}, output),
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
label: "Stats",
|
|
45
|
-
detail: "Inspect token savings & verification cache telemetry",
|
|
46
|
-
key: "4",
|
|
47
|
-
kind: "command",
|
|
48
|
-
run: (cwd, output, opts) => statsCommand(cwd, output, opts),
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
label: "Install CCE",
|
|
52
|
-
detail: "Provision persistent uv & code-context-engine runtime",
|
|
53
|
-
key: "5",
|
|
54
|
-
kind: "install",
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
label: "New project",
|
|
58
|
-
detail: "Scaffold a static-first production-ready application",
|
|
59
|
-
key: "6",
|
|
60
|
-
kind: "create",
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
label: "Adopt project",
|
|
64
|
-
detail: "Integrate Mozole toolchain into an existing app",
|
|
65
|
-
key: "7",
|
|
66
|
-
kind: "adopt",
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
label: "Servers",
|
|
70
|
-
detail: "Start & stop template project npm servers",
|
|
71
|
-
key: "8",
|
|
72
|
-
kind: "servers",
|
|
73
|
-
},
|
|
74
|
-
{
|
|
75
|
-
label: "Exit",
|
|
76
|
-
detail: "Return to your shell",
|
|
77
|
-
key: "9",
|
|
78
|
-
kind: "exit",
|
|
79
|
-
},
|
|
80
|
-
];
|
|
81
|
-
const ansi = {
|
|
82
|
-
alternateEnter: "\u001B[?1049h",
|
|
83
|
-
alternateExit: "\u001B[?1049l",
|
|
84
|
-
clear: "\u001B[2J\u001B[H",
|
|
85
|
-
cursorHide: "\u001B[?25l",
|
|
86
|
-
cursorShow: "\u001B[?25h",
|
|
87
|
-
reset: "\u001B[0m",
|
|
88
|
-
};
|
|
89
|
-
const ansiSequence = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
|
|
90
|
-
function paint(value, code, color) {
|
|
91
|
-
return color ? `\u001B[${code}m${value}${ansi.reset}` : value;
|
|
92
|
-
}
|
|
93
|
-
function clamp(value, width) {
|
|
94
|
-
if (width < 2)
|
|
95
|
-
return "";
|
|
96
|
-
const plain = value.replace(ansiSequence, "");
|
|
97
|
-
return plain.length <= width
|
|
98
|
-
? `${value}${" ".repeat(width - plain.length)}`
|
|
99
|
-
: `${plain.slice(0, Math.max(0, width - 1))}…`;
|
|
100
|
-
}
|
|
101
|
-
function box(lines, width, color, sectionTitle) {
|
|
102
|
-
const inner = Math.max(10, width - 2);
|
|
103
|
-
let topBorder = "─".repeat(inner);
|
|
104
|
-
if (sectionTitle) {
|
|
105
|
-
const titleText = ` ${sectionTitle} `;
|
|
106
|
-
if (titleText.length < inner - 4) {
|
|
107
|
-
topBorder = `──${titleText}${"─".repeat(inner - 2 - titleText.length)}`;
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const top = `${paint("╭", 90, color)}${paint(topBorder, 90, color)}${paint("╮", 90, color)}`;
|
|
111
|
-
const bottom = `${paint("╰", 90, color)}${paint("─".repeat(inner), 90, color)}${paint("╯", 90, color)}`;
|
|
112
|
-
return [
|
|
113
|
-
top,
|
|
114
|
-
...lines.map((line) => `${paint("│", 90, color)} ${clamp(line, inner - 2)} ${paint("│", 90, color)}`),
|
|
115
|
-
bottom,
|
|
116
|
-
];
|
|
117
|
-
}
|
|
118
|
-
export function uiMenuText() {
|
|
119
|
-
return actions.map((action) => `${action.key} ${action.label}`).join("\n");
|
|
120
|
-
}
|
|
121
|
-
export function uiDashboardFrame(state, dimensions, color = true) {
|
|
122
|
-
const width = Math.max(54, Math.min(100, dimensions.columns - 2));
|
|
123
|
-
const height = Math.max(20, dimensions.rows);
|
|
124
|
-
const title = paint("Mozole-cli", 96, color);
|
|
125
|
-
const version = paint(`v${CLI_VERSION}`, 90, color);
|
|
126
|
-
const subtitle = paint("COMMAND DECK", 95, color);
|
|
127
|
-
const copyright = paint("© MozoleStudio", 90, color);
|
|
128
|
-
const projectLabel = paint("PROJECT", 36, color);
|
|
129
|
-
const projectName = paint(sanitizeTerminalText(path.basename(state.cwd) || state.cwd), 97, color);
|
|
130
|
-
const header = [
|
|
131
|
-
` ${paint("◆", 96, color)} ${title} ${version} ${paint("│", 90, color)} ${subtitle} ${paint("│", 90, color)} ${copyright}`,
|
|
132
|
-
` ${projectLabel} ${projectName}`,
|
|
133
|
-
];
|
|
134
|
-
const actionLines = actions.map((action, index) => {
|
|
135
|
-
const active = state.selected === index;
|
|
136
|
-
const marker = active ? paint("❯", 96, color) : paint("·", 90, color);
|
|
137
|
-
const key = paint(`[${action.key}]`, active ? 97 : 36, color);
|
|
138
|
-
const label = paint(action.label.padEnd(14), active ? 97 : 37, color);
|
|
139
|
-
const detail = paint(action.detail, active ? 96 : 90, color);
|
|
140
|
-
return `${marker} ${key} ${label} ${detail}`;
|
|
141
|
-
});
|
|
142
|
-
let statusBadge = paint("● READY Select an operation", 92, color);
|
|
143
|
-
let progressLine;
|
|
144
|
-
if (state.busy) {
|
|
145
|
-
statusBadge = paint(`⟳ RUNNING ${state.busy}`, 93, color);
|
|
146
|
-
if (state.progress) {
|
|
147
|
-
const bar = renderProgressBar(state.progress.current / state.progress.total, 14);
|
|
148
|
-
const stepInfo = paint(`[${state.progress.current}/${state.progress.total}]`, 97, color);
|
|
149
|
-
const msg = paint(sanitizeTerminalText(state.progress.message), 90, color);
|
|
150
|
-
progressLine = ` ${paint(bar, 36, color)} ${stepInfo} ${msg}`;
|
|
151
|
-
}
|
|
13
|
+
export async function uiCommand(options = {}) {
|
|
14
|
+
const cwd = options.cwd ?? process.cwd();
|
|
15
|
+
const protoRoot = await findPrototypeRoot(cwd);
|
|
16
|
+
let projectRoot = await findProjectRoot(cwd);
|
|
17
|
+
if (projectRoot === protoRoot)
|
|
18
|
+
projectRoot = null;
|
|
19
|
+
const discoveredProjects = protoRoot
|
|
20
|
+
? await discoverWorkspaceProjects(protoRoot)
|
|
21
|
+
: [];
|
|
22
|
+
let activeProject = options.project ?? null;
|
|
23
|
+
if (!activeProject && projectRoot) {
|
|
24
|
+
const matched = discoveredProjects.find((p) => p.path === projectRoot);
|
|
25
|
+
activeProject = matched ? matched.name : path.basename(projectRoot);
|
|
152
26
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
27
|
+
let initialTab = "overview";
|
|
28
|
+
// If in prototype / monorepo workspace and no project specified
|
|
29
|
+
if (!projectRoot && protoRoot && discoveredProjects.length > 0) {
|
|
30
|
+
if (options.project) {
|
|
31
|
+
const matched = discoveredProjects.find((p) => p.name === options.project ||
|
|
32
|
+
p.relative === options.project ||
|
|
33
|
+
path.basename(p.path) === options.project);
|
|
34
|
+
if (matched) {
|
|
35
|
+
activeProject = matched.name;
|
|
36
|
+
projectRoot = matched.path;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
activeProject = options.project;
|
|
40
|
+
projectRoot = path.join(protoRoot, options.project);
|
|
41
|
+
}
|
|
156
42
|
}
|
|
157
|
-
else if (
|
|
158
|
-
|
|
43
|
+
else if (process.env.VITEST || options.interactive === false) {
|
|
44
|
+
// In automated tests or headless programmatic mode, prompt for selection if available
|
|
45
|
+
const choice = await prompts({
|
|
46
|
+
type: "select",
|
|
47
|
+
name: "project",
|
|
48
|
+
message: "Select an active client project:",
|
|
49
|
+
choices: discoveredProjects.map((p) => ({ title: p.name, value: p.name })),
|
|
50
|
+
});
|
|
51
|
+
if (choice?.project) {
|
|
52
|
+
const found = discoveredProjects.find((p) => p.name === choice.project);
|
|
53
|
+
activeProject = choice.project;
|
|
54
|
+
projectRoot = found ? found.path : path.join(protoRoot, choice.project);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
activeProject = discoveredProjects[0].name;
|
|
58
|
+
projectRoot = discoveredProjects[0].path;
|
|
59
|
+
}
|
|
159
60
|
}
|
|
160
61
|
else {
|
|
161
|
-
|
|
62
|
+
// In interactive terminal at monorepo root: default to first project and open projects explorer tab!
|
|
63
|
+
activeProject = discoveredProjects[0].name;
|
|
64
|
+
projectRoot = discoveredProjects[0].path;
|
|
65
|
+
initialTab = "projects";
|
|
162
66
|
}
|
|
163
67
|
}
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return `${ansi.clear}${frame.join("\n")}${"\n".repeat(padding)}`;
|
|
185
|
-
}
|
|
186
|
-
export function uiServerManagerFrame(root, servers, selected, dimensions, color = true, message = "") {
|
|
187
|
-
const width = Math.max(54, Math.min(100, dimensions.columns - 2));
|
|
188
|
-
const height = Math.max(20, dimensions.rows);
|
|
189
|
-
const rows = servers.length
|
|
190
|
-
? servers.map((server, index) => {
|
|
191
|
-
const marker = selected === index ? paint("❯", 96, color) : paint("·", 90, color);
|
|
192
|
-
const statusCode = server.status === "running"
|
|
193
|
-
? 92
|
|
194
|
-
: server.status === "failed"
|
|
195
|
-
? 91
|
|
196
|
-
: server.status === "starting"
|
|
197
|
-
? 93
|
|
198
|
-
: 90;
|
|
199
|
-
const status = paint(server.status.toUpperCase().padEnd(8), statusCode, color);
|
|
200
|
-
const address = server.status === "running"
|
|
201
|
-
? `http://127.0.0.1:${server.port}`
|
|
202
|
-
: `preferred :${server.preferredPort}`;
|
|
203
|
-
return `${marker} ${status} ${sanitizeTerminalText(server.relativePath).padEnd(22)} ${address}`;
|
|
204
|
-
})
|
|
205
|
-
: [paint(" No projects found. Run `mozole new <name>` here.", 90, color)];
|
|
206
|
-
const frame = [
|
|
207
|
-
"",
|
|
208
|
-
` ${paint("◆", 96, color)} ${paint("NPM SERVER MANAGER", 96, color)}`,
|
|
209
|
-
` ${paint("TEMPLATE", 36, color)} ${paint(sanitizeTerminalText(path.basename(root)), 97, color)}`,
|
|
210
|
-
"",
|
|
211
|
-
...box(rows, width, color, "PROJECTS"),
|
|
212
|
-
"",
|
|
213
|
-
...box([
|
|
214
|
-
` ${sanitizeTerminalText(message || "Select a project and press Enter to start or stop it.")}`,
|
|
215
|
-
], width, color, "STATUS"),
|
|
216
|
-
"",
|
|
217
|
-
paint(" ↑↓ / j k navigate enter / s start-stop a start all x stop all q / esc back", 90, color),
|
|
218
|
-
paint(" Servers live for this UI session. Logs: .mozole/logs/servers/", 90, color),
|
|
219
|
-
];
|
|
220
|
-
return `${ansi.clear}${frame.join("\n")}${"\n".repeat(Math.max(0, height - frame.length - 1))}`;
|
|
221
|
-
}
|
|
222
|
-
function hasColor(output) {
|
|
223
|
-
return (Boolean(output.isTTY) &&
|
|
224
|
-
!process.env.NO_COLOR &&
|
|
225
|
-
process.env.TERM !== "dumb");
|
|
226
|
-
}
|
|
227
|
-
function prompt(input, output, question) {
|
|
228
|
-
const readline = createInterface({ input, output });
|
|
229
|
-
return readline
|
|
230
|
-
.question(question)
|
|
231
|
-
.then((answer) => answer.trim())
|
|
232
|
-
.catch(() => undefined)
|
|
233
|
-
.finally(() => readline.close());
|
|
234
|
-
}
|
|
235
|
-
function waitForKey(input) {
|
|
236
|
-
return new Promise((resolve) => {
|
|
237
|
-
input.once("data", (chunk) => resolve(String(chunk)));
|
|
238
|
-
});
|
|
239
|
-
}
|
|
240
|
-
function statusLine(message) {
|
|
241
|
-
return sanitizeTerminalText(String(message))
|
|
242
|
-
.split(/\r?\n/)
|
|
243
|
-
.map((line) => line.trim())
|
|
244
|
-
.filter(Boolean)
|
|
245
|
-
.slice(-5);
|
|
246
|
-
}
|
|
247
|
-
export async function uiCommand(cwd, output = console, streams = {}) {
|
|
248
|
-
const input = streams.input ?? stdin;
|
|
249
|
-
const terminalOutput = streams.output ?? stdout;
|
|
250
|
-
const interactive = streams.interactive ?? Boolean(input.isTTY && terminalOutput.isTTY);
|
|
251
|
-
if (!interactive || !input.setRawMode) {
|
|
252
|
-
output.error("UI requires an interactive terminal with ANSI support. Use `mozole doctor`, `mozole verify`, `mozole qa`, or `mozole stats` in CI.");
|
|
253
|
-
return 2;
|
|
68
|
+
const targetPath = projectRoot ?? cwd;
|
|
69
|
+
const projectName = activeProject ?? path.basename(targetPath);
|
|
70
|
+
// If programmatic action was requested (or prompted during non-interactive test)
|
|
71
|
+
let requestedAction = options.action;
|
|
72
|
+
if (!requestedAction && (process.env.VITEST || options.interactive === false)) {
|
|
73
|
+
const promptRes = await prompts({
|
|
74
|
+
type: "select",
|
|
75
|
+
name: "action",
|
|
76
|
+
message: "Select operation:",
|
|
77
|
+
choices: [
|
|
78
|
+
{ title: "[01] Phase Status", value: "phase-status" },
|
|
79
|
+
{ title: "[02] Phase Next", value: "phase-next" },
|
|
80
|
+
{ title: "[03] Verify", value: "verify" },
|
|
81
|
+
{ title: "[04] Server Start", value: "server-start" },
|
|
82
|
+
{ title: "[05] Server Stop", value: "server-stop" },
|
|
83
|
+
{ title: "[06] Repomap", value: "repomap" },
|
|
84
|
+
{ title: "[07] Doctor", value: "doctor" },
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
requestedAction = promptRes?.action;
|
|
254
88
|
}
|
|
255
|
-
|
|
256
|
-
const directOutput = {
|
|
257
|
-
log: (message, ...optional) => output.log(sanitizeTerminalText(String(message ?? "")), ...optional.map((value) => sanitizeTerminalText(String(value ?? "")))),
|
|
258
|
-
error: (message, ...optional) => output.error(sanitizeTerminalText(String(message ?? "")), ...optional.map((value) => sanitizeTerminalText(String(value ?? "")))),
|
|
259
|
-
};
|
|
260
|
-
const color = hasColor(terminalOutput);
|
|
261
|
-
let entered = false;
|
|
262
|
-
let serverManager;
|
|
263
|
-
let serverViewRender;
|
|
264
|
-
let requestedExitCode;
|
|
265
|
-
const render = () => terminalOutput.write(uiDashboardFrame(state, {
|
|
266
|
-
columns: terminalOutput.columns ?? 100,
|
|
267
|
-
rows: terminalOutput.rows ?? 32,
|
|
268
|
-
}, color));
|
|
269
|
-
const enter = () => {
|
|
270
|
-
if (entered)
|
|
271
|
-
return;
|
|
272
|
-
terminalOutput.write(`${ansi.alternateEnter}${ansi.cursorHide}`);
|
|
273
|
-
input.setRawMode?.(true);
|
|
274
|
-
input.resume?.();
|
|
275
|
-
entered = true;
|
|
276
|
-
render();
|
|
277
|
-
};
|
|
278
|
-
const leave = () => {
|
|
279
|
-
if (!entered)
|
|
280
|
-
return;
|
|
281
|
-
input.setRawMode?.(false);
|
|
282
|
-
input.pause?.();
|
|
283
|
-
terminalOutput.write(`${ansi.cursorShow}${ansi.alternateExit}`);
|
|
284
|
-
entered = false;
|
|
285
|
-
};
|
|
286
|
-
const resize = () => (serverViewRender ?? render)();
|
|
287
|
-
terminalOutput.on("resize", resize);
|
|
288
|
-
const abort = () => leave();
|
|
289
|
-
process.once("SIGINT", abort);
|
|
290
|
-
process.once("SIGTERM", abort);
|
|
291
|
-
const dashboardOutput = {
|
|
292
|
-
log: (message) => {
|
|
293
|
-
state.log = statusLine(message);
|
|
294
|
-
},
|
|
295
|
-
error: (message) => {
|
|
296
|
-
state.log = statusLine(message);
|
|
297
|
-
},
|
|
298
|
-
};
|
|
299
|
-
async function runExternal(action) {
|
|
300
|
-
leave();
|
|
89
|
+
if (requestedAction) {
|
|
301
90
|
try {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
91
|
+
switch (requestedAction) {
|
|
92
|
+
case "phase-status":
|
|
93
|
+
await phaseCommand({ action: "status", cwd: targetPath });
|
|
94
|
+
break;
|
|
95
|
+
case "phase-next":
|
|
96
|
+
await phaseCommand({ action: "next", cwd: targetPath });
|
|
97
|
+
break;
|
|
98
|
+
case "verify":
|
|
99
|
+
await verifyCommand({ cwd: targetPath });
|
|
100
|
+
break;
|
|
101
|
+
case "server-start":
|
|
102
|
+
await serverManager.startServer(projectName, targetPath);
|
|
103
|
+
break;
|
|
104
|
+
case "server-stop":
|
|
105
|
+
serverManager.stopServer(projectName);
|
|
106
|
+
break;
|
|
107
|
+
case "repomap":
|
|
108
|
+
await repomapCommand({ action: "sync", cwd: targetPath });
|
|
109
|
+
break;
|
|
110
|
+
case "doctor":
|
|
111
|
+
await doctorCommand();
|
|
112
|
+
break;
|
|
305
113
|
}
|
|
306
|
-
else {
|
|
307
|
-
const question = action.kind === "create"
|
|
308
|
-
? "New project name: "
|
|
309
|
-
: "Project path to adopt: ";
|
|
310
|
-
const value = await prompt(input, terminalOutput, question);
|
|
311
|
-
if (!value) {
|
|
312
|
-
state.lastResult = `${action.label} CANCELLED`;
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
const code = action.kind === "create"
|
|
316
|
-
? await newCommand(value, cwd, directOutput)
|
|
317
|
-
: (await findTemplateRoot(cwd))
|
|
318
|
-
? await templateAdoptCommand(value, cwd, directOutput)
|
|
319
|
-
: await adoptCommand(value, cwd, directOutput);
|
|
320
|
-
state.lastResult = `${action.label} ${code === 0 ? "COMPLETE" : "FAILED"}`;
|
|
321
|
-
}
|
|
322
|
-
await prompt(input, terminalOutput, "\nPress Enter to return to Mozole…");
|
|
323
|
-
}
|
|
324
|
-
finally {
|
|
325
|
-
enter();
|
|
326
114
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
state.log = ["Run `mozole template init <folder>` first."];
|
|
333
|
-
render();
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
serverManager ??= new TemplateServerManager(root);
|
|
337
|
-
let servers = await serverManager.refresh();
|
|
338
|
-
let selected = 0;
|
|
339
|
-
let message = "";
|
|
340
|
-
const draw = () => terminalOutput.write(uiServerManagerFrame(root, servers, selected, {
|
|
341
|
-
columns: terminalOutput.columns ?? 100,
|
|
342
|
-
rows: terminalOutput.rows ?? 32,
|
|
343
|
-
}, color, message));
|
|
344
|
-
serverViewRender = draw;
|
|
345
|
-
draw();
|
|
346
|
-
while (true) {
|
|
347
|
-
const key = await waitForKey(input);
|
|
348
|
-
if (key === "\u0003") {
|
|
349
|
-
requestedExitCode = 130;
|
|
350
|
-
serverViewRender = undefined;
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
if (key === "q" || key === "Q" || key === "\u001B") {
|
|
354
|
-
state.lastResult = `${servers.filter((item) => item.status === "running").length} SERVER(S) RUNNING`;
|
|
355
|
-
serverViewRender = undefined;
|
|
356
|
-
render();
|
|
357
|
-
return;
|
|
115
|
+
catch (err) {
|
|
116
|
+
console.log(pc.red("\n┌─ OPERATION ERROR ──────────────────────────────────────────┐"));
|
|
117
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
118
|
+
for (const line of errMsg.split("\n")) {
|
|
119
|
+
console.log(pc.red(`│ [!] ${line}`));
|
|
358
120
|
}
|
|
359
|
-
|
|
360
|
-
if (servers.length)
|
|
361
|
-
selected = (selected + servers.length - 1) % servers.length;
|
|
362
|
-
draw();
|
|
363
|
-
continue;
|
|
364
|
-
}
|
|
365
|
-
if (key === "\u001B[B" || key === "j") {
|
|
366
|
-
if (servers.length)
|
|
367
|
-
selected = (selected + 1) % servers.length;
|
|
368
|
-
draw();
|
|
369
|
-
continue;
|
|
370
|
-
}
|
|
371
|
-
if (key === "x" || key === "X") {
|
|
372
|
-
message = "Stopping all project servers…";
|
|
373
|
-
draw();
|
|
374
|
-
await serverManager.stopAll();
|
|
375
|
-
servers = serverManager.list();
|
|
376
|
-
message = "All project servers stopped.";
|
|
377
|
-
draw();
|
|
378
|
-
continue;
|
|
379
|
-
}
|
|
380
|
-
if (key === "a" || key === "A") {
|
|
381
|
-
for (const server of servers) {
|
|
382
|
-
if (server.status === "running")
|
|
383
|
-
continue;
|
|
384
|
-
message = `Starting ${server.relativePath}…`;
|
|
385
|
-
draw();
|
|
386
|
-
await serverManager.start(server.relativePath);
|
|
387
|
-
servers = serverManager.list();
|
|
388
|
-
}
|
|
389
|
-
message = "All available project servers started.";
|
|
390
|
-
draw();
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
if (key !== "s" && key !== "S" && key !== "\r" && key !== "\n")
|
|
394
|
-
continue;
|
|
395
|
-
const server = servers[selected];
|
|
396
|
-
if (!server)
|
|
397
|
-
continue;
|
|
398
|
-
if (server.status === "running" || server.status === "starting") {
|
|
399
|
-
message = `Stopping ${server.relativePath}…`;
|
|
400
|
-
draw();
|
|
401
|
-
await serverManager.stop(server.relativePath);
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
message = `Starting ${server.relativePath}…`;
|
|
405
|
-
draw();
|
|
406
|
-
await serverManager.start(server.relativePath);
|
|
407
|
-
}
|
|
408
|
-
servers = serverManager.list();
|
|
409
|
-
const current = servers.find((candidate) => candidate.relativePath === server.relativePath);
|
|
410
|
-
message = current?.error
|
|
411
|
-
? `${server.relativePath}: ${current.error}`
|
|
412
|
-
: `${server.relativePath}: ${current?.status ?? "stopped"}`;
|
|
413
|
-
draw();
|
|
121
|
+
console.log(pc.red("└────────────────────────────────────────────────────────────┘"));
|
|
414
122
|
}
|
|
123
|
+
return;
|
|
415
124
|
}
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
if (key === "\u001B[A" || key === "k") {
|
|
425
|
-
state.selected = (state.selected + actions.length - 1) % actions.length;
|
|
426
|
-
render();
|
|
427
|
-
continue;
|
|
428
|
-
}
|
|
429
|
-
if (key === "\u001B[B" || key === "j") {
|
|
430
|
-
state.selected = (state.selected + 1) % actions.length;
|
|
431
|
-
render();
|
|
432
|
-
continue;
|
|
433
|
-
}
|
|
434
|
-
if (key === "r") {
|
|
435
|
-
render();
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
const direct = actions.findIndex((action) => action.key === key);
|
|
439
|
-
if (direct >= 0)
|
|
440
|
-
state.selected = direct;
|
|
441
|
-
if (key !== "\r" && key !== "\n" && direct < 0)
|
|
442
|
-
continue;
|
|
443
|
-
const action = actions[state.selected];
|
|
444
|
-
if (!action)
|
|
445
|
-
continue;
|
|
446
|
-
if (action.kind === "exit")
|
|
447
|
-
return 0;
|
|
448
|
-
if (action.kind === "servers") {
|
|
449
|
-
await runServerManager();
|
|
450
|
-
if (requestedExitCode !== undefined)
|
|
451
|
-
return requestedExitCode;
|
|
452
|
-
continue;
|
|
453
|
-
}
|
|
454
|
-
if (action.kind !== "command") {
|
|
455
|
-
await runExternal(action);
|
|
456
|
-
continue;
|
|
457
|
-
}
|
|
458
|
-
state.busy = action.label;
|
|
459
|
-
delete state.progress;
|
|
460
|
-
render();
|
|
461
|
-
const code = await action.run?.(cwd, dashboardOutput, {
|
|
462
|
-
onProgress: (prog) => {
|
|
463
|
-
state.progress = prog;
|
|
464
|
-
render();
|
|
465
|
-
},
|
|
466
|
-
});
|
|
467
|
-
delete state.busy;
|
|
468
|
-
delete state.progress;
|
|
469
|
-
state.lastResult = `${action.label} ${code === 0 ? "COMPLETE" : "FAILED"}`;
|
|
470
|
-
render();
|
|
125
|
+
// Fullscreen Ink TUI Mode (Alternate Screen Buffer)
|
|
126
|
+
const isTTY = Boolean(process.stdout.isTTY);
|
|
127
|
+
if (isTTY) {
|
|
128
|
+
process.stdout.write("\x1b[?1049h\x1b[H");
|
|
129
|
+
}
|
|
130
|
+
const restore = () => {
|
|
131
|
+
if (isTTY) {
|
|
132
|
+
process.stdout.write("\x1b[?1049l\x1b[?25h");
|
|
471
133
|
}
|
|
134
|
+
};
|
|
135
|
+
process.once("SIGINT", restore);
|
|
136
|
+
process.once("SIGTERM", restore);
|
|
137
|
+
try {
|
|
138
|
+
const instance = render(React.createElement(CockpitApp, {
|
|
139
|
+
projectRoot: targetPath,
|
|
140
|
+
activeProject: projectName,
|
|
141
|
+
protoRoot,
|
|
142
|
+
projects: discoveredProjects,
|
|
143
|
+
initialTab,
|
|
144
|
+
onExit: restore,
|
|
145
|
+
}));
|
|
146
|
+
await instance.waitUntilExit();
|
|
472
147
|
}
|
|
473
148
|
finally {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
process.removeListener("SIGINT", abort);
|
|
478
|
-
process.removeListener("SIGTERM", abort);
|
|
149
|
+
restore();
|
|
150
|
+
process.removeListener("SIGINT", restore);
|
|
151
|
+
process.removeListener("SIGTERM", restore);
|
|
479
152
|
}
|
|
480
153
|
}
|
|
481
154
|
//# sourceMappingURL=ui.js.map
|