@nitrostack/cli 1.0.10 → 1.0.13
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 +38 -52
- package/dist/commands/build.d.ts.map +1 -1
- package/dist/commands/build.js +13 -1
- package/dist/commands/cursor.d.ts +11 -0
- package/dist/commands/cursor.d.ts.map +1 -0
- package/dist/commands/cursor.js +238 -0
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -6
- package/dist/commands/init.js +3 -3
- package/dist/commands/start.d.ts.map +1 -1
- package/dist/commands/start.js +2 -0
- package/dist/commands/upgrade.d.ts +12 -0
- package/dist/commands/upgrade.d.ts.map +1 -1
- package/dist/commands/upgrade.js +144 -106
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -0
- package/package.json +4 -4
- package/templates/typescript-oauth/.env.example +75 -14
- package/templates/typescript-oauth/README.md +37 -51
- package/templates/typescript-oauth/package.json +2 -1
- package/templates/typescript-oauth/src/app.module.ts +7 -0
- package/templates/typescript-oauth/src/guards/oauth.guard.ts +19 -0
- package/templates/typescript-oauth/src/index.ts +9 -11
- package/templates/typescript-oauth/src/modules/flights/flights.prompts.ts +19 -1
- package/templates/typescript-oauth/src/services/duffel.service.ts +4 -2
- package/templates/typescript-oauth/src/widgets/package.json +2 -1
- package/templates/typescript-pizzaz/.env.example +14 -0
- package/templates/typescript-pizzaz/README.md +35 -60
- package/templates/typescript-pizzaz/package.json +2 -1
- package/templates/typescript-pizzaz/src/modules/pizzaz/pizzaz.tools.ts +17 -3
- package/templates/typescript-pizzaz/src/widgets/package.json +1 -0
- package/templates/typescript-starter/.env.example +11 -5
- package/templates/typescript-starter/README.md +28 -68
- package/templates/typescript-starter/package.json +2 -1
- package/templates/typescript-starter/src/widgets/package.json +2 -1
package/dist/commands/upgrade.js
CHANGED
|
@@ -2,22 +2,57 @@ import chalk from 'chalk';
|
|
|
2
2
|
import { execSync } from 'child_process';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
|
+
import https from 'https';
|
|
5
6
|
import { createHeader, createBox, createSuccessBox, createErrorBox, NitroSpinner, log, spacer, nextSteps, NITRO_BANNER_FULL, showFooter } from '../ui/branding.js';
|
|
6
7
|
import { trackEvent, shutdownAnalytics } from '../analytics/posthog.js';
|
|
7
8
|
/**
|
|
8
|
-
*
|
|
9
|
+
* Fetch a package's latest published version from NPM using standard https module.
|
|
9
10
|
*/
|
|
10
|
-
function
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
11
|
+
export function fetchLatestNpmVersion(packageName) {
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
const url = `https://registry.npmjs.org/${packageName}/latest`;
|
|
14
|
+
const req = https.get(url, {
|
|
15
|
+
headers: {
|
|
16
|
+
'Accept': 'application/json',
|
|
17
|
+
'User-Agent': 'nitrostack-cli-upgrade'
|
|
18
|
+
},
|
|
19
|
+
timeout: 5000
|
|
20
|
+
}, (res) => {
|
|
21
|
+
if (res.statusCode !== 200) {
|
|
22
|
+
reject(new Error(`Registry responded with HTTP ${res.statusCode}`));
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
let data = '';
|
|
26
|
+
res.on('data', (chunk) => {
|
|
27
|
+
data += chunk;
|
|
28
|
+
});
|
|
29
|
+
// Handle stream errors
|
|
30
|
+
res.on('error', (err) => {
|
|
31
|
+
reject(new Error(`Response stream error: ${err.message}`));
|
|
32
|
+
});
|
|
33
|
+
res.on('end', () => {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = JSON.parse(data);
|
|
36
|
+
if (parsed.version) {
|
|
37
|
+
resolve(parsed.version);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
reject(new Error('Invalid response structure from NPM registry'));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
catch (e) {
|
|
44
|
+
reject(new Error(`Failed to parse response: ${e.message}`));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
req.on('error', (err) => {
|
|
49
|
+
reject(err);
|
|
50
|
+
});
|
|
51
|
+
req.on('timeout', () => {
|
|
52
|
+
req.destroy();
|
|
53
|
+
reject(new Error('Request timed out'));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
21
56
|
}
|
|
22
57
|
/**
|
|
23
58
|
* Get the current installed version of @nitrostack/core from package.json
|
|
@@ -32,15 +67,19 @@ function getCoreVersion(packageJsonPath) {
|
|
|
32
67
|
null;
|
|
33
68
|
}
|
|
34
69
|
/**
|
|
35
|
-
* Parse version string to extract the actual version
|
|
70
|
+
* Parse version string to extract the actual numeric version.
|
|
71
|
+
*
|
|
72
|
+
* Strips any leading range operator (^ ~ >= <= > <) and drops the pre-release
|
|
73
|
+
* suffix (e.g. `-beta.1`) so the dot-separated segments always parse to numbers
|
|
74
|
+
* instead of producing `NaN` in `compareVersions`.
|
|
36
75
|
*/
|
|
37
76
|
function parseVersion(versionString) {
|
|
38
|
-
return versionString.replace(/^[\^~>=<]+/, '');
|
|
77
|
+
return versionString.replace(/^[\^~>=<]+/, '').split('-')[0];
|
|
39
78
|
}
|
|
40
79
|
/**
|
|
41
80
|
* Compare two version strings
|
|
42
81
|
*/
|
|
43
|
-
function compareVersions(v1, v2) {
|
|
82
|
+
export function compareVersions(v1, v2) {
|
|
44
83
|
const parts1 = parseVersion(v1).split('.').map(Number);
|
|
45
84
|
const parts2 = parseVersion(v2).split('.').map(Number);
|
|
46
85
|
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
@@ -54,37 +93,58 @@ function compareVersions(v1, v2) {
|
|
|
54
93
|
return 0;
|
|
55
94
|
}
|
|
56
95
|
/**
|
|
57
|
-
*
|
|
96
|
+
* Determine if a dependency refers to a local file or workspace link.
|
|
58
97
|
*/
|
|
59
|
-
function
|
|
98
|
+
export function isLocalDependency(version) {
|
|
99
|
+
return version.startsWith('file:') ||
|
|
100
|
+
version.startsWith('link:') ||
|
|
101
|
+
version.startsWith('workspace:') ||
|
|
102
|
+
version.startsWith('.') ||
|
|
103
|
+
version.startsWith('/');
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Update all @nitrostack/* versions in package.json using dynamic npm registry fetch
|
|
107
|
+
*/
|
|
108
|
+
async function updatePackageJson(packageJsonPath, dryRun) {
|
|
60
109
|
if (!fs.existsSync(packageJsonPath)) {
|
|
61
110
|
return [];
|
|
62
111
|
}
|
|
63
112
|
const packageJson = fs.readJSONSync(packageJsonPath);
|
|
64
113
|
const results = [];
|
|
65
114
|
let hasChanges = false;
|
|
66
|
-
const updateDeps = (deps) => {
|
|
115
|
+
const updateDeps = async (deps) => {
|
|
67
116
|
if (!deps)
|
|
68
117
|
return;
|
|
69
|
-
|
|
118
|
+
const promises = Object.keys(deps).map(async (pkg) => {
|
|
70
119
|
if (pkg.startsWith('@nitrostack/')) {
|
|
71
120
|
const currentVersion = deps[pkg];
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
121
|
+
// Skip local dependencies entirely
|
|
122
|
+
if (isLocalDependency(currentVersion)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
const latestVersion = await fetchLatestNpmVersion(pkg);
|
|
127
|
+
if (compareVersions(currentVersion, latestVersion) < 0) {
|
|
128
|
+
results.push({
|
|
129
|
+
location: path.basename(path.dirname(packageJsonPath)),
|
|
130
|
+
packageName: pkg,
|
|
131
|
+
previousVersion: currentVersion,
|
|
132
|
+
newVersion: `^${latestVersion}`,
|
|
133
|
+
upgraded: true,
|
|
134
|
+
});
|
|
135
|
+
deps[pkg] = `^${latestVersion}`;
|
|
136
|
+
hasChanges = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.warn(`\n⚠️ Skipped upgrade check for ${pkg}: ${error.message}`);
|
|
82
141
|
}
|
|
83
142
|
}
|
|
84
|
-
}
|
|
143
|
+
});
|
|
144
|
+
await Promise.all(promises);
|
|
85
145
|
};
|
|
86
|
-
updateDeps(packageJson.dependencies);
|
|
87
|
-
updateDeps(packageJson.devDependencies);
|
|
146
|
+
await updateDeps(packageJson.dependencies);
|
|
147
|
+
await updateDeps(packageJson.devDependencies);
|
|
88
148
|
if (hasChanges && !dryRun) {
|
|
89
149
|
fs.writeJSONSync(packageJsonPath, packageJson, { spaces: 2 });
|
|
90
150
|
}
|
|
@@ -123,51 +183,22 @@ export async function upgradeCommand(options) {
|
|
|
123
183
|
console.log(createErrorBox('Not a NitroStack Project', '@nitrostack/core is not a dependency'));
|
|
124
184
|
process.exit(1);
|
|
125
185
|
}
|
|
126
|
-
// Fetch latest version
|
|
127
|
-
const spinner = new NitroSpinner('Checking for updates...').start();
|
|
128
|
-
let latestVersion;
|
|
129
|
-
try {
|
|
130
|
-
latestVersion = getLatestVersion('@nitrostack/core');
|
|
131
|
-
spinner.succeed(`Latest version: ${chalk.cyan(latestVersion)}`);
|
|
132
|
-
}
|
|
133
|
-
catch (error) {
|
|
134
|
-
spinner.fail('Failed to fetch latest version');
|
|
135
|
-
process.exit(1);
|
|
136
|
-
}
|
|
137
|
-
// Check if already on latest
|
|
138
|
-
const currentParsedVersion = parseVersion(coreVersion);
|
|
139
|
-
if (compareVersions(currentParsedVersion, latestVersion) >= 0) {
|
|
140
|
-
spacer();
|
|
141
|
-
console.log(createSuccessBox('Already Up to Date', [
|
|
142
|
-
`Current version: ${currentParsedVersion}`,
|
|
143
|
-
`Latest version: ${latestVersion}`,
|
|
144
|
-
]));
|
|
145
|
-
trackEvent('cli_upgrade_completed', {
|
|
146
|
-
packages_upgraded: 0,
|
|
147
|
-
from_version: currentParsedVersion,
|
|
148
|
-
to_version: latestVersion,
|
|
149
|
-
dry_run: !!options.dryRun,
|
|
150
|
-
already_current: true,
|
|
151
|
-
});
|
|
152
|
-
await shutdownAnalytics();
|
|
153
|
-
return;
|
|
154
|
-
}
|
|
155
|
-
const allResults = [];
|
|
156
186
|
const dryRun = options.dryRun ?? false;
|
|
157
187
|
if (dryRun) {
|
|
158
188
|
spacer();
|
|
159
189
|
log('Dry run mode - no changes will be made', 'warning');
|
|
160
190
|
}
|
|
161
191
|
spacer();
|
|
162
|
-
log('
|
|
192
|
+
log('Checking for package updates...', 'info');
|
|
163
193
|
spacer();
|
|
194
|
+
const allResults = [];
|
|
164
195
|
// Upgrade root
|
|
165
|
-
const rootSpinner = new NitroSpinner('
|
|
196
|
+
const rootSpinner = new NitroSpinner('Checking root package.json...').start();
|
|
166
197
|
try {
|
|
167
|
-
const results = updatePackageJson(rootPackageJsonPath,
|
|
198
|
+
const results = await updatePackageJson(rootPackageJsonPath, dryRun);
|
|
168
199
|
if (results.length > 0) {
|
|
169
200
|
allResults.push(...results);
|
|
170
|
-
rootSpinner.succeed(`Root:
|
|
201
|
+
rootSpinner.succeed(`Root: Found ${results.length} package update(s)`);
|
|
171
202
|
if (!dryRun) {
|
|
172
203
|
const installSpinner = new NitroSpinner('Installing dependencies...').start();
|
|
173
204
|
runNpmInstall(projectRoot);
|
|
@@ -175,7 +206,7 @@ export async function upgradeCommand(options) {
|
|
|
175
206
|
}
|
|
176
207
|
}
|
|
177
208
|
else {
|
|
178
|
-
rootSpinner.info('Root: All @nitrostack packages are up to date');
|
|
209
|
+
rootSpinner.info('Root: All @nitrostack packages are up to date (or local references)');
|
|
179
210
|
}
|
|
180
211
|
}
|
|
181
212
|
catch (error) {
|
|
@@ -184,12 +215,12 @@ export async function upgradeCommand(options) {
|
|
|
184
215
|
}
|
|
185
216
|
// Upgrade widgets if they exist
|
|
186
217
|
if (fs.existsSync(widgetsPackageJsonPath)) {
|
|
187
|
-
const widgetsSpinner = new NitroSpinner('
|
|
218
|
+
const widgetsSpinner = new NitroSpinner('Checking widgets package.json...').start();
|
|
188
219
|
try {
|
|
189
|
-
const results = updatePackageJson(widgetsPackageJsonPath,
|
|
220
|
+
const results = await updatePackageJson(widgetsPackageJsonPath, dryRun);
|
|
190
221
|
if (results.length > 0) {
|
|
191
222
|
allResults.push(...results);
|
|
192
|
-
widgetsSpinner.succeed(`Widgets:
|
|
223
|
+
widgetsSpinner.succeed(`Widgets: Found ${results.length} package update(s)`);
|
|
193
224
|
if (!dryRun) {
|
|
194
225
|
const installSpinner = new NitroSpinner('Installing widget dependencies...').start();
|
|
195
226
|
runNpmInstall(widgetsPath);
|
|
@@ -197,54 +228,61 @@ export async function upgradeCommand(options) {
|
|
|
197
228
|
}
|
|
198
229
|
}
|
|
199
230
|
else {
|
|
200
|
-
widgetsSpinner.info('Widgets: All @nitrostack packages are up to date');
|
|
231
|
+
widgetsSpinner.info('Widgets: All @nitrostack packages are up to date (or local references)');
|
|
201
232
|
}
|
|
202
233
|
}
|
|
203
234
|
catch (error) {
|
|
204
235
|
widgetsSpinner.fail('Failed to upgrade widgets');
|
|
236
|
+
console.error(error);
|
|
205
237
|
}
|
|
206
238
|
}
|
|
207
239
|
// Summary
|
|
208
240
|
spacer();
|
|
209
241
|
if (allResults.length === 0) {
|
|
210
|
-
log('
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
// Unique packages upgraded
|
|
214
|
-
const uniquePackages = Array.from(new Set(allResults.map(r => r.packageName)));
|
|
215
|
-
const summaryItems = uniquePackages.map(pkg => {
|
|
216
|
-
const result = allResults.find(r => r.packageName === pkg);
|
|
217
|
-
return `${pkg}: ${parseVersion(result.previousVersion)} → ${parseVersion(result.newVersion)}`;
|
|
218
|
-
});
|
|
219
|
-
if (dryRun) {
|
|
220
|
-
spacer();
|
|
221
|
-
console.log(createBox([
|
|
222
|
-
chalk.yellow.bold('Dry run complete'),
|
|
223
|
-
'',
|
|
224
|
-
chalk.dim('No changes were made to your project.'),
|
|
225
|
-
chalk.dim('Run without --dry-run to apply the upgrade.'),
|
|
226
|
-
], 'warning'));
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
console.log(createSuccessBox('Upgrade Complete', [
|
|
230
|
-
...summaryItems,
|
|
231
|
-
'',
|
|
232
|
-
chalk.dim(`Total updates across all packages: ${allResults.length}`)
|
|
233
|
-
]));
|
|
234
|
-
nextSteps([
|
|
235
|
-
'Review the changes in package.json',
|
|
236
|
-
'Restart your development server',
|
|
237
|
-
'Check docs.nitrostack.ai for migration guides',
|
|
238
|
-
]);
|
|
239
|
-
}
|
|
240
|
-
showFooter();
|
|
242
|
+
console.log(createSuccessBox('Already Up to Date', [
|
|
243
|
+
'All @nitrostack packages are already running their latest versions.',
|
|
244
|
+
]));
|
|
241
245
|
trackEvent('cli_upgrade_completed', {
|
|
242
|
-
packages_upgraded:
|
|
243
|
-
from_version: currentParsedVersion,
|
|
244
|
-
to_version: latestVersion,
|
|
246
|
+
packages_upgraded: 0,
|
|
245
247
|
dry_run: dryRun,
|
|
246
|
-
already_current:
|
|
248
|
+
already_current: true,
|
|
247
249
|
});
|
|
248
250
|
await shutdownAnalytics();
|
|
251
|
+
return;
|
|
249
252
|
}
|
|
253
|
+
// Unique packages upgraded
|
|
254
|
+
const uniquePackages = Array.from(new Set(allResults.map(r => r.packageName)));
|
|
255
|
+
const summaryItems = uniquePackages.map(pkg => {
|
|
256
|
+
const result = allResults.find(r => r.packageName === pkg);
|
|
257
|
+
return `${pkg}: ${parseVersion(result.previousVersion)} → ${parseVersion(result.newVersion)}`;
|
|
258
|
+
});
|
|
259
|
+
if (dryRun) {
|
|
260
|
+
spacer();
|
|
261
|
+
console.log(createBox([
|
|
262
|
+
chalk.yellow.bold('Dry Run - Proposed Upgrades:'),
|
|
263
|
+
...summaryItems.map(item => ` • ${item}`),
|
|
264
|
+
'',
|
|
265
|
+
chalk.dim('No changes were made to your project.'),
|
|
266
|
+
chalk.dim('Run without --dry-run to apply the upgrade.'),
|
|
267
|
+
], 'warning'));
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
console.log(createSuccessBox('Upgrade Complete', [
|
|
271
|
+
...summaryItems,
|
|
272
|
+
'',
|
|
273
|
+
chalk.dim(`Total updates across all packages: ${allResults.length}`)
|
|
274
|
+
]));
|
|
275
|
+
nextSteps([
|
|
276
|
+
'Review the changes in package.json',
|
|
277
|
+
'Restart your development server',
|
|
278
|
+
'Check docs.nitrostack.ai for migration guides',
|
|
279
|
+
]);
|
|
280
|
+
}
|
|
281
|
+
showFooter();
|
|
282
|
+
trackEvent('cli_upgrade_completed', {
|
|
283
|
+
packages_upgraded: allResults.length,
|
|
284
|
+
dry_run: dryRun,
|
|
285
|
+
already_current: false,
|
|
286
|
+
});
|
|
287
|
+
await shutdownAnalytics();
|
|
250
288
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,4 +8,5 @@ export { startCommand } from './commands/start.js';
|
|
|
8
8
|
export { generate } from './commands/generate.js';
|
|
9
9
|
export { upgradeCommand } from './commands/upgrade.js';
|
|
10
10
|
export { installCommand } from './commands/install.js';
|
|
11
|
+
export { cursorCommand } from './commands/cursor.js';
|
|
11
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAcpC,wBAAgB,aAAa,YA4E5B;AAGD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { startCommand } from './commands/start.js';
|
|
|
8
8
|
import { generate } from './commands/generate.js';
|
|
9
9
|
import { upgradeCommand } from './commands/upgrade.js';
|
|
10
10
|
import { installCommand } from './commands/install.js';
|
|
11
|
+
import { cursorCommand } from './commands/cursor.js';
|
|
11
12
|
const require = createRequire(import.meta.url);
|
|
12
13
|
const packageJson = require('../package.json');
|
|
13
14
|
export function createProgram() {
|
|
@@ -64,6 +65,17 @@ export function createProgram() {
|
|
|
64
65
|
.option('--skip-widgets', 'Skip installing widget dependencies')
|
|
65
66
|
.option('--production', 'Install production dependencies only')
|
|
66
67
|
.action(installCommand);
|
|
68
|
+
program
|
|
69
|
+
.command('cursor')
|
|
70
|
+
.alias('c')
|
|
71
|
+
.description('Integrate this MCP server with Cursor')
|
|
72
|
+
.option('-g, --global', 'Install globally to ~/.cursor/mcp.json')
|
|
73
|
+
.option('-l, --local', 'Install locally to .cursor/mcp.json')
|
|
74
|
+
.option('-t, --type <type>', 'Connection type: "command", "legacy-sse", or "streamable-http" (alias: "sse" → legacy-sse)')
|
|
75
|
+
.option('-u, --url <url>', 'HTTP connection URL (for legacy-sse or streamable-http)')
|
|
76
|
+
.option('-p, --port <port>', 'Port for default HTTP URL (for legacy-sse or streamable-http)')
|
|
77
|
+
.option('--force', 'Force overwrite of existing configuration')
|
|
78
|
+
.action(cursorCommand);
|
|
67
79
|
return program;
|
|
68
80
|
}
|
|
69
81
|
// Re-export commands for programmatic use
|
|
@@ -74,6 +86,7 @@ export { startCommand } from './commands/start.js';
|
|
|
74
86
|
export { generate } from './commands/generate.js';
|
|
75
87
|
export { upgradeCommand } from './commands/upgrade.js';
|
|
76
88
|
export { installCommand } from './commands/install.js';
|
|
89
|
+
export { cursorCommand } from './commands/cursor.js';
|
|
77
90
|
// Run the CLI when this module is the entry point
|
|
78
91
|
import { fileURLToPath } from 'url';
|
|
79
92
|
import { realpathSync } from 'fs';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nitrostack/cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.13",
|
|
4
4
|
"description": "CLI for NitroStack - Create and manage MCP server projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
14
15
|
"import": "./dist/index.js",
|
|
15
|
-
"require": "./dist/index.js"
|
|
16
|
-
"types": "./dist/index.d.ts"
|
|
16
|
+
"require": "./dist/index.js"
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
@@ -68,4 +68,4 @@
|
|
|
68
68
|
"url": "https://github.com/nitrocloudofficial/nitrostack/issues"
|
|
69
69
|
},
|
|
70
70
|
"homepage": "https://nitrostack.ai"
|
|
71
|
-
}
|
|
71
|
+
}
|
|
@@ -1,27 +1,88 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NitroStack Configuration
|
|
2
|
+
NITRO_LOG_LEVEL=info
|
|
3
|
+
NITROSTACK_APP_MODE=openai
|
|
4
|
+
|
|
5
|
+
# Server Transport Configuration (Optional)
|
|
2
6
|
# =============================================================================
|
|
3
|
-
#
|
|
7
|
+
# MCP_TRANSPORT_TYPE: Toggles transport mode. Values: stdio | http | dual.
|
|
8
|
+
# Defaults to 'stdio' in development and 'dual' in production/NODE_ENV=production.
|
|
4
9
|
# =============================================================================
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
10
|
+
# MCP_TRANSPORT_TYPE=dual
|
|
11
|
+
# PORT=3000
|
|
12
|
+
# HOST=localhost
|
|
13
|
+
# ENABLE_CORS=true
|
|
14
|
+
|
|
15
|
+
# Streamable HTTP Session Limits (Optional)
|
|
9
16
|
# =============================================================================
|
|
10
|
-
#
|
|
17
|
+
# Bounds memory against unauthenticated initialize floods. Defaults: 1000 sessions,
|
|
18
|
+
# 30-minute idle timeout. Only applies when running in http or dual transport mode.
|
|
19
|
+
# =============================================================================
|
|
20
|
+
# MCP_MAX_SESSIONS=1000
|
|
21
|
+
# MCP_SESSION_TIMEOUT_MS=1800000
|
|
22
|
+
|
|
23
|
+
# OAuth 2.1 MCP Server Configuration
|
|
11
24
|
# =============================================================================
|
|
25
|
+
# Enforcement Gate (Optional)
|
|
12
26
|
# =============================================================================
|
|
13
|
-
#
|
|
27
|
+
# OAUTH_REQUIRED controls whether authentication is enforced.
|
|
28
|
+
# - Unset / false (default): auth is NOT enforced. The server runs out-of-the-box
|
|
29
|
+
# and protected endpoints are reachable without a token. Best for local dev.
|
|
30
|
+
# - true: auth is enforced (fail-closed). If neither JWKS_URI nor
|
|
31
|
+
# INTROSPECTION_ENDPOINT is configured, the server STILL starts and logs a
|
|
32
|
+
# clear warning, but rejects all protected requests until a verifier is set.
|
|
33
|
+
# Set this to true (and configure a verifier below) before deploying.
|
|
34
|
+
# =============================================================================
|
|
35
|
+
# OAUTH_REQUIRED=true
|
|
36
|
+
|
|
37
|
+
# =============================================================================
|
|
38
|
+
# REQUIRED: Server Configuration
|
|
14
39
|
# =============================================================================
|
|
15
|
-
# After creating your API and Application in Auth0, fill in these values:
|
|
16
40
|
# Your Auth0 API Identifier (from APIs → MCP Server → Identifier)
|
|
17
41
|
# This MUST match exactly what you set when creating the API
|
|
18
|
-
# This MUST match exactly what you set when creating the API
|
|
19
42
|
RESOURCE_URI=https://mcplocal
|
|
20
|
-
|
|
43
|
+
|
|
44
|
+
# Your Auth0 tenant domain (authorization server URL)
|
|
21
45
|
AUTH_SERVER_URL=https://dev-5dt0utuk315713tjm.us.auth0.com
|
|
22
|
-
|
|
46
|
+
|
|
47
|
+
# Cryptographic JWKS Signature Verification (Recommended)
|
|
48
|
+
# =============================================================================
|
|
49
|
+
# If configured, the SDK cryptographically verifies token signatures using JWKS keys.
|
|
50
|
+
# Token validation is SECURE BY DEFAULT (fail-closed): if neither JWKS_URI nor an
|
|
51
|
+
# introspection endpoint (below) is configured, ALL tokens are rejected. Configure
|
|
52
|
+
# one of them for a working server.
|
|
53
|
+
# NOTE: Unsigned/signature-less token decoding is disabled by default and is only
|
|
54
|
+
# available via the in-code `allowInsecureTokenDecode: true` option in
|
|
55
|
+
# OAuthModule.forRoot(...) — intended for local development only, never production.
|
|
56
|
+
# =============================================================================
|
|
57
|
+
# JWKS_URI=https://dev-5dt0utuk315713tjm.us.auth0.com/.well-known/jwks.json
|
|
58
|
+
|
|
59
|
+
# Expected token validation audience (should match RESOURCE_URI)
|
|
23
60
|
TOKEN_AUDIENCE=https://mcplocal
|
|
24
|
-
|
|
61
|
+
|
|
62
|
+
# Expected token validation issuer (your authorization server tenant domain)
|
|
25
63
|
TOKEN_ISSUER=https://dev-5dt0utuk315713tjm.us.auth0.com
|
|
26
64
|
|
|
27
|
-
|
|
65
|
+
# Token Introspection Configuration (Alternative to JWKS signature verification)
|
|
66
|
+
# =============================================================================
|
|
67
|
+
# INTROSPECTION_ENDPOINT=https://dev-5dt0utuk315713tjm.us.auth0.com/oauth/introspect
|
|
68
|
+
# INTROSPECTION_CLIENT_ID=your-introspection-client-id
|
|
69
|
+
# INTROSPECTION_CLIENT_SECRET=your-introspection-client-secret
|
|
70
|
+
|
|
71
|
+
# OAuth Discovery Settings (Optional)
|
|
72
|
+
# =============================================================================
|
|
73
|
+
# OAUTH_DISCOVERY_PORT=3005
|
|
74
|
+
# OAUTH_DISCOVERY_AUTO_RETRY=true
|
|
75
|
+
# MCP_SERVER_PORT=3000
|
|
76
|
+
|
|
77
|
+
# Dynamic Client Registration (Optional / disabled by default)
|
|
78
|
+
# =============================================================================
|
|
79
|
+
# Set to 'true' to expose the /oauth/v2/register endpoint. It only serves the
|
|
80
|
+
# statically configured client below; without OAUTH_CLIENT_ID it stays disabled.
|
|
81
|
+
# =============================================================================
|
|
82
|
+
# OAUTH_ENABLE_CLIENT_REGISTRATION=true
|
|
83
|
+
# OAUTH_CLIENT_ID=your-client-id
|
|
84
|
+
# OAUTH_CLIENT_SECRET=your-client-secret
|
|
85
|
+
|
|
86
|
+
# Duffel API Integration (Required for Flight Search & Booking)
|
|
87
|
+
# =============================================================================
|
|
88
|
+
DUFFEL_API_KEY=your-duffel-api-key
|
|
@@ -1,70 +1,56 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NitroStack OAuth Template
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Template for OAuth 2.1-enabled MCP servers, including auth-aware tool patterns
|
|
4
|
+
and a production-style project structure.
|
|
4
5
|
|
|
5
|
-
##
|
|
6
|
+
## What This Template Includes
|
|
6
7
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
8
|
+
- OAuth 2.1 oriented project setup
|
|
9
|
+
- Tool and module structure for protected workflows
|
|
10
|
+
- Environment-based configuration (`.env`)
|
|
11
|
+
- Widget-ready frontend integration
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## 🚀 Quick Start
|
|
15
|
-
|
|
16
|
-
### 1. Initialize Your Project
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
npx nitrostack init my-flight-app --template typescript-oauth
|
|
20
|
-
cd my-flight-app
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
### 2. Install Dependencies
|
|
13
|
+
## Quick Start
|
|
24
14
|
|
|
25
15
|
```bash
|
|
26
|
-
|
|
16
|
+
npx @nitrostack/cli init my-oauth-app --template typescript-oauth
|
|
17
|
+
cd my-oauth-app
|
|
18
|
+
npm run dev
|
|
27
19
|
```
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
## Configuration
|
|
30
22
|
|
|
31
|
-
|
|
23
|
+
1. Copy `.env.example` to `.env`
|
|
24
|
+
2. Configure OAuth provider values
|
|
25
|
+
3. (Optional) Configure provider-specific API keys
|
|
32
26
|
|
|
33
|
-
|
|
27
|
+
For detailed setup, see `OAUTH_SETUP.md`.
|
|
34
28
|
|
|
35
|
-
|
|
36
|
-
2. **Open Project**: Launch NitroStudio and select your project folder.
|
|
29
|
+
## Common Commands
|
|
37
30
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
You need a free Duffel API key to see live flight data:
|
|
31
|
+
```bash
|
|
32
|
+
npm run dev
|
|
33
|
+
npm run build
|
|
34
|
+
npm start
|
|
35
|
+
```
|
|
45
36
|
|
|
46
|
-
|
|
47
|
-
2. Copy your **Test Token** from the dashboard.
|
|
48
|
-
3. Create `.env` from the example:
|
|
49
|
-
```bash
|
|
50
|
-
cp .env.example .env
|
|
51
|
-
```
|
|
52
|
-
4. Update `DUFFEL_API_KEY` in your `.env`.
|
|
37
|
+
## NitroStudio
|
|
53
38
|
|
|
54
|
-
|
|
39
|
+
Use NitroStudio to test auth flows, inspect tool requests, and validate behavior.
|
|
55
40
|
|
|
56
|
-
|
|
41
|
+
- Download: <https://nitrostack.ai/studio>
|
|
42
|
+
- Studio: <https://nitrostack.ai/studio>
|
|
57
43
|
|
|
58
|
-
|
|
44
|
+
## Links
|
|
59
45
|
|
|
60
|
-
|
|
46
|
+
- Docs: <https://docs.nitrostack.ai>
|
|
47
|
+
- OAuth docs: <https://docs.nitrostack.ai/sdk/typescript/11-oauth-authentication>
|
|
48
|
+
- Main repository: <https://github.com/nitrocloudofficial/nitrostack>
|
|
61
49
|
|
|
62
|
-
|
|
63
|
-
- `npm run build` — Bundle TypeScript and widgets for deployment.
|
|
64
|
-
- `npm run upgrade` — Keep NitroStack core up to date.
|
|
50
|
+
## Community
|
|
65
51
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
-
|
|
69
|
-
-
|
|
70
|
-
-
|
|
52
|
+
- Discord: <https://discord.gg/uVWey6UhuD>
|
|
53
|
+
- X: <https://x.com/nitrostackai>
|
|
54
|
+
- YouTube: <https://www.youtube.com/@nitrostackai>
|
|
55
|
+
- LinkedIn: <https://linkedin.com/company/nitrostack-ai/>
|
|
56
|
+
- GitHub: <https://github.com/nitrostackai>
|
|
@@ -36,6 +36,13 @@ import { SystemHealthCheck } from './health/system.health.js';
|
|
|
36
36
|
|
|
37
37
|
// Enable OAuth 2.1 authentication
|
|
38
38
|
OAuthModule.forRoot({
|
|
39
|
+
// Whether OAuth is enforced. Defaults to false (dev-friendly): the server
|
|
40
|
+
// runs out-of-the-box and protected endpoints are reachable without a token.
|
|
41
|
+
// Set OAUTH_REQUIRED=true to enforce auth (fail-closed). When enforced but no
|
|
42
|
+
// verifier (JWKS_URI / INTROSPECTION_ENDPOINT) is configured, the server still
|
|
43
|
+
// starts and warns, but rejects protected requests until one is configured.
|
|
44
|
+
required: process.env.OAUTH_REQUIRED === 'true',
|
|
45
|
+
|
|
39
46
|
// Resource URI - YOUR MCP server's public URL
|
|
40
47
|
// This is used for token audience binding (RFC 8707)
|
|
41
48
|
// CRITICAL: Tokens must be issued specifically for this URI
|