@husar.ai/cli 0.4.2 → 0.4.4
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/dist/auth/login.js +1 -1
- package/dist/auth/login.js.map +1 -1
- package/dist/cli.js +132 -54
- package/dist/cli.js.map +1 -1
- package/dist/functions/create.d.ts +1 -1
- package/dist/functions/create.js +286 -152
- package/dist/functions/create.js.map +1 -1
- package/dist/ui.d.ts +51 -0
- package/dist/ui.js +181 -0
- package/dist/ui.js.map +1 -0
- package/package.json +9 -5
- package/src/auth/login.ts +3 -3
- package/src/cli.ts +182 -60
- package/src/functions/create.ts +354 -190
- package/src/ui.ts +260 -0
package/dist/ui.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import boxen from 'boxen';
|
|
3
|
+
import ora, { Ora } from 'ora';
|
|
4
|
+
import logSymbols from 'log-symbols';
|
|
5
|
+
import Enquirer from 'enquirer';
|
|
6
|
+
export declare const theme: {
|
|
7
|
+
primary: import("chalk").ChalkInstance;
|
|
8
|
+
secondary: import("chalk").ChalkInstance;
|
|
9
|
+
success: import("chalk").ChalkInstance;
|
|
10
|
+
warning: import("chalk").ChalkInstance;
|
|
11
|
+
error: import("chalk").ChalkInstance;
|
|
12
|
+
info: import("chalk").ChalkInstance;
|
|
13
|
+
muted: import("chalk").ChalkInstance;
|
|
14
|
+
bold: import("chalk").ChalkInstance;
|
|
15
|
+
dim: import("chalk").ChalkInstance;
|
|
16
|
+
link: import("chalk").ChalkInstance;
|
|
17
|
+
};
|
|
18
|
+
export declare function printBanner(subtitle?: string): void;
|
|
19
|
+
export declare function printCompactBanner(): void;
|
|
20
|
+
export declare function box(content: string, title?: string): void;
|
|
21
|
+
export declare function successBox(content: string, title?: string): void;
|
|
22
|
+
export declare function errorBox(content: string, title?: string): void;
|
|
23
|
+
export declare function infoBox(content: string, title?: string): void;
|
|
24
|
+
export declare function spinner(text: string): Ora;
|
|
25
|
+
export declare const log: {
|
|
26
|
+
success: (message: string) => void;
|
|
27
|
+
error: (message: string) => void;
|
|
28
|
+
warning: (message: string) => void;
|
|
29
|
+
info: (message: string) => void;
|
|
30
|
+
step: (message: string) => void;
|
|
31
|
+
substep: (message: string) => void;
|
|
32
|
+
done: (message: string) => void;
|
|
33
|
+
fail: (message: string) => void;
|
|
34
|
+
dim: (message: string) => void;
|
|
35
|
+
blank: () => void;
|
|
36
|
+
};
|
|
37
|
+
interface SelectChoice {
|
|
38
|
+
name: string;
|
|
39
|
+
message: string;
|
|
40
|
+
hint?: string;
|
|
41
|
+
}
|
|
42
|
+
export declare function select<T extends string>(message: string, choices: SelectChoice[]): Promise<T>;
|
|
43
|
+
export declare function input(message: string, defaultValue?: string): Promise<string>;
|
|
44
|
+
export declare function confirm(message: string, defaultValue?: boolean): Promise<boolean>;
|
|
45
|
+
export declare function password(message: string): Promise<string>;
|
|
46
|
+
export declare function list(items: string[], title?: string): void;
|
|
47
|
+
export declare function keyValue(pairs: Record<string, string | undefined>, title?: string): void;
|
|
48
|
+
export declare function divider(char?: string, length?: number): void;
|
|
49
|
+
export declare function header(text: string): void;
|
|
50
|
+
export declare function nextSteps(steps: string[], title?: string): void;
|
|
51
|
+
export { chalk, boxen, ora, logSymbols, Enquirer };
|
package/dist/ui.js
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import boxen from 'boxen';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import logSymbols from 'log-symbols';
|
|
5
|
+
import Enquirer from 'enquirer';
|
|
6
|
+
export const theme = {
|
|
7
|
+
primary: chalk.hex('#6366f1'),
|
|
8
|
+
secondary: chalk.hex('#8b5cf6'),
|
|
9
|
+
success: chalk.hex('#22c55e'),
|
|
10
|
+
warning: chalk.hex('#f59e0b'),
|
|
11
|
+
error: chalk.hex('#ef4444'),
|
|
12
|
+
info: chalk.hex('#3b82f6'),
|
|
13
|
+
muted: chalk.gray,
|
|
14
|
+
bold: chalk.bold,
|
|
15
|
+
dim: chalk.dim,
|
|
16
|
+
link: chalk.cyan.underline,
|
|
17
|
+
};
|
|
18
|
+
const HUSAR_LOGO = `
|
|
19
|
+
██╗ ██╗██╗ ██╗███████╗ █████╗ ██████╔╗
|
|
20
|
+
██║ ██║██║ ██║██╔════╝██╔══██╗██╔══██╗
|
|
21
|
+
███████║██║ ██║███████╗███████║██████╔╝
|
|
22
|
+
██╔══██║██║ ██║╚════██║██╔══██║██╔══██╗
|
|
23
|
+
██║ ██║╚██████╔╝███████║██║ ██║██║ ██║
|
|
24
|
+
╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝`;
|
|
25
|
+
export function printBanner(subtitle) {
|
|
26
|
+
const gradient = HUSAR_LOGO.split('\n')
|
|
27
|
+
.map((line, i) => {
|
|
28
|
+
const colors = ['#818cf8', '#8b5cf6', '#a855f7', '#c084fc', '#d8b4fe', '#e9d5ff'];
|
|
29
|
+
return chalk.hex(colors[i % colors.length])(line);
|
|
30
|
+
})
|
|
31
|
+
.join('\n');
|
|
32
|
+
console.log(gradient);
|
|
33
|
+
console.log();
|
|
34
|
+
console.log(theme.muted(' Headless CMS for Modern Developers'));
|
|
35
|
+
if (subtitle) {
|
|
36
|
+
console.log(theme.primary(` ${subtitle}`));
|
|
37
|
+
}
|
|
38
|
+
console.log();
|
|
39
|
+
}
|
|
40
|
+
export function printCompactBanner() {
|
|
41
|
+
console.log();
|
|
42
|
+
console.log(chalk.bold(theme.primary(' HUSAR.AI')) + theme.muted(' • Headless CMS'));
|
|
43
|
+
console.log();
|
|
44
|
+
}
|
|
45
|
+
export function box(content, title) {
|
|
46
|
+
console.log(boxen(content, {
|
|
47
|
+
padding: 1,
|
|
48
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
49
|
+
borderStyle: 'round',
|
|
50
|
+
borderColor: '#6366f1',
|
|
51
|
+
title: title,
|
|
52
|
+
titleAlignment: 'left',
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
export function successBox(content, title = 'Success') {
|
|
56
|
+
console.log(boxen(content, {
|
|
57
|
+
padding: 1,
|
|
58
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
59
|
+
borderStyle: 'round',
|
|
60
|
+
borderColor: '#22c55e',
|
|
61
|
+
title: `${logSymbols.success} ${title}`,
|
|
62
|
+
titleAlignment: 'left',
|
|
63
|
+
}));
|
|
64
|
+
}
|
|
65
|
+
export function errorBox(content, title = 'Error') {
|
|
66
|
+
console.log(boxen(content, {
|
|
67
|
+
padding: 1,
|
|
68
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
69
|
+
borderStyle: 'round',
|
|
70
|
+
borderColor: '#ef4444',
|
|
71
|
+
title: `${logSymbols.error} ${title}`,
|
|
72
|
+
titleAlignment: 'left',
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
export function infoBox(content, title = 'Info') {
|
|
76
|
+
console.log(boxen(content, {
|
|
77
|
+
padding: 1,
|
|
78
|
+
margin: { top: 1, bottom: 1, left: 0, right: 0 },
|
|
79
|
+
borderStyle: 'round',
|
|
80
|
+
borderColor: '#3b82f6',
|
|
81
|
+
title: `${logSymbols.info} ${title}`,
|
|
82
|
+
titleAlignment: 'left',
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
export function spinner(text) {
|
|
86
|
+
return ora({
|
|
87
|
+
text,
|
|
88
|
+
color: 'magenta',
|
|
89
|
+
spinner: 'dots',
|
|
90
|
+
}).start();
|
|
91
|
+
}
|
|
92
|
+
export const log = {
|
|
93
|
+
success: (message) => console.log(`${logSymbols.success} ${theme.success(message)}`),
|
|
94
|
+
error: (message) => console.log(`${logSymbols.error} ${theme.error(message)}`),
|
|
95
|
+
warning: (message) => console.log(`${logSymbols.warning} ${theme.warning(message)}`),
|
|
96
|
+
info: (message) => console.log(`${logSymbols.info} ${theme.info(message)}`),
|
|
97
|
+
step: (message) => console.log(` ${theme.primary('→')} ${message}`),
|
|
98
|
+
substep: (message) => console.log(` ${theme.muted('•')} ${message}`),
|
|
99
|
+
done: (message) => console.log(` ${theme.success('✓')} ${message}`),
|
|
100
|
+
fail: (message) => console.log(` ${theme.error('✗')} ${message}`),
|
|
101
|
+
dim: (message) => console.log(theme.dim(` ${message}`)),
|
|
102
|
+
blank: () => console.log(),
|
|
103
|
+
};
|
|
104
|
+
const enquirer = new Enquirer();
|
|
105
|
+
export async function select(message, choices) {
|
|
106
|
+
const response = (await enquirer.prompt({
|
|
107
|
+
type: 'select',
|
|
108
|
+
name: 'value',
|
|
109
|
+
message: theme.primary(message),
|
|
110
|
+
choices: choices.map((c) => ({
|
|
111
|
+
name: c.name,
|
|
112
|
+
message: c.message,
|
|
113
|
+
hint: c.hint ? theme.dim(c.hint) : undefined,
|
|
114
|
+
})),
|
|
115
|
+
}));
|
|
116
|
+
return response.value;
|
|
117
|
+
}
|
|
118
|
+
export async function input(message, defaultValue) {
|
|
119
|
+
const response = (await enquirer.prompt({
|
|
120
|
+
type: 'input',
|
|
121
|
+
name: 'value',
|
|
122
|
+
message: theme.primary(message),
|
|
123
|
+
initial: defaultValue,
|
|
124
|
+
}));
|
|
125
|
+
return response.value;
|
|
126
|
+
}
|
|
127
|
+
export async function confirm(message, defaultValue = true) {
|
|
128
|
+
const response = (await enquirer.prompt({
|
|
129
|
+
type: 'confirm',
|
|
130
|
+
name: 'value',
|
|
131
|
+
message: theme.primary(message),
|
|
132
|
+
initial: defaultValue,
|
|
133
|
+
}));
|
|
134
|
+
return response.value;
|
|
135
|
+
}
|
|
136
|
+
export async function password(message) {
|
|
137
|
+
const response = (await enquirer.prompt({
|
|
138
|
+
type: 'password',
|
|
139
|
+
name: 'value',
|
|
140
|
+
message: theme.primary(message),
|
|
141
|
+
}));
|
|
142
|
+
return response.value;
|
|
143
|
+
}
|
|
144
|
+
export function list(items, title) {
|
|
145
|
+
if (title) {
|
|
146
|
+
console.log(chalk.bold(title));
|
|
147
|
+
}
|
|
148
|
+
items.forEach((item) => {
|
|
149
|
+
console.log(` ${theme.primary('•')} ${item}`);
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
export function keyValue(pairs, title) {
|
|
153
|
+
if (title) {
|
|
154
|
+
console.log(chalk.bold(title));
|
|
155
|
+
}
|
|
156
|
+
const maxKeyLen = Math.max(...Object.keys(pairs).map((k) => k.length));
|
|
157
|
+
Object.entries(pairs).forEach(([key, value]) => {
|
|
158
|
+
if (value !== undefined) {
|
|
159
|
+
const paddedKey = key.padEnd(maxKeyLen);
|
|
160
|
+
console.log(` ${theme.muted(paddedKey)} ${value}`);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
export function divider(char = '─', length = 50) {
|
|
165
|
+
console.log(theme.muted(char.repeat(length)));
|
|
166
|
+
}
|
|
167
|
+
export function header(text) {
|
|
168
|
+
console.log();
|
|
169
|
+
console.log(theme.primary(chalk.bold(text)));
|
|
170
|
+
divider();
|
|
171
|
+
}
|
|
172
|
+
export function nextSteps(steps, title = 'Next Steps') {
|
|
173
|
+
console.log();
|
|
174
|
+
console.log(chalk.bold(title));
|
|
175
|
+
steps.forEach((step, i) => {
|
|
176
|
+
console.log(` ${theme.primary(`${i + 1}.`)} ${step}`);
|
|
177
|
+
});
|
|
178
|
+
console.log();
|
|
179
|
+
}
|
|
180
|
+
export { chalk, boxen, ora, logSymbols, Enquirer };
|
|
181
|
+
//# sourceMappingURL=ui.js.map
|
package/dist/ui.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@husar.ai/cli",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli.js",
|
|
@@ -16,13 +16,17 @@
|
|
|
16
16
|
"watch": "tspc --watch"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"graphql": "^16.9.0",
|
|
20
|
-
"@husar.ai/ssr": "^0.4.2",
|
|
21
19
|
"@husar.ai/genai": "^0.4.2",
|
|
20
|
+
"@husar.ai/ssr": "^0.4.2",
|
|
22
21
|
"@modelcontextprotocol/sdk": "^1.17.4",
|
|
22
|
+
"boxen": "^8.0.1",
|
|
23
|
+
"chalk": "^5.6.2",
|
|
23
24
|
"commander": "^11.0.0",
|
|
24
25
|
"config-maker": "^0.0.6",
|
|
26
|
+
"enquirer": "^2.4.1",
|
|
27
|
+
"graphql": "^16.9.0",
|
|
28
|
+
"log-symbols": "^7.0.1",
|
|
29
|
+
"ora": "^9.3.0",
|
|
25
30
|
"zod": "^3.23.8"
|
|
26
|
-
}
|
|
27
|
-
"devDependencies": {}
|
|
31
|
+
}
|
|
28
32
|
}
|
package/src/auth/login.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import { createServer, IncomingMessage, ServerResponse } from 'node:http';
|
|
10
10
|
import { URL } from 'node:url';
|
|
11
11
|
import { randomBytes } from 'node:crypto';
|
|
12
|
+
import { exec } from 'node:child_process';
|
|
12
13
|
import { saveCloudAuth, saveProjectAuth, ProjectAuth, CloudAuth } from './config.js';
|
|
13
14
|
|
|
14
15
|
const DEFAULT_CALLBACK_PORT = 9876;
|
|
@@ -290,7 +291,7 @@ export async function startPanelLoginFlow(
|
|
|
290
291
|
project,
|
|
291
292
|
});
|
|
292
293
|
})
|
|
293
|
-
.catch((err) => {
|
|
294
|
+
.catch((err: Error) => {
|
|
294
295
|
server.close();
|
|
295
296
|
resolve({ success: false, error: err.message });
|
|
296
297
|
});
|
|
@@ -406,7 +407,7 @@ export async function startLoginFlow(options?: { port?: number; timeout?: number
|
|
|
406
407
|
project,
|
|
407
408
|
});
|
|
408
409
|
})
|
|
409
|
-
.catch((err) => {
|
|
410
|
+
.catch((err: Error) => {
|
|
410
411
|
server.close();
|
|
411
412
|
resolve({ success: false, error: err.message });
|
|
412
413
|
});
|
|
@@ -450,7 +451,6 @@ export async function startLoginFlow(options?: { port?: number; timeout?: number
|
|
|
450
451
|
* Open URL in default browser
|
|
451
452
|
*/
|
|
452
453
|
function openBrowser(url: string): void {
|
|
453
|
-
const { exec } = require('node:child_process');
|
|
454
454
|
const platform = process.platform;
|
|
455
455
|
|
|
456
456
|
let command: string;
|
package/src/cli.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/* eslint-disable no-useless-escape */
|
|
3
3
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
4
|
+
|
|
5
|
+
// Suppress punycode deprecation warning
|
|
6
|
+
process.removeAllListeners('warning');
|
|
7
|
+
process.on('warning', (warning) => {
|
|
8
|
+
if (warning.name === 'DeprecationWarning' && warning.message.includes('punycode')) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
console.warn(warning.name, warning.message);
|
|
12
|
+
});
|
|
13
|
+
|
|
4
14
|
import { Command } from 'commander';
|
|
5
|
-
// nofs
|
|
6
15
|
import { ConfigMaker } from 'config-maker';
|
|
7
16
|
import { parser } from './functions/parser.js';
|
|
8
17
|
import { generateCms } from './functions/generate.js';
|
|
@@ -11,85 +20,170 @@ import { HusarConfigType, getAdminToken } from '@/types/config.js';
|
|
|
11
20
|
import { startLoginFlow } from './auth/login.js';
|
|
12
21
|
import { clearAuth, getCurrentUser, getConfigPath, isLoggedIn, readAuthConfig } from './auth/config.js';
|
|
13
22
|
import { runCreateCommand } from './functions/create.js';
|
|
23
|
+
import { printBanner, printCompactBanner, log, successBox, errorBox, infoBox, spinner, theme, chalk } from './ui.js';
|
|
14
24
|
|
|
15
25
|
const config = new ConfigMaker<HusarConfigType>('husar', { decoders: {} });
|
|
16
26
|
|
|
17
27
|
const program = new Command();
|
|
18
28
|
|
|
19
|
-
program
|
|
29
|
+
program
|
|
30
|
+
.name('husar.ai')
|
|
31
|
+
.description(theme.muted('Headless CMS for Modern Developers'))
|
|
32
|
+
.version('0.4.3')
|
|
33
|
+
.hook('preAction', (thisCommand) => {
|
|
34
|
+
// Show compact banner for all commands except 'create' and 'mcp'
|
|
35
|
+
const cmdName = thisCommand.args[0];
|
|
36
|
+
if (cmdName !== 'mcp') {
|
|
37
|
+
printCompactBanner();
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
42
|
+
// GENERATE COMMAND
|
|
43
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
20
44
|
|
|
21
45
|
program
|
|
22
46
|
.command('generate [folderPath]')
|
|
23
|
-
.description('Generate cms structure
|
|
47
|
+
.description('Generate cms/ structure with GraphQL client')
|
|
24
48
|
.action(async (folderPath: string = '.') => {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
const spin = spinner('Loading configuration...');
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const host = await config.getValueOrThrow('host', { saveOnInput: true });
|
|
53
|
+
const conf = config.get();
|
|
54
|
+
const hostEnv = conf.hostEnvironmentVariable;
|
|
55
|
+
const authenticationEnv = conf.authenticationEnvironmentVariable;
|
|
56
|
+
|
|
57
|
+
spin.text = 'Generating CMS client...';
|
|
58
|
+
|
|
59
|
+
await generateCms({
|
|
60
|
+
baseFolder: folderPath,
|
|
61
|
+
host,
|
|
62
|
+
hostEnvironmentVariable: hostEnv,
|
|
63
|
+
authenticationEnvironmentVariable: authenticationEnv,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
spin.succeed('CMS client generated successfully');
|
|
67
|
+
|
|
68
|
+
log.blank();
|
|
69
|
+
log.done(`Generated in ${theme.info(folderPath + '/cms/')}`);
|
|
70
|
+
log.substep('zeus/ - GraphQL Zeus client');
|
|
71
|
+
log.substep('ssr.ts - Server-side client');
|
|
72
|
+
log.substep('react.ts - React components');
|
|
73
|
+
log.substep('host.ts - Host configuration');
|
|
74
|
+
log.blank();
|
|
75
|
+
} catch (err: any) {
|
|
76
|
+
spin.fail('Generation failed');
|
|
77
|
+
log.error(err.message);
|
|
78
|
+
process.exit(1);
|
|
79
|
+
}
|
|
35
80
|
});
|
|
36
81
|
|
|
82
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
83
|
+
// COPY COMMAND
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
85
|
+
|
|
37
86
|
program
|
|
38
87
|
.command('copy <inputFile> <name>')
|
|
39
|
-
.description('AI-parse HTML/JSX and upsert into CMS
|
|
88
|
+
.description('AI-parse HTML/JSX and upsert into CMS')
|
|
40
89
|
.option('-t, --type <type>', 'target type: model|shape', 'shape')
|
|
41
90
|
.action(async (inputFile, name, opts) => {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
91
|
+
const spin = spinner(`Parsing ${inputFile}...`);
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const conf = config.get();
|
|
95
|
+
if (!conf.host) {
|
|
96
|
+
spin.fail('Host not configured');
|
|
97
|
+
errorBox('Please run "husar.ai generate" first to configure your CMS host.');
|
|
98
|
+
process.exit(1);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const adminToken = getAdminToken(conf);
|
|
102
|
+
if (!adminToken) {
|
|
103
|
+
spin.fail('Admin token not configured');
|
|
104
|
+
errorBox(
|
|
105
|
+
'Set HUSAR_ADMIN_TOKEN environment variable,\nor add adminTokenEnv to husar.json',
|
|
106
|
+
'Authentication Required',
|
|
107
|
+
);
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
spin.text = `AI parsing ${inputFile}...`;
|
|
112
|
+
|
|
113
|
+
const result = await parser({
|
|
114
|
+
inputFile,
|
|
115
|
+
opts: { name, type: opts?.type },
|
|
116
|
+
authentication: { HUSAR_MCP_HOST: conf.host, HUSAR_MCP_ADMIN_TOKEN: adminToken },
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (result) {
|
|
120
|
+
spin.succeed(`Parsed and upserted as ${opts?.type || 'shape'}: ${name}`);
|
|
121
|
+
} else {
|
|
122
|
+
spin.fail('Failed to parse file');
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
} catch (err: any) {
|
|
126
|
+
spin.fail('Parse failed');
|
|
127
|
+
log.error(err.message);
|
|
128
|
+
process.exit(1);
|
|
49
129
|
}
|
|
50
|
-
const result = await parser({
|
|
51
|
-
inputFile,
|
|
52
|
-
opts: { name, type: opts?.type },
|
|
53
|
-
authentication: { HUSAR_MCP_HOST: conf.host, HUSAR_MCP_ADMIN_TOKEN: adminToken },
|
|
54
|
-
});
|
|
55
|
-
console.log(result ? 'File parsed and upserted successfully.' : 'Failed to parse and upsert file.');
|
|
56
130
|
});
|
|
57
131
|
|
|
132
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
133
|
+
// MCP COMMAND
|
|
134
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
135
|
+
|
|
58
136
|
program
|
|
59
137
|
.command('mcp')
|
|
60
|
-
.description('
|
|
138
|
+
.description('Start MCP server for AI IDE integration')
|
|
61
139
|
.action(async () => {
|
|
140
|
+
// MCP runs in silent mode - no banner, no output except JSON-RPC
|
|
62
141
|
await startMcpServer();
|
|
63
|
-
// keep process alive
|
|
64
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
65
142
|
await new Promise<void>(() => {});
|
|
66
143
|
});
|
|
67
144
|
|
|
68
|
-
//
|
|
145
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
146
|
+
// AUTH COMMANDS
|
|
147
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
69
148
|
|
|
70
149
|
program
|
|
71
150
|
.command('login')
|
|
72
|
-
.description('Log in to Husar.ai via browser
|
|
151
|
+
.description('Log in to Husar.ai via browser')
|
|
73
152
|
.action(async () => {
|
|
74
153
|
const loggedIn = await isLoggedIn();
|
|
75
154
|
if (loggedIn) {
|
|
76
155
|
const user = await getCurrentUser();
|
|
77
|
-
|
|
78
|
-
|
|
156
|
+
infoBox(
|
|
157
|
+
`Logged in as ${theme.info(user?.email ?? 'unknown')}\n\n` +
|
|
158
|
+
`Run ${theme.muted('husar.ai logout')} to switch accounts.`,
|
|
159
|
+
'Already Authenticated',
|
|
160
|
+
);
|
|
79
161
|
return;
|
|
80
162
|
}
|
|
81
163
|
|
|
82
|
-
|
|
83
|
-
const result = await startLoginFlow();
|
|
164
|
+
const spin = spinner('Opening browser for authentication...');
|
|
84
165
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
166
|
+
try {
|
|
167
|
+
const result = await startLoginFlow();
|
|
168
|
+
|
|
169
|
+
if (result.success) {
|
|
170
|
+
spin.succeed('Authentication successful');
|
|
171
|
+
|
|
172
|
+
const details = [`Email: ${theme.info(result.email ?? 'unknown')}`];
|
|
173
|
+
if (result.project) {
|
|
174
|
+
details.push(`Project: ${theme.info(result.project.projectName)}`);
|
|
175
|
+
details.push(`Host: ${theme.muted(result.project.host)}`);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
successBox(details.join('\n'), 'Logged In');
|
|
179
|
+
} else {
|
|
180
|
+
spin.fail('Authentication failed');
|
|
181
|
+
errorBox(result.error ?? 'Unknown error');
|
|
182
|
+
process.exit(1);
|
|
90
183
|
}
|
|
91
|
-
}
|
|
92
|
-
|
|
184
|
+
} catch (err: any) {
|
|
185
|
+
spin.fail('Login failed');
|
|
186
|
+
log.error(err.message);
|
|
93
187
|
process.exit(1);
|
|
94
188
|
}
|
|
95
189
|
});
|
|
@@ -100,57 +194,85 @@ program
|
|
|
100
194
|
.action(async () => {
|
|
101
195
|
const loggedIn = await isLoggedIn();
|
|
102
196
|
if (!loggedIn) {
|
|
103
|
-
|
|
197
|
+
log.info('Not logged in.');
|
|
104
198
|
return;
|
|
105
199
|
}
|
|
106
200
|
|
|
107
201
|
const user = await getCurrentUser();
|
|
202
|
+
const spin = spinner('Clearing credentials...');
|
|
203
|
+
|
|
108
204
|
await clearAuth();
|
|
109
|
-
|
|
110
|
-
|
|
205
|
+
|
|
206
|
+
spin.succeed('Logged out successfully');
|
|
207
|
+
log.dim(`Was: ${user?.email ?? 'unknown'}`);
|
|
208
|
+
log.dim(`Cleared: ${getConfigPath()}`);
|
|
111
209
|
});
|
|
112
210
|
|
|
113
211
|
program
|
|
114
212
|
.command('whoami')
|
|
115
|
-
.description('Display current
|
|
213
|
+
.description('Display current user and connected projects')
|
|
116
214
|
.action(async () => {
|
|
117
215
|
const loggedIn = await isLoggedIn();
|
|
118
216
|
if (!loggedIn) {
|
|
119
|
-
|
|
217
|
+
infoBox(`Not logged in.\n\nRun ${theme.info('husar.ai login')} to authenticate.`, 'Authentication Status');
|
|
120
218
|
return;
|
|
121
219
|
}
|
|
122
220
|
|
|
123
221
|
const user = await getCurrentUser();
|
|
124
222
|
const authConfig = await readAuthConfig();
|
|
125
223
|
|
|
126
|
-
console.log(
|
|
224
|
+
console.log();
|
|
225
|
+
console.log(chalk.bold(' Account'));
|
|
226
|
+
console.log(` ${theme.muted('Email')} ${theme.info(user?.email ?? 'unknown')}`);
|
|
227
|
+
console.log(` ${theme.muted('Config')} ${getConfigPath()}`);
|
|
127
228
|
|
|
128
229
|
if (authConfig.projects && Object.keys(authConfig.projects).length > 0) {
|
|
129
|
-
console.log(
|
|
230
|
+
console.log();
|
|
231
|
+
console.log(chalk.bold(' Connected Projects'));
|
|
130
232
|
for (const [name, project] of Object.entries(authConfig.projects)) {
|
|
131
|
-
console.log(`
|
|
132
|
-
console.log(`
|
|
233
|
+
console.log(` ${theme.primary('•')} ${name}`);
|
|
234
|
+
console.log(` ${theme.muted(project.host)}`);
|
|
133
235
|
}
|
|
134
|
-
} else {
|
|
135
|
-
console.log('\nNo projects connected. Run "husar create" to set up a new project.');
|
|
136
236
|
}
|
|
137
237
|
|
|
138
|
-
console.log(
|
|
238
|
+
console.log();
|
|
139
239
|
});
|
|
140
240
|
|
|
141
|
-
//
|
|
241
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
242
|
+
// CREATE COMMAND
|
|
243
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
142
244
|
|
|
143
245
|
program
|
|
144
246
|
.command('create [projectName]')
|
|
145
|
-
.description('Create a new project with Husar CMS
|
|
146
|
-
.option('-f, --framework <framework>', '
|
|
147
|
-
.option('--skip-install', 'Skip
|
|
247
|
+
.description('Create a new project with Husar CMS')
|
|
248
|
+
.option('-f, --framework <framework>', 'nextjs, vite, or clean', 'nextjs')
|
|
249
|
+
.option('--skip-install', 'Skip npm install after scaffolding')
|
|
148
250
|
.action(async (projectName: string | undefined, opts: { framework: string; skipInstall?: boolean }) => {
|
|
251
|
+
// Create command has its own banner
|
|
252
|
+
printBanner('Project Creator');
|
|
253
|
+
|
|
149
254
|
await runCreateCommand({
|
|
150
255
|
projectName,
|
|
151
|
-
framework: opts.framework as 'nextjs' | 'vite',
|
|
256
|
+
framework: opts.framework as 'nextjs' | 'vite' | 'clean',
|
|
152
257
|
skipInstall: opts.skipInstall,
|
|
153
258
|
});
|
|
154
259
|
});
|
|
155
260
|
|
|
261
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
262
|
+
// HELP CUSTOMIZATION
|
|
263
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
264
|
+
|
|
265
|
+
program.addHelpText('before', () => {
|
|
266
|
+
printBanner();
|
|
267
|
+
return '';
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
program.addHelpText('after', () => {
|
|
271
|
+
console.log();
|
|
272
|
+
console.log(theme.muted(' Documentation:'), theme.link('https://docs.husar.ai'));
|
|
273
|
+
console.log(theme.muted(' Discord:'), theme.link('https://discord.gg/husar'));
|
|
274
|
+
console.log();
|
|
275
|
+
return '';
|
|
276
|
+
});
|
|
277
|
+
|
|
156
278
|
program.parse(process.argv);
|