archbyte 0.3.4 → 0.3.5
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/bin/archbyte.js +8 -9
- package/dist/cli/auth.d.ts +3 -0
- package/dist/cli/auth.js +71 -42
- package/dist/cli/config.d.ts +1 -0
- package/dist/cli/config.js +20 -10
- package/dist/cli/constants.js +4 -1
- package/dist/cli/license-gate.d.ts +1 -1
- package/dist/cli/license-gate.js +3 -2
- package/dist/cli/mcp.js +8 -12
- package/dist/cli/setup.js +87 -29
- package/dist/cli/ui.d.ts +5 -0
- package/dist/cli/ui.js +44 -14
- package/dist/cli/utils.d.ts +23 -0
- package/dist/cli/utils.js +52 -0
- package/dist/server/src/index.js +3 -3
- package/package.json +1 -1
- package/ui/dist/assets/{index-Bdr9FnaA.js → index-BTo0zV5E.js} +13 -13
- package/ui/dist/index.html +1 -1
package/dist/cli/ui.js
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
const BRAILLE_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
3
|
+
// ─── Cursor Safety ───
|
|
4
|
+
// Ensure the terminal cursor is always restored, even on unhandled crashes.
|
|
5
|
+
let cursorHidden = false;
|
|
6
|
+
function hideCursor() {
|
|
7
|
+
if (!cursorHidden) {
|
|
8
|
+
process.stdout.write("\x1b[?25l");
|
|
9
|
+
cursorHidden = true;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function showCursor() {
|
|
13
|
+
if (cursorHidden) {
|
|
14
|
+
process.stdout.write("\x1b[?25h");
|
|
15
|
+
cursorHidden = false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
// Restore cursor on any exit path
|
|
19
|
+
for (const event of ["exit", "SIGINT", "SIGTERM", "uncaughtException", "unhandledRejection"]) {
|
|
20
|
+
process.on(event, () => {
|
|
21
|
+
showCursor();
|
|
22
|
+
});
|
|
23
|
+
}
|
|
3
24
|
/**
|
|
4
25
|
* Animated braille spinner. Falls back to static console.log when not a TTY.
|
|
5
26
|
*/
|
|
@@ -30,6 +51,8 @@ export function spinner(label) {
|
|
|
30
51
|
/**
|
|
31
52
|
* Arrow-key selection menu. Returns the selected index.
|
|
32
53
|
* Non-TTY fallback: returns 0 (first option).
|
|
54
|
+
*
|
|
55
|
+
* Navigation: ↑/↓ arrows to move, Enter to confirm, Ctrl+C to exit.
|
|
33
56
|
*/
|
|
34
57
|
export function select(prompt, options) {
|
|
35
58
|
if (!process.stdout.isTTY || options.length === 0) {
|
|
@@ -45,10 +68,9 @@ export function select(prompt, options) {
|
|
|
45
68
|
stdin.resume();
|
|
46
69
|
stdin.setEncoding("utf8");
|
|
47
70
|
let selected = 0;
|
|
48
|
-
|
|
49
|
-
process.stdout.write("\x1b[?25l");
|
|
71
|
+
hideCursor();
|
|
50
72
|
function render() {
|
|
51
|
-
// Move up to clear previous render
|
|
73
|
+
// Move up to clear previous render
|
|
52
74
|
const lines = options.length + 1; // prompt + options
|
|
53
75
|
process.stdout.write(`\x1b[${lines}A`);
|
|
54
76
|
process.stdout.write(`\x1b[K ${chalk.bold(prompt)}\n`);
|
|
@@ -74,13 +96,13 @@ export function select(prompt, options) {
|
|
|
74
96
|
}
|
|
75
97
|
}
|
|
76
98
|
const onData = (data) => {
|
|
77
|
-
if (data === "\x1b[A") {
|
|
78
|
-
// Up arrow
|
|
99
|
+
if (data === "\x1b[A" || data === "k") {
|
|
100
|
+
// Up arrow or k (vim-style)
|
|
79
101
|
selected = (selected - 1 + options.length) % options.length;
|
|
80
102
|
render();
|
|
81
103
|
}
|
|
82
|
-
else if (data === "\x1b[B") {
|
|
83
|
-
// Down arrow
|
|
104
|
+
else if (data === "\x1b[B" || data === "j") {
|
|
105
|
+
// Down arrow or j (vim-style)
|
|
84
106
|
selected = (selected + 1) % options.length;
|
|
85
107
|
render();
|
|
86
108
|
}
|
|
@@ -89,18 +111,19 @@ export function select(prompt, options) {
|
|
|
89
111
|
cleanup();
|
|
90
112
|
resolve(selected);
|
|
91
113
|
}
|
|
92
|
-
else if (data === "\x03"
|
|
93
|
-
// Ctrl+C
|
|
114
|
+
else if (data === "\x03") {
|
|
115
|
+
// Ctrl+C only — clean exit
|
|
94
116
|
cleanup();
|
|
117
|
+
process.stdout.write("\n");
|
|
95
118
|
process.exit(0);
|
|
96
119
|
}
|
|
120
|
+
// All other keys (including q, arrows, etc.) are ignored
|
|
97
121
|
};
|
|
98
122
|
function cleanup() {
|
|
99
123
|
stdin.removeListener("data", onData);
|
|
100
124
|
stdin.setRawMode(wasRaw ?? false);
|
|
101
125
|
stdin.pause();
|
|
102
|
-
|
|
103
|
-
process.stdout.write("\x1b[?25h");
|
|
126
|
+
showCursor();
|
|
104
127
|
}
|
|
105
128
|
stdin.on("data", onData);
|
|
106
129
|
});
|
|
@@ -152,6 +175,9 @@ export function progressBar(totalSteps) {
|
|
|
152
175
|
/**
|
|
153
176
|
* Y/n confirmation prompt. Returns true for y/Enter, false for n.
|
|
154
177
|
* Non-TTY fallback: returns true.
|
|
178
|
+
*
|
|
179
|
+
* Only responds to explicit y/n/Enter/Ctrl+C. Ignores escape sequences
|
|
180
|
+
* (arrow keys, etc.) to prevent accidental confirmation.
|
|
155
181
|
*/
|
|
156
182
|
export function confirm(prompt) {
|
|
157
183
|
if (!process.stdout.isTTY) {
|
|
@@ -166,6 +192,9 @@ export function confirm(prompt) {
|
|
|
166
192
|
stdin.resume();
|
|
167
193
|
stdin.setEncoding("utf8");
|
|
168
194
|
const onData = (data) => {
|
|
195
|
+
// Ignore escape sequences (arrow keys, function keys, etc.)
|
|
196
|
+
if (data.startsWith("\x1b"))
|
|
197
|
+
return;
|
|
169
198
|
stdin.removeListener("data", onData);
|
|
170
199
|
stdin.setRawMode(wasRaw ?? false);
|
|
171
200
|
stdin.pause();
|
|
@@ -173,15 +202,16 @@ export function confirm(prompt) {
|
|
|
173
202
|
process.stdout.write("n\n");
|
|
174
203
|
resolve(false);
|
|
175
204
|
}
|
|
176
|
-
else if (data === "\x03"
|
|
205
|
+
else if (data === "\x03") {
|
|
206
|
+
// Ctrl+C only
|
|
177
207
|
process.stdout.write("\n");
|
|
178
208
|
process.exit(0);
|
|
179
209
|
}
|
|
180
|
-
else {
|
|
181
|
-
// y, Y, Enter — all true
|
|
210
|
+
else if (data === "y" || data === "Y" || data === "\r" || data === "\n") {
|
|
182
211
|
process.stdout.write("y\n");
|
|
183
212
|
resolve(true);
|
|
184
213
|
}
|
|
214
|
+
// Ignore any other single keypresses — wait for y/n/Enter
|
|
185
215
|
};
|
|
186
216
|
stdin.on("data", onData);
|
|
187
217
|
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check if a command exists in the system PATH.
|
|
3
|
+
* Uses `which` (Unix) or `where` (Windows).
|
|
4
|
+
*/
|
|
5
|
+
export declare function isInPath(cmd: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Mask an API key for safe display.
|
|
8
|
+
* Shows first 6 + last 4 characters: `sk-ant-...7x9z`
|
|
9
|
+
*/
|
|
10
|
+
export declare function maskKey(key: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Whether stdin/stdout support interactive terminal features.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isTTY(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the user's home directory, or throw if unresolvable.
|
|
17
|
+
* Prefers $HOME (Unix), falls back to $USERPROFILE (Windows).
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveHome(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Basic email format check. Not exhaustive — just catches obvious typos.
|
|
22
|
+
*/
|
|
23
|
+
export declare function isValidEmail(email: string): boolean;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Shared CLI utilities.
|
|
2
|
+
// Single source of truth for helpers used across multiple CLI modules.
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
/**
|
|
5
|
+
* Check if a command exists in the system PATH.
|
|
6
|
+
* Uses `which` (Unix) or `where` (Windows).
|
|
7
|
+
*/
|
|
8
|
+
export function isInPath(cmd) {
|
|
9
|
+
try {
|
|
10
|
+
// Only allow simple command names (no spaces, slashes, or shell metacharacters)
|
|
11
|
+
if (!/^[a-zA-Z0-9._-]+$/.test(cmd))
|
|
12
|
+
return false;
|
|
13
|
+
const which = process.platform === "win32" ? "where" : "which";
|
|
14
|
+
execSync(`${which} ${cmd}`, { stdio: "ignore" });
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Mask an API key for safe display.
|
|
23
|
+
* Shows first 6 + last 4 characters: `sk-ant-...7x9z`
|
|
24
|
+
*/
|
|
25
|
+
export function maskKey(key) {
|
|
26
|
+
if (key.length <= 10)
|
|
27
|
+
return "****";
|
|
28
|
+
return key.slice(0, 6) + "..." + key.slice(-4);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Whether stdin/stdout support interactive terminal features.
|
|
32
|
+
*/
|
|
33
|
+
export function isTTY() {
|
|
34
|
+
return Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the user's home directory, or throw if unresolvable.
|
|
38
|
+
* Prefers $HOME (Unix), falls back to $USERPROFILE (Windows).
|
|
39
|
+
*/
|
|
40
|
+
export function resolveHome() {
|
|
41
|
+
const home = process.env.HOME ?? process.env.USERPROFILE;
|
|
42
|
+
if (!home) {
|
|
43
|
+
throw new Error("Cannot determine home directory. Set the HOME environment variable.");
|
|
44
|
+
}
|
|
45
|
+
return home;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Basic email format check. Not exhaustive — just catches obvious typos.
|
|
49
|
+
*/
|
|
50
|
+
export function isValidEmail(email) {
|
|
51
|
+
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
|
|
52
|
+
}
|
package/dist/server/src/index.js
CHANGED
|
@@ -150,7 +150,7 @@ function createHttpServer() {
|
|
|
150
150
|
// Read current architecture file
|
|
151
151
|
const content = await readFile(config.diagramPath, "utf-8");
|
|
152
152
|
const arch = JSON.parse(content);
|
|
153
|
-
// Apply position/dimension updates
|
|
153
|
+
// Apply position/dimension updates only to moved nodes
|
|
154
154
|
for (const update of updates) {
|
|
155
155
|
const node = arch.nodes.find((n) => n.id === update.id);
|
|
156
156
|
if (node) {
|
|
@@ -158,10 +158,9 @@ function createHttpServer() {
|
|
|
158
158
|
node.y = update.y;
|
|
159
159
|
node.width = update.width;
|
|
160
160
|
node.height = update.height;
|
|
161
|
+
node.userPositioned = true;
|
|
161
162
|
}
|
|
162
163
|
}
|
|
163
|
-
// Mark layout as user-saved so UI uses these positions
|
|
164
|
-
arch.layoutSaved = true;
|
|
165
164
|
// Write back
|
|
166
165
|
await writeFile(config.diagramPath, JSON.stringify(arch, null, 2), "utf-8");
|
|
167
166
|
currentArchitecture = arch;
|
|
@@ -726,6 +725,7 @@ function createHttpServer() {
|
|
|
726
725
|
node.width = update.width;
|
|
727
726
|
if (update.height)
|
|
728
727
|
node.height = update.height;
|
|
728
|
+
node.userPositioned = true;
|
|
729
729
|
}
|
|
730
730
|
}
|
|
731
731
|
// Update timestamp
|