@nitrostack/cli 1.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/README.md +131 -0
- package/dist/commands/build.d.ts +6 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +185 -0
- package/dist/commands/dev.d.ts +7 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +365 -0
- package/dist/commands/generate-types.d.ts +8 -0
- package/dist/commands/generate-types.d.ts.map +1 -0
- package/dist/commands/generate-types.js +219 -0
- package/dist/commands/generate.d.ts +12 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +375 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +324 -0
- package/dist/commands/install.d.ts +10 -0
- package/dist/commands/install.d.ts.map +1 -0
- package/dist/commands/install.js +80 -0
- package/dist/commands/start.d.ts +6 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +70 -0
- package/dist/commands/upgrade.d.ts +10 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +214 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +94 -0
- package/dist/mcp-dev-wrapper.d.ts +15 -0
- package/dist/mcp-dev-wrapper.d.ts.map +1 -0
- package/dist/mcp-dev-wrapper.js +187 -0
- package/dist/ui/branding.d.ts +31 -0
- package/dist/ui/branding.d.ts.map +1 -0
- package/dist/ui/branding.js +136 -0
- package/package.json +69 -0
- package/templates/typescript-oauth/.env.example +27 -0
- package/templates/typescript-oauth/OAUTH_SETUP.md +592 -0
- package/templates/typescript-oauth/README.md +263 -0
- package/templates/typescript-oauth/package.json +29 -0
- package/templates/typescript-oauth/src/app.module.ts +92 -0
- package/templates/typescript-oauth/src/guards/oauth.guard.ts +126 -0
- package/templates/typescript-oauth/src/health/system.health.ts +55 -0
- package/templates/typescript-oauth/src/index.ts +63 -0
- package/templates/typescript-oauth/src/modules/flights/booking.tools.ts +323 -0
- package/templates/typescript-oauth/src/modules/flights/flights.module.ts +14 -0
- package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +228 -0
- package/templates/typescript-oauth/src/modules/flights/flights.resources.ts +215 -0
- package/templates/typescript-oauth/src/modules/flights/flights.tools.ts +457 -0
- package/templates/typescript-oauth/src/services/duffel.service.ts +285 -0
- package/templates/typescript-oauth/src/widgets/app/airport-search/page.tsx +270 -0
- package/templates/typescript-oauth/src/widgets/app/flight-details/page.tsx +261 -0
- package/templates/typescript-oauth/src/widgets/app/flight-search-results/page.tsx +378 -0
- package/templates/typescript-oauth/src/widgets/app/globals.css +167 -0
- package/templates/typescript-oauth/src/widgets/app/layout.tsx +18 -0
- package/templates/typescript-oauth/src/widgets/app/order-cancellation/page.tsx +207 -0
- package/templates/typescript-oauth/src/widgets/app/order-summary/page.tsx +245 -0
- package/templates/typescript-oauth/src/widgets/app/payment-confirmation/page.tsx +152 -0
- package/templates/typescript-oauth/src/widgets/app/seat-selection/page.tsx +486 -0
- package/templates/typescript-oauth/src/widgets/next-env.d.ts +5 -0
- package/templates/typescript-oauth/src/widgets/next.config.js +45 -0
- package/templates/typescript-oauth/src/widgets/package-lock.json +4493 -0
- package/templates/typescript-oauth/src/widgets/package.json +24 -0
- package/templates/typescript-oauth/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-oauth/src/widgets/widget-manifest.json +395 -0
- package/templates/typescript-oauth/tsconfig.json +23 -0
- package/templates/typescript-pizzaz/README.md +252 -0
- package/templates/typescript-pizzaz/package.json +34 -0
- package/templates/typescript-pizzaz/src/app.module.ts +28 -0
- package/templates/typescript-pizzaz/src/index.ts +30 -0
- package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.data.ts +106 -0
- package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.module.ts +11 -0
- package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.service.ts +60 -0
- package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.tools.ts +197 -0
- package/templates/typescript-pizzaz/src/widgets/app/layout.tsx +18 -0
- package/templates/typescript-pizzaz/src/widgets/app/pizza-list/page.tsx +272 -0
- package/templates/typescript-pizzaz/src/widgets/app/pizza-map/page.tsx +216 -0
- package/templates/typescript-pizzaz/src/widgets/app/pizza-shop/page.tsx +374 -0
- package/templates/typescript-pizzaz/src/widgets/components/CompactShopCard.tsx +144 -0
- package/templates/typescript-pizzaz/src/widgets/components/PizzaCard.tsx +191 -0
- package/templates/typescript-pizzaz/src/widgets/next.config.js +45 -0
- package/templates/typescript-pizzaz/src/widgets/package.json +30 -0
- package/templates/typescript-pizzaz/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-pizzaz/src/widgets/widget-manifest.json +253 -0
- package/templates/typescript-pizzaz/tsconfig.json +30 -0
- package/templates/typescript-starter/README.md +320 -0
- package/templates/typescript-starter/package.json +25 -0
- package/templates/typescript-starter/src/app.module.ts +34 -0
- package/templates/typescript-starter/src/health/system.health.ts +55 -0
- package/templates/typescript-starter/src/index.ts +29 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.module.ts +12 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.prompts.ts +73 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.resources.ts +59 -0
- package/templates/typescript-starter/src/modules/calculator/calculator.tools.ts +166 -0
- package/templates/typescript-starter/src/widgets/app/calculator-result/page.tsx +180 -0
- package/templates/typescript-starter/src/widgets/app/layout.tsx +18 -0
- package/templates/typescript-starter/src/widgets/next.config.js +45 -0
- package/templates/typescript-starter/src/widgets/package.json +24 -0
- package/templates/typescript-starter/src/widgets/tsconfig.json +28 -0
- package/templates/typescript-starter/src/widgets/widget-manifest.json +48 -0
- package/templates/typescript-starter/tsconfig.json +23 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from 'child_process';
|
|
3
|
+
import chokidar from 'chokidar';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import { fileURLToPath } from 'url';
|
|
6
|
+
import http from 'http';
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
/**
|
|
10
|
+
* MCP Server Hot Reload Wrapper
|
|
11
|
+
*
|
|
12
|
+
* This wrapper:
|
|
13
|
+
* 1. Starts the MCP server as a child process
|
|
14
|
+
* 2. Watches for changes in the dist/ directory
|
|
15
|
+
* 3. Restarts the server when changes are detected
|
|
16
|
+
* 4. Maintains stdio connection for Studio
|
|
17
|
+
* 5. Captures stderr from the MCP server and streams it over an SSE endpoint
|
|
18
|
+
*
|
|
19
|
+
* IMPORTANT: All logs go to stderr to avoid corrupting MCP JSON-RPC on stdout
|
|
20
|
+
*/
|
|
21
|
+
export async function run() {
|
|
22
|
+
const distPath = process.argv[2];
|
|
23
|
+
if (!distPath) {
|
|
24
|
+
console.error('Usage: mcp-dev-wrapper <path-to-dist/index.js>');
|
|
25
|
+
process.exit(1);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const projectRoot = path.resolve(distPath, '../..');
|
|
29
|
+
const watchPath = path.join(projectRoot, 'dist');
|
|
30
|
+
let mcpProcess = null;
|
|
31
|
+
let isRestarting = false;
|
|
32
|
+
let consecutiveFailures = 0;
|
|
33
|
+
const maxConsecutiveFailures = 3;
|
|
34
|
+
let restartTimeout = null;
|
|
35
|
+
let sseClient = null;
|
|
36
|
+
const logCache = [];
|
|
37
|
+
// Create an HTTP server for SSE log streaming
|
|
38
|
+
const logServer = http.createServer((req, res) => {
|
|
39
|
+
if (req.url === '/mcp-logs') {
|
|
40
|
+
res.writeHead(200, {
|
|
41
|
+
'Content-Type': 'text/event-stream',
|
|
42
|
+
'Cache-Control': 'no-cache',
|
|
43
|
+
'Connection': 'keep-alive',
|
|
44
|
+
'Access-Control-Allow-Origin': '*', // Allow all origins
|
|
45
|
+
});
|
|
46
|
+
sseClient = res;
|
|
47
|
+
// Send cached logs
|
|
48
|
+
logCache.forEach(log => {
|
|
49
|
+
sseClient?.write(`data: ${log}\n\n`);
|
|
50
|
+
});
|
|
51
|
+
req.on('close', () => {
|
|
52
|
+
sseClient = null;
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
res.writeHead(404);
|
|
57
|
+
res.end();
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
const logServerPort = parseInt(process.env.MCP_LOG_PORT || '3004');
|
|
61
|
+
logServer.listen(logServerPort, () => {
|
|
62
|
+
console.error(`[NitroStack Hot Reload] Log streaming server running at http://localhost:${logServerPort}/mcp-logs`);
|
|
63
|
+
});
|
|
64
|
+
function startMCPServer() {
|
|
65
|
+
if (isRestarting)
|
|
66
|
+
return;
|
|
67
|
+
console.error(`[DEBUG] mcp-dev-wrapper.ts: MCP_SERVER_PORT is ${process.env.MCP_SERVER_PORT}`);
|
|
68
|
+
const mcpEnv = {
|
|
69
|
+
...process.env,
|
|
70
|
+
NODE_ENV: 'development',
|
|
71
|
+
PORT: process.env.MCP_SERVER_PORT,
|
|
72
|
+
};
|
|
73
|
+
mcpProcess = spawn('node', [distPath], {
|
|
74
|
+
stdio: ['inherit', 'inherit', 'pipe'], // Pipe stderr to capture logs
|
|
75
|
+
env: mcpEnv,
|
|
76
|
+
});
|
|
77
|
+
if (!mcpProcess)
|
|
78
|
+
return;
|
|
79
|
+
let buffer = '';
|
|
80
|
+
if (mcpProcess.stderr) {
|
|
81
|
+
mcpProcess.stderr.on('data', (data) => {
|
|
82
|
+
buffer += data.toString();
|
|
83
|
+
let boundary = buffer.indexOf('\n');
|
|
84
|
+
while (boundary !== -1) {
|
|
85
|
+
const line = buffer.substring(0, boundary);
|
|
86
|
+
buffer = buffer.substring(boundary + 1);
|
|
87
|
+
// Forward to the console for local debugging
|
|
88
|
+
process.stderr.write(line + '\n');
|
|
89
|
+
if (line.startsWith('NITRO_LOG::')) {
|
|
90
|
+
const logJson = line.substring('NITRO_LOG::'.length);
|
|
91
|
+
logCache.push(logJson);
|
|
92
|
+
if (sseClient) {
|
|
93
|
+
sseClient.write(`data: ${logJson}\n\n`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
boundary = buffer.indexOf('\n');
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
mcpProcess.on('exit', (code) => {
|
|
101
|
+
if (!isRestarting) {
|
|
102
|
+
if (code !== 0) {
|
|
103
|
+
consecutiveFailures++;
|
|
104
|
+
if (consecutiveFailures >= maxConsecutiveFailures) {
|
|
105
|
+
console.error(`[NitroStack Hot Reload] Server failed ${maxConsecutiveFailures} times in a row. Giving up.`);
|
|
106
|
+
console.error('[NitroStack Hot Reload] Check your configuration and try again.');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
console.error(`[NitroStack Hot Reload] Server exited with code ${code} (attempt ${consecutiveFailures}/${maxConsecutiveFailures}), restarting...`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
consecutiveFailures = 0;
|
|
113
|
+
console.error(`[NitroStack Hot Reload] Server exited with code ${code}, restarting...`);
|
|
114
|
+
}
|
|
115
|
+
mcpProcess = null;
|
|
116
|
+
setTimeout(startMCPServer, 1000);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
mcpProcess.on('error', (err) => {
|
|
120
|
+
console.error('[NitroStack Hot Reload] Server error:', err);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
function restartMCPServer() {
|
|
124
|
+
if (restartTimeout) {
|
|
125
|
+
clearTimeout(restartTimeout);
|
|
126
|
+
}
|
|
127
|
+
restartTimeout = setTimeout(() => {
|
|
128
|
+
if (isRestarting)
|
|
129
|
+
return;
|
|
130
|
+
isRestarting = true;
|
|
131
|
+
if (mcpProcess) {
|
|
132
|
+
console.error('[NitroStack Hot Reload] Changes detected, restarting server...');
|
|
133
|
+
mcpProcess.kill('SIGTERM');
|
|
134
|
+
setTimeout(() => {
|
|
135
|
+
mcpProcess = null;
|
|
136
|
+
isRestarting = false;
|
|
137
|
+
startMCPServer();
|
|
138
|
+
console.error('[NitroStack Hot Reload] Server restarted successfully');
|
|
139
|
+
}, 1500);
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
isRestarting = false;
|
|
143
|
+
startMCPServer();
|
|
144
|
+
}
|
|
145
|
+
}, 1000);
|
|
146
|
+
}
|
|
147
|
+
// Start the server initially
|
|
148
|
+
startMCPServer();
|
|
149
|
+
const watcher = chokidar.watch(watchPath, {
|
|
150
|
+
ignoreInitial: true,
|
|
151
|
+
persistent: true,
|
|
152
|
+
awaitWriteFinish: {
|
|
153
|
+
stabilityThreshold: 100,
|
|
154
|
+
pollInterval: 50,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
watcher.on('change', (filepath) => {
|
|
158
|
+
console.error(`[NitroStack Hot Reload] File changed: ${path.relative(projectRoot, filepath)}`);
|
|
159
|
+
restartMCPServer();
|
|
160
|
+
});
|
|
161
|
+
watcher.on('add', (filepath) => {
|
|
162
|
+
console.error(`[NitroStack Hot Reload] File added: ${path.relative(projectRoot, filepath)}`);
|
|
163
|
+
restartMCPServer();
|
|
164
|
+
});
|
|
165
|
+
const shutdown = () => {
|
|
166
|
+
console.error('[NitroStack Hot Reload] Shutting down...');
|
|
167
|
+
watcher.close();
|
|
168
|
+
if (mcpProcess) {
|
|
169
|
+
mcpProcess.kill('SIGTERM');
|
|
170
|
+
}
|
|
171
|
+
logServer.close();
|
|
172
|
+
process.exit(0);
|
|
173
|
+
};
|
|
174
|
+
process.on('SIGINT', shutdown);
|
|
175
|
+
process.on('SIGTERM', shutdown);
|
|
176
|
+
}
|
|
177
|
+
// Run if main
|
|
178
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
179
|
+
if (process.argv[1] && (process.argv[1] === currentFilePath ||
|
|
180
|
+
process.argv[1].endsWith('mcp-dev-wrapper.js') ||
|
|
181
|
+
process.argv[1].endsWith('mcp-dev-wrapper.ts') ||
|
|
182
|
+
process.argv[1].endsWith('mcp-dev-wrapper'))) {
|
|
183
|
+
run().catch(err => {
|
|
184
|
+
console.error(err);
|
|
185
|
+
process.exit(1);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export declare const brand: {
|
|
2
|
+
accent: import("chalk").ChalkInstance;
|
|
3
|
+
accentBold: import("chalk").ChalkInstance;
|
|
4
|
+
accentLight: import("chalk").ChalkInstance;
|
|
5
|
+
accentLighter: import("chalk").ChalkInstance;
|
|
6
|
+
};
|
|
7
|
+
export declare const NITRO_BANNER_FULL: string;
|
|
8
|
+
export declare function createHeader(title: string, subtitle?: string): string;
|
|
9
|
+
export declare function createBox(lines: string[], type?: 'success' | 'error' | 'info' | 'warning'): string;
|
|
10
|
+
export declare function createSuccessBox(title: string, items: string[]): string;
|
|
11
|
+
export declare function createErrorBox(title: string, message: string): string;
|
|
12
|
+
export declare function createInfoBox(items: {
|
|
13
|
+
label: string;
|
|
14
|
+
value: string;
|
|
15
|
+
}[]): string;
|
|
16
|
+
export declare class NitroSpinner {
|
|
17
|
+
private spinner;
|
|
18
|
+
constructor(text: string);
|
|
19
|
+
start(): this;
|
|
20
|
+
update(text: string): this;
|
|
21
|
+
succeed(text?: string): this;
|
|
22
|
+
fail(text?: string): this;
|
|
23
|
+
info(text?: string): this;
|
|
24
|
+
warn(text?: string): this;
|
|
25
|
+
stop(): this;
|
|
26
|
+
}
|
|
27
|
+
export declare function log(message: string, type?: 'success' | 'error' | 'info' | 'warning' | 'dim'): void;
|
|
28
|
+
export declare function divider(): void;
|
|
29
|
+
export declare function spacer(): void;
|
|
30
|
+
export declare function nextSteps(steps: string[]): void;
|
|
31
|
+
//# sourceMappingURL=branding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"branding.d.ts","sourceRoot":"","sources":["../../src/ui/branding.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,KAAK;;;;;CAKjB,CAAC;AAEF,eAAO,MAAM,iBAAiB,QAa7B,CAAC;AAEF,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CASrE;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,IAAI,GAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAkB,GAAG,MAAM,CAoB1G;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAQvE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQrE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,MAAM,CAK/E;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAM;gBAET,IAAI,EAAE,MAAM;IAQxB,KAAK,IAAI,IAAI;IAKb,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAK1B,OAAO,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAK5B,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI;IAKzB,IAAI,IAAI,IAAI;CAIb;AAED,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,KAAc,GAAG,IAAI,CAU1G;AAED,wBAAgB,OAAO,IAAI,IAAI,CAE9B;AAED,wBAAgB,MAAM,IAAI,IAAI,CAE7B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAM/C"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
4
|
+
// NITROSTACK CLI BRANDING
|
|
5
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
6
|
+
const ACCENT = '#FF6B35';
|
|
7
|
+
const ACCENT_LIGHT = '#FF8C42';
|
|
8
|
+
const ACCENT_LIGHTER = '#FFA94D';
|
|
9
|
+
export const brand = {
|
|
10
|
+
accent: chalk.hex(ACCENT),
|
|
11
|
+
accentBold: chalk.hex(ACCENT).bold,
|
|
12
|
+
accentLight: chalk.hex(ACCENT_LIGHT),
|
|
13
|
+
accentLighter: chalk.hex(ACCENT_LIGHTER),
|
|
14
|
+
};
|
|
15
|
+
export const NITRO_BANNER_FULL = `
|
|
16
|
+
${brand.accentBold('╔══════════════════════════════════════════════════════════════════╗')}
|
|
17
|
+
${brand.accentBold('║')} ${brand.accentBold('║')}
|
|
18
|
+
${brand.accentBold('║')} ${brand.accentBold('███╗ ██╗██╗████████╗██████╗ ██████╗ ')} ${brand.accentBold('║')}
|
|
19
|
+
${brand.accentBold('║')} ${brand.accentLight('████╗ ██║██║╚══██╔══╝██╔══██╗██╔═══██╗')} ${brand.accentBold('║')}
|
|
20
|
+
${brand.accentBold('║')} ${brand.accentLighter('██╔██╗ ██║██║ ██║ ██████╔╝██║ ██║')} ${brand.accentBold('║')}
|
|
21
|
+
${brand.accentBold('║')} ${chalk.hex('#FFCC80')('██║╚██╗██║██║ ██║ ██╔══██╗██║ ██║')} ${brand.accentBold('║')}
|
|
22
|
+
${brand.accentBold('║')} ${chalk.hex('#FFE0B2')('██║ ╚████║██║ ██║ ██║ ██║╚██████╔╝')} ${brand.accentBold('║')}
|
|
23
|
+
${brand.accentBold('║')} ${chalk.dim('╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ')} ${brand.accentBold('║')}
|
|
24
|
+
${brand.accentBold('║')} ${brand.accentBold('║')}
|
|
25
|
+
${brand.accentBold('║')} ${chalk.white.bold('S T A C K')} ${chalk.dim('─────────────────────────────────────────')} ${brand.accentBold('║')}
|
|
26
|
+
${brand.accentBold('║')} ${brand.accentBold('║')}
|
|
27
|
+
${brand.accentBold('╚══════════════════════════════════════════════════════════════════╝')}
|
|
28
|
+
`;
|
|
29
|
+
export function createHeader(title, subtitle) {
|
|
30
|
+
const paddedTitle = title.padEnd(40);
|
|
31
|
+
const paddedSubtitle = (subtitle || '').padEnd(40);
|
|
32
|
+
return `
|
|
33
|
+
${brand.accentBold('┌──────────────────────────────────────────────────────────────────┐')}
|
|
34
|
+
${brand.accentBold('│')} ${brand.accentBold('NITRO')}${chalk.white.bold('STACK')} ${chalk.dim('━━')} ${chalk.white.bold(paddedTitle)} ${brand.accentBold('│')}
|
|
35
|
+
${subtitle ? brand.accentBold('│') + ' ' + chalk.dim(paddedSubtitle) + ' ' + brand.accentBold('│') + '\n' : ''}${brand.accentBold('└──────────────────────────────────────────────────────────────────┘')}
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
export function createBox(lines, type = 'info') {
|
|
39
|
+
const colors = {
|
|
40
|
+
success: { border: chalk.green, title: chalk.green.bold },
|
|
41
|
+
error: { border: chalk.red, title: chalk.red.bold },
|
|
42
|
+
info: { border: brand.accent, title: brand.accentBold },
|
|
43
|
+
warning: { border: chalk.yellow, title: chalk.yellow.bold },
|
|
44
|
+
};
|
|
45
|
+
const { border } = colors[type];
|
|
46
|
+
let output = border('┌──────────────────────────────────────────────────────────────────┐\n');
|
|
47
|
+
for (const line of lines) {
|
|
48
|
+
const paddedLine = line.padEnd(64);
|
|
49
|
+
output += border('│') + ' ' + paddedLine + ' ' + border('│') + '\n';
|
|
50
|
+
}
|
|
51
|
+
output += border('└──────────────────────────────────────────────────────────────────┘');
|
|
52
|
+
return output;
|
|
53
|
+
}
|
|
54
|
+
export function createSuccessBox(title, items) {
|
|
55
|
+
const lines = [
|
|
56
|
+
chalk.green.bold(`✓ ${title}`),
|
|
57
|
+
'',
|
|
58
|
+
...items.map(item => chalk.dim(` ${item}`)),
|
|
59
|
+
'',
|
|
60
|
+
];
|
|
61
|
+
return createBox(lines, 'success');
|
|
62
|
+
}
|
|
63
|
+
export function createErrorBox(title, message) {
|
|
64
|
+
const lines = [
|
|
65
|
+
chalk.red.bold(`✗ ${title}`),
|
|
66
|
+
'',
|
|
67
|
+
chalk.white(message.substring(0, 60)),
|
|
68
|
+
'',
|
|
69
|
+
];
|
|
70
|
+
return createBox(lines, 'error');
|
|
71
|
+
}
|
|
72
|
+
export function createInfoBox(items) {
|
|
73
|
+
const lines = items.map(({ label, value }) => `${chalk.white.bold(label.padEnd(14))} ${chalk.cyan(value)}`);
|
|
74
|
+
return createBox(['', ...lines, ''], 'info');
|
|
75
|
+
}
|
|
76
|
+
export class NitroSpinner {
|
|
77
|
+
spinner;
|
|
78
|
+
constructor(text) {
|
|
79
|
+
this.spinner = ora({
|
|
80
|
+
text: chalk.dim(text),
|
|
81
|
+
color: 'yellow',
|
|
82
|
+
spinner: 'dots12',
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
start() {
|
|
86
|
+
this.spinner.start();
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
update(text) {
|
|
90
|
+
this.spinner.text = chalk.dim(text);
|
|
91
|
+
return this;
|
|
92
|
+
}
|
|
93
|
+
succeed(text) {
|
|
94
|
+
this.spinner.succeed(text ? chalk.green('✓ ') + chalk.dim(text) : undefined);
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
fail(text) {
|
|
98
|
+
this.spinner.fail(text ? chalk.red('✗ ') + chalk.dim(text) : undefined);
|
|
99
|
+
return this;
|
|
100
|
+
}
|
|
101
|
+
info(text) {
|
|
102
|
+
this.spinner.info(text ? chalk.blue('ℹ ') + chalk.dim(text) : undefined);
|
|
103
|
+
return this;
|
|
104
|
+
}
|
|
105
|
+
warn(text) {
|
|
106
|
+
this.spinner.warn(text ? chalk.yellow('⚠ ') + chalk.dim(text) : undefined);
|
|
107
|
+
return this;
|
|
108
|
+
}
|
|
109
|
+
stop() {
|
|
110
|
+
this.spinner.stop();
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export function log(message, type = 'info') {
|
|
115
|
+
const icons = {
|
|
116
|
+
success: chalk.green('✓'),
|
|
117
|
+
error: chalk.red('✗'),
|
|
118
|
+
info: chalk.blue('ℹ'),
|
|
119
|
+
warning: chalk.yellow('⚠'),
|
|
120
|
+
dim: chalk.dim('·'),
|
|
121
|
+
};
|
|
122
|
+
console.log(` ${icons[type]} ${type === 'dim' ? chalk.dim(message) : message}`);
|
|
123
|
+
}
|
|
124
|
+
export function divider() {
|
|
125
|
+
console.log(chalk.dim(' ─────────────────────────────────────────────────────────────'));
|
|
126
|
+
}
|
|
127
|
+
export function spacer() {
|
|
128
|
+
console.log('');
|
|
129
|
+
}
|
|
130
|
+
export function nextSteps(steps) {
|
|
131
|
+
console.log(chalk.white.bold('\n Next steps:\n'));
|
|
132
|
+
steps.forEach((step, i) => {
|
|
133
|
+
console.log(chalk.dim(` ${i + 1}.`) + ' ' + chalk.cyan(step));
|
|
134
|
+
});
|
|
135
|
+
console.log('');
|
|
136
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nitrostack/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for NitroStack - Create and manage MCP server projects",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"nitrostack-cli": "dist/index.js",
|
|
10
|
+
"@nitrostack/cli": "dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist/",
|
|
21
|
+
"templates/",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "tsc && chmod +x dist/index.js",
|
|
26
|
+
"dev": "tsc --watch",
|
|
27
|
+
"test": "NODE_OPTIONS=--experimental-vm-modules jest",
|
|
28
|
+
"test:coverage": "NODE_OPTIONS=--experimental-vm-modules jest --coverage",
|
|
29
|
+
"prepublishOnly": "npm run build"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"nitrostack",
|
|
33
|
+
"cli",
|
|
34
|
+
"mcp",
|
|
35
|
+
"create",
|
|
36
|
+
"scaffold",
|
|
37
|
+
"generator"
|
|
38
|
+
],
|
|
39
|
+
"author": "Abhishek Pandit <abhishekpanditofficial@gmail.com>",
|
|
40
|
+
"license": "Apache-2.0",
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"chalk": "^5.3.0",
|
|
43
|
+
"chokidar": "^3.6.0",
|
|
44
|
+
"commander": "^12.1.0",
|
|
45
|
+
"fs-extra": "^11.3.2",
|
|
46
|
+
"inquirer": "^9.3.7",
|
|
47
|
+
"open": "^10.1.0",
|
|
48
|
+
"ora": "^8.1.1"
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@types/fs-extra": "^11.0.4",
|
|
52
|
+
"@types/inquirer": "^9.0.9",
|
|
53
|
+
"@types/jest": "^29.5.14",
|
|
54
|
+
"@types/node": "^22.10.5",
|
|
55
|
+
"jest": "^29.7.0",
|
|
56
|
+
"ts-jest": "^29.2.5",
|
|
57
|
+
"typescript": "^5.7.2"
|
|
58
|
+
},
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "https://github.com/abhishekpanditofficial/nitrostack.git",
|
|
62
|
+
"directory": "typescript/packages/cli"
|
|
63
|
+
},
|
|
64
|
+
"bugs": {
|
|
65
|
+
"url": "https://github.com/abhishekpanditofficial/nitrostack/issues"
|
|
66
|
+
},
|
|
67
|
+
"homepage": "https://nitrostack.vercel.app"
|
|
68
|
+
}
|
|
69
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
# OAuth 2.1 MCP Server Configuration
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# TRANSPORT MODE (AUTO-CONFIGURED)
|
|
5
|
+
# =============================================================================
|
|
6
|
+
# When OAuth is configured, the server automatically runs in DUAL mode:
|
|
7
|
+
# - STDIO: For MCP protocol communication with Studio/Claude
|
|
8
|
+
# - HTTP: For OAuth metadata endpoints (/.well-known/oauth-protected-resource)
|
|
9
|
+
# Both transports run simultaneously on different channels.
|
|
10
|
+
# =============================================================================
|
|
11
|
+
# REQUIRED: Server Configuration
|
|
12
|
+
# =============================================================================
|
|
13
|
+
# =============================================================================
|
|
14
|
+
# Auth0 Configuration
|
|
15
|
+
# =============================================================================
|
|
16
|
+
# After creating your API and Application in Auth0, fill in these values:
|
|
17
|
+
# Your Auth0 API Identifier (from APIs → MCP Server → Identifier)
|
|
18
|
+
# This MUST match exactly what you set when creating the API
|
|
19
|
+
RESOURCE_URI=https://mcplocal
|
|
20
|
+
# Your Auth0 tenant domain (authorization server)
|
|
21
|
+
AUTH_SERVER_URL=https://dev-5dt0utuk31h13tjm.us.auth0.com
|
|
22
|
+
# Expected token audience (should match RESOURCE_URI)
|
|
23
|
+
TOKEN_AUDIENCE=https://mcplocal
|
|
24
|
+
# Expected token issuer (your Auth0 tenant domain with trailing slash)
|
|
25
|
+
TOKEN_ISSUER=https://dev-5dt0utuk31h13tjm.us.auth0.com/
|
|
26
|
+
|
|
27
|
+
DUFFEL_API_KEY=duffel_test_-w0wGDHB0M3DU9k-sBeUbxLqwcibUQqfEbjWDTKNnlf
|