sunpeak 0.16.2 → 0.16.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.
Files changed (36) hide show
  1. package/README.md +3 -1
  2. package/bin/commands/build.mjs +1 -0
  3. package/bin/commands/dev.mjs +65 -166
  4. package/bin/sunpeak.js +0 -1
  5. package/dist/chatgpt/globals.css +0 -8
  6. package/dist/mcp/index.cjs +7258 -7254
  7. package/dist/mcp/index.cjs.map +1 -1
  8. package/dist/mcp/index.js +7259 -7255
  9. package/dist/mcp/index.js.map +1 -1
  10. package/dist/style.css +0 -8
  11. package/package.json +1 -1
  12. package/template/package.json +0 -3
  13. package/template/src/components/avatar.tsx +1 -1
  14. package/template/src/resources/albums/components/album-card.tsx +2 -2
  15. package/template/src/resources/albums/components/album-carousel.tsx +3 -3
  16. package/template/src/resources/albums/components/film-strip.tsx +2 -2
  17. package/template/src/resources/albums/components/fullscreen-viewer.tsx +1 -1
  18. package/template/src/resources/carousel/components/card.tsx +2 -2
  19. package/template/src/resources/carousel/components/carousel.tsx +3 -3
  20. package/template/src/resources/map/components/map-view.tsx +1 -1
  21. package/template/src/resources/map/components/map.tsx +3 -3
  22. package/template/src/resources/map/components/place-card.tsx +2 -2
  23. package/template/src/resources/map/components/place-carousel.tsx +1 -1
  24. package/template/src/resources/map/components/place-inspector.tsx +4 -4
  25. package/template/src/resources/map/components/place-list.tsx +2 -2
  26. package/template/src/resources/review/review.tsx +59 -29
  27. package/template/src/tools/review-diff.ts +3 -1
  28. package/template/src/tools/review-post.ts +3 -1
  29. package/template/src/tools/review-purchase.ts +3 -1
  30. package/template/src/tools/show-albums.ts +3 -1
  31. package/template/src/tools/show-carousel.ts +3 -1
  32. package/template/src/tools/show-map.ts +3 -1
  33. package/template/tsconfig.json +7 -2
  34. package/template/vitest.config.ts +1 -0
  35. package/template/node_modules/.bin/nodemon +0 -21
  36. package/template/node_modules/.bin/tsx +0 -21
package/README.md CHANGED
@@ -141,7 +141,9 @@ export const schema = {
141
141
  title: z.string().describe('Title describing the changes'),
142
142
  };
143
143
 
144
- export default async function (args: Record<string, unknown>, extra: ToolHandlerExtra) {
144
+ type Args = z.infer<z.ZodObject<typeof schema>>;
145
+
146
+ export default async function (args: Args, extra: ToolHandlerExtra) {
145
147
  return { structuredContent: { title: args.title, sections: [] } };
146
148
  }
147
149
  ```
@@ -241,6 +241,7 @@ export async function build(projectRoot = process.cwd()) {
241
241
  resolve: {
242
242
  conditions: ['style', 'import', 'module', 'browser', 'default'],
243
243
  alias: {
244
+ '@': path.resolve(projectRoot, 'src'),
244
245
  // In workspace dev mode, use local sunpeak source
245
246
  ...(isTemplate && {
246
247
  sunpeak: parentSrc,
@@ -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
@@ -204,6 +201,7 @@ export async function dev(projectRoot = process.cwd(), args = []) {
204
201
  plugins: [react(), tailwindcss(), sunpeakFaviconPlugin()],
205
202
  resolve: {
206
203
  alias: {
204
+ '@': path.resolve(projectRoot, 'src'),
207
205
  // In workspace dev mode, use local sunpeak source
208
206
  ...(isTemplate && {
209
207
  sunpeak: parentSrc,
@@ -296,33 +294,29 @@ export async function dev(projectRoot = process.cwd(), args = []) {
296
294
  });
297
295
  }
298
296
 
299
- // Start MCP server with its own Vite instance (unless --prod-mcp is set)
297
+ // Start MCP server with its own Vite instance for HMR
300
298
  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;
299
+ console.log(`\nStarting MCP server with ${simulations.length} simulation(s) (Vite HMR)...`);
300
+
301
+ // Virtual entry module plugin for MCP
302
+ const sunpeakEntryPlugin = () => ({
303
+ name: 'sunpeak-entry',
304
+ resolveId(id) {
305
+ if (id.startsWith('virtual:sunpeak-entry')) {
306
+ return id;
307
+ }
308
+ },
309
+ load(id) {
310
+ if (id.startsWith('virtual:sunpeak-entry')) {
311
+ const url = new URL(id.replace('virtual:sunpeak-entry', 'http://x'));
312
+ const srcPath = url.searchParams.get('src');
313
+ const componentName = url.searchParams.get('component');
314
+
315
+ if (!srcPath || !componentName) {
316
+ return 'console.error("Missing src or component param");';
313
317
  }
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
318
 
325
- return `
319
+ return `
326
320
  import { createElement } from 'react';
327
321
  import { createRoot } from 'react-dom/client';
328
322
  import { AppProvider } from 'sunpeak';
@@ -349,149 +343,54 @@ if (import.meta.hot) {
349
343
  import.meta.hot.accept();
350
344
  }
351
345
  `;
352
- }
353
- },
354
- });
346
+ }
347
+ },
348
+ });
355
349
 
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
- },
350
+ // Create Vite dev server in middleware mode for MCP
351
+ // Use separate cache directory to avoid conflicts with main dev server
352
+ const mcpViteServer = await createServer({
353
+ root: projectRoot,
354
+ cacheDir: 'node_modules/.vite-mcp',
355
+ plugins: [react(), tailwindcss(), sunpeakEntryPlugin()],
356
+ resolve: {
357
+ alias: {
358
+ '@': path.resolve(projectRoot, 'src'),
359
+ ...(isTemplate && {
360
+ sunpeak: parentSrc,
361
+ }),
368
362
  },
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
- },
363
+ },
364
+ server: {
365
+ middlewareMode: true,
366
+ allowedHosts: true,
367
+ watch: {
368
+ // Only watch files that affect the UI bundle (not JSON, tests, etc.)
369
+ // MCP resources reload on next tool call, not on file change
370
+ ignored: (filePath) => {
371
+ if (!filePath.includes('.')) return false; // Watch directories
372
+ if (/\.(tsx?|css)$/.test(filePath)) {
373
+ return /\.(test|spec)\.tsx?$/.test(filePath); // Ignore tests
374
+ }
375
+ return true; // Ignore everything else
382
376
  },
383
377
  },
384
- optimizeDeps: {
385
- include: ['react', 'react-dom/client'],
386
- },
387
- appType: 'custom',
388
- });
389
- }
378
+ },
379
+ optimizeDeps: {
380
+ include: ['react', 'react-dom/client'],
381
+ },
382
+ appType: 'custom',
383
+ });
390
384
 
391
385
  const pkg = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
392
386
 
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
- }
387
+ const mcpHandle = runMCPServer({
388
+ name: pkg.name || 'Sunpeak',
389
+ version: pkg.version || '0.1.0',
390
+ simulations,
391
+ port: 8000,
392
+ viteServer: mcpViteServer,
393
+ });
495
394
 
496
395
  // Build production bundles and watch for changes.
497
396
  // Tunnel clients (e.g. Claude via ngrok) get the pre-built HTML since they can't
@@ -502,13 +401,13 @@ if (import.meta.hot) {
502
401
 
503
402
  // Handle signals - close both servers
504
403
  process.on('SIGINT', async () => {
505
- if (mcpViteServer) await mcpViteServer.close();
404
+ await mcpViteServer.close();
506
405
  await server.close();
507
406
  process.exit(0);
508
407
  });
509
408
 
510
409
  process.on('SIGTERM', async () => {
511
- if (mcpViteServer) await mcpViteServer.close();
410
+ await mcpViteServer.close();
512
411
  await server.close();
513
412
  process.exit(0);
514
413
  });
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)
@@ -1368,10 +1368,6 @@
1368
1368
  background-color: #0000;
1369
1369
  }
1370
1370
 
1371
- .bg-white {
1372
- background-color: var(--color-white);
1373
- }
1374
-
1375
1371
  .bg-gradient-to-l {
1376
1372
  --tw-gradient-position: to left in oklab;
1377
1373
  background-image: linear-gradient(var(--tw-gradient-stops));
@@ -1672,10 +1668,6 @@
1672
1668
  white-space: pre-wrap;
1673
1669
  }
1674
1670
 
1675
- .text-\[\#000000\] {
1676
- color: #000;
1677
- }
1678
-
1679
1671
  .text-\[var\(--color-text-danger\)\] {
1680
1672
  color: var(--color-text-danger);
1681
1673
  }