sunpeak 0.16.2 → 0.16.3

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.
@@ -149,9 +149,6 @@ export async function dev(projectRoot = process.cwd(), args = []) {
149
149
  // Parse --no-begging flag
150
150
  const noBegging = args.includes('--no-begging');
151
151
 
152
- // Parse --prod-mcp flag (serve production build files over MCP instead of Vite HMR)
153
- const prodMcp = args.includes('--prod-mcp');
154
-
155
152
  console.log(`Starting Vite dev server on port ${port}...`);
156
153
 
157
154
  // Check if we're in the sunpeak workspace (directory is named "template")
@@ -178,7 +175,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
178
175
  sunpeakMcp = await import(pathToFileURL(join(sunpeakBase, 'dist/mcp/index.js')).href);
179
176
  sunpeakDiscovery = await import(pathToFileURL(join(sunpeakBase, 'dist/lib/discovery-cli.js')).href);
180
177
  }
181
- const { FAVICON_BUFFER: faviconBuffer, runMCPServer, startProductionHttpServer } = sunpeakMcp;
178
+ const { FAVICON_BUFFER: faviconBuffer, runMCPServer } = sunpeakMcp;
182
179
  const { findResourceDirs, findSimulationFilesFlat, findToolFiles, extractResourceExport, extractToolExport } = sunpeakDiscovery;
183
180
 
184
181
  // Vite plugin to serve the sunpeak favicon
@@ -296,33 +293,29 @@ export async function dev(projectRoot = process.cwd(), args = []) {
296
293
  });
297
294
  }
298
295
 
299
- // Start MCP server with its own Vite instance (unless --prod-mcp is set)
296
+ // Start MCP server with its own Vite instance for HMR
300
297
  if (simulations.length > 0) {
301
- const mcpMode = prodMcp ? 'production build' : 'Vite HMR';
302
- console.log(`\nStarting MCP server with ${simulations.length} simulation(s) (${mcpMode})...`);
303
-
304
- let mcpViteServer = null;
305
-
306
- if (!prodMcp) {
307
- // Virtual entry module plugin for MCP
308
- const sunpeakEntryPlugin = () => ({
309
- name: 'sunpeak-entry',
310
- resolveId(id) {
311
- if (id.startsWith('virtual:sunpeak-entry')) {
312
- return id;
298
+ console.log(`\nStarting MCP server with ${simulations.length} simulation(s) (Vite HMR)...`);
299
+
300
+ // Virtual entry module plugin for MCP
301
+ const sunpeakEntryPlugin = () => ({
302
+ name: 'sunpeak-entry',
303
+ resolveId(id) {
304
+ if (id.startsWith('virtual:sunpeak-entry')) {
305
+ return id;
306
+ }
307
+ },
308
+ load(id) {
309
+ if (id.startsWith('virtual:sunpeak-entry')) {
310
+ const url = new URL(id.replace('virtual:sunpeak-entry', 'http://x'));
311
+ const srcPath = url.searchParams.get('src');
312
+ const componentName = url.searchParams.get('component');
313
+
314
+ if (!srcPath || !componentName) {
315
+ return 'console.error("Missing src or component param");';
313
316
  }
314
- },
315
- load(id) {
316
- if (id.startsWith('virtual:sunpeak-entry')) {
317
- const url = new URL(id.replace('virtual:sunpeak-entry', 'http://x'));
318
- const srcPath = url.searchParams.get('src');
319
- const componentName = url.searchParams.get('component');
320
-
321
- if (!srcPath || !componentName) {
322
- return 'console.error("Missing src or component param");';
323
- }
324
317
 
325
- return `
318
+ return `
326
319
  import { createElement } from 'react';
327
320
  import { createRoot } from 'react-dom/client';
328
321
  import { AppProvider } from 'sunpeak';
@@ -349,149 +342,53 @@ if (import.meta.hot) {
349
342
  import.meta.hot.accept();
350
343
  }
351
344
  `;
352
- }
353
- },
354
- });
345
+ }
346
+ },
347
+ });
355
348
 
356
- // Create Vite dev server in middleware mode for MCP
357
- // Use separate cache directory to avoid conflicts with main dev server
358
- mcpViteServer = await createServer({
359
- root: projectRoot,
360
- cacheDir: 'node_modules/.vite-mcp',
361
- plugins: [react(), tailwindcss(), sunpeakEntryPlugin()],
362
- resolve: {
363
- alias: {
364
- ...(isTemplate && {
365
- sunpeak: parentSrc,
366
- }),
367
- },
349
+ // Create Vite dev server in middleware mode for MCP
350
+ // Use separate cache directory to avoid conflicts with main dev server
351
+ const mcpViteServer = await createServer({
352
+ root: projectRoot,
353
+ cacheDir: 'node_modules/.vite-mcp',
354
+ plugins: [react(), tailwindcss(), sunpeakEntryPlugin()],
355
+ resolve: {
356
+ alias: {
357
+ ...(isTemplate && {
358
+ sunpeak: parentSrc,
359
+ }),
368
360
  },
369
- server: {
370
- middlewareMode: true,
371
- allowedHosts: true,
372
- watch: {
373
- // Only watch files that affect the UI bundle (not JSON, tests, etc.)
374
- // MCP resources reload on next tool call, not on file change
375
- ignored: (filePath) => {
376
- if (!filePath.includes('.')) return false; // Watch directories
377
- if (/\.(tsx?|css)$/.test(filePath)) {
378
- return /\.(test|spec)\.tsx?$/.test(filePath); // Ignore tests
379
- }
380
- return true; // Ignore everything else
381
- },
361
+ },
362
+ server: {
363
+ middlewareMode: true,
364
+ allowedHosts: true,
365
+ watch: {
366
+ // Only watch files that affect the UI bundle (not JSON, tests, etc.)
367
+ // MCP resources reload on next tool call, not on file change
368
+ ignored: (filePath) => {
369
+ if (!filePath.includes('.')) return false; // Watch directories
370
+ if (/\.(tsx?|css)$/.test(filePath)) {
371
+ return /\.(test|spec)\.tsx?$/.test(filePath); // Ignore tests
372
+ }
373
+ return true; // Ignore everything else
382
374
  },
383
375
  },
384
- optimizeDeps: {
385
- include: ['react', 'react-dom/client'],
386
- },
387
- appType: 'custom',
388
- });
389
- }
376
+ },
377
+ optimizeDeps: {
378
+ include: ['react', 'react-dom/client'],
379
+ },
380
+ appType: 'custom',
381
+ });
390
382
 
391
383
  const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
392
384
 
393
- let mcpHandle = null;
394
-
395
- if (prodMcp) {
396
- // --prod-mcp: Use real tool handlers via Vite SSR
397
- // Load full tool modules (handler + schema + config) from TypeScript source
398
- const ssrLoader = await createServer({
399
- root: projectRoot,
400
- server: { middlewareMode: true },
401
- appType: 'custom',
402
- logLevel: 'silent',
403
- resolve: {
404
- alias: {
405
- ...(isTemplate && { sunpeak: parentSrc }),
406
- },
407
- },
408
- });
409
-
410
- const prodTools = [];
411
- for (const { name: toolName, path: toolPath } of toolFiles) {
412
- try {
413
- const relativePath = './' + toolPath.replace(projectRoot + '/', '').replace(projectRoot + '\\', '');
414
- const mod = await ssrLoader.ssrLoadModule(relativePath);
415
- if (mod.tool && mod.default) {
416
- prodTools.push({
417
- name: toolName,
418
- tool: mod.tool,
419
- schema: mod.schema,
420
- handler: mod.default,
421
- });
422
- }
423
- } catch (err) {
424
- console.warn(`Warning: Could not load tool handler ${toolName}: ${err.message}`);
425
- }
426
- }
427
-
428
- // Load server entry if present
429
- const serverEntryPath = join(projectRoot, 'src/server.ts');
430
- let auth = undefined;
431
- let serverConfig = {};
432
- if (existsSync(serverEntryPath)) {
433
- try {
434
- const serverMod = await ssrLoader.ssrLoadModule('./src/server.ts');
435
- if (typeof serverMod.auth === 'function') {
436
- auth = serverMod.auth;
437
- console.log('Loaded auth from src/server.ts');
438
- }
439
- if (serverMod.server) serverConfig = serverMod.server;
440
- } catch (err) {
441
- console.warn(`Warning: Could not load server entry: ${err.message}`);
442
- }
443
- }
444
-
445
- await ssrLoader.close();
446
-
447
- // Build production resources from dist/
448
- const prodResources = [];
449
- for (const { key } of resourceDirs) {
450
- const meta = resourceMap.get(key);
451
- if (!meta) continue;
452
- const htmlPath = join(projectRoot, `dist/${key}/${key}.html`);
453
- const jsonPath = join(projectRoot, `dist/${key}/${key}.json`);
454
- if (!existsSync(htmlPath)) continue;
455
-
456
- const html = readFileSync(htmlPath, 'utf-8');
457
- let uri = `ui://${meta.name ?? key}`;
458
- let _meta = meta._meta;
459
-
460
- // Use URI from build JSON if available (has cache-bust timestamp)
461
- if (existsSync(jsonPath)) {
462
- try {
463
- const buildMeta = JSON.parse(readFileSync(jsonPath, 'utf-8'));
464
- if (buildMeta.uri) uri = buildMeta.uri;
465
- } catch {}
466
- }
467
-
468
- prodResources.push({
469
- name: meta.name ?? key,
470
- uri,
471
- html,
472
- description: meta.description,
473
- _meta,
474
- });
475
- }
476
-
477
- const name = serverConfig.name ?? pkg.name ?? 'Sunpeak';
478
- const version = serverConfig.version ?? pkg.version ?? '0.1.0';
479
-
480
- console.log(`Starting production MCP server with ${prodTools.length} tool(s)...`);
481
- startProductionHttpServer(
482
- { name, version, tools: prodTools, resources: prodResources, auth },
483
- 8000
484
- );
485
- } else {
486
- // Default: simulation-based MCP server with fixture data
487
- mcpHandle = runMCPServer({
488
- name: pkg.name || 'Sunpeak',
489
- version: pkg.version || '0.1.0',
490
- simulations,
491
- port: 8000,
492
- ...(mcpViteServer && { viteServer: mcpViteServer }),
493
- });
494
- }
385
+ const mcpHandle = runMCPServer({
386
+ name: pkg.name || 'Sunpeak',
387
+ version: pkg.version || '0.1.0',
388
+ simulations,
389
+ port: 8000,
390
+ viteServer: mcpViteServer,
391
+ });
495
392
 
496
393
  // Build production bundles and watch for changes.
497
394
  // Tunnel clients (e.g. Claude via ngrok) get the pre-built HTML since they can't
@@ -502,13 +399,13 @@ if (import.meta.hot) {
502
399
 
503
400
  // Handle signals - close both servers
504
401
  process.on('SIGINT', async () => {
505
- if (mcpViteServer) await mcpViteServer.close();
402
+ await mcpViteServer.close();
506
403
  await server.close();
507
404
  process.exit(0);
508
405
  });
509
406
 
510
407
  process.on('SIGTERM', async () => {
511
- if (mcpViteServer) await mcpViteServer.close();
408
+ await mcpViteServer.close();
512
409
  await server.close();
513
410
  process.exit(0);
514
411
  });
package/bin/sunpeak.js CHANGED
@@ -97,7 +97,6 @@ Usage:
97
97
  sunpeak new [name] [resources] Create a new project
98
98
  sunpeak dev Start dev server + MCP endpoint
99
99
  --no-begging Suppress GitHub star message
100
- --prod-mcp Use real tool handlers (not fixtures)
101
100
  sunpeak build Build resources + tools for production
102
101
  sunpeak start Start production MCP server
103
102
  --port, -p Server port (default: 8000, or PORT env)