sunpeak 0.4.4 → 0.5.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.
Files changed (54) hide show
  1. package/dist/chatgpt/chatgpt-simulator-types.d.ts +1 -1
  2. package/dist/chatgpt/chatgpt-simulator.d.ts +4 -15
  3. package/dist/chatgpt/index.d.ts +1 -1
  4. package/dist/chatgpt/mock-openai.d.ts +4 -16
  5. package/dist/index.cjs +69 -40
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.js +69 -40
  8. package/dist/index.js.map +1 -1
  9. package/dist/mcp/index.cjs +76 -112
  10. package/dist/mcp/index.cjs.map +1 -1
  11. package/dist/mcp/index.d.ts +2 -1
  12. package/dist/mcp/index.js +76 -112
  13. package/dist/mcp/index.js.map +1 -1
  14. package/dist/mcp/server.d.ts +2 -4
  15. package/dist/mcp/types.d.ts +16 -62
  16. package/dist/providers/index.d.ts +1 -3
  17. package/dist/providers/openai/index.d.ts +7 -0
  18. package/dist/{chatgpt/openai-provider.d.ts → providers/openai/provider.d.ts} +1 -1
  19. package/dist/{chatgpt/openai-types.d.ts → providers/openai/types.d.ts} +3 -32
  20. package/dist/providers/types.d.ts +4 -5
  21. package/dist/runtime/index.d.ts +7 -0
  22. package/dist/runtime/provider-detection.d.ts +17 -0
  23. package/dist/types/index.d.ts +2 -1
  24. package/dist/types/runtime.d.ts +34 -0
  25. package/dist/types/simulation.d.ts +47 -0
  26. package/package.json +2 -2
  27. package/template/README.md +6 -7
  28. package/template/dev/main.tsx +38 -3
  29. package/template/mcp/server.ts +9 -6
  30. package/template/nodemon.json +7 -0
  31. package/template/package.json +4 -2
  32. package/template/scripts/build-all.mjs +108 -0
  33. package/template/scripts/validate.mjs +16 -7
  34. package/template/src/components/index.ts +1 -1
  35. package/template/src/components/resources/AlbumsResource.tsx +13 -0
  36. package/template/src/components/{apps/PlacesApp.tsx → resources/CarouselResource.tsx} +11 -10
  37. package/template/src/components/{apps/App.tsx → resources/CounterResource.tsx} +5 -14
  38. package/template/src/components/resources/index.ts +3 -0
  39. package/template/src/index-resource.tsx +11 -0
  40. package/template/src/simulations/albums-simulation.ts +35 -9
  41. package/template/src/simulations/carousel-simulation.ts +35 -9
  42. package/template/src/simulations/counter-simulation.ts +41 -0
  43. package/template/src/simulations/index.ts +10 -10
  44. package/template/vite.config.build.ts +21 -15
  45. package/dist/chatgpt/mcp-provider.d.ts +0 -25
  46. package/dist/style.css +0 -4420
  47. package/template/src/components/apps/AlbumsApp.tsx +0 -13
  48. package/template/src/components/apps/active-app.ts +0 -11
  49. package/template/src/components/apps/index.ts +0 -3
  50. package/template/src/index.chatgpt.tsx +0 -9
  51. package/template/src/index.ts +0 -2
  52. package/template/src/simulations/app-configs.ts +0 -30
  53. package/template/src/simulations/app-simulation.ts +0 -15
  54. package/template/src/simulations/simulations.ts +0 -74
@@ -1,15 +1,16 @@
1
1
  import * as React from "react"
2
2
  import { useWidgetProps } from "sunpeak"
3
- import { Carousel, Card } from ".."
3
+ import { Carousel } from "../carousel/carousel"
4
+ import { Card } from "../card/card"
4
5
 
5
6
  /**
6
- * Production-ready Places App
7
+ * Production-ready Carousel Resource
7
8
  *
8
- * This app displays places in a carousel layout with cards.
9
+ * This resource displays places in a carousel layout with cards.
9
10
  * Can be dropped into any production environment without changes.
10
11
  */
11
12
 
12
- export interface Place {
13
+ interface CarouselCard {
13
14
  id: string
14
15
  name: string
15
16
  rating: number
@@ -19,17 +20,17 @@ export interface Place {
19
20
  description: string
20
21
  }
21
22
 
22
- export interface PlacesData extends Record<string, unknown> {
23
- places: Place[]
23
+ interface CarouselData extends Record<string, unknown> {
24
+ places: CarouselCard[]
24
25
  }
25
26
 
26
- export const PlacesApp = React.forwardRef<HTMLDivElement>((_props, ref) => {
27
- const data = useWidgetProps<PlacesData>(() => ({ places: [] }))
27
+ export const CarouselResource = React.forwardRef<HTMLDivElement>((_props, ref) => {
28
+ const data = useWidgetProps<CarouselData>(() => ({ places: [] }))
28
29
 
29
30
  return (
30
31
  <div ref={ref}>
31
32
  <Carousel gap={16} showArrows={true} showEdgeGradients={true} cardWidth={220}>
32
- {(data.places || []).map((place) => (
33
+ {(data.places || []).map((place: CarouselCard) => (
33
34
  <Card
34
35
  key={place.id}
35
36
  image={place.image}
@@ -54,4 +55,4 @@ export const PlacesApp = React.forwardRef<HTMLDivElement>((_props, ref) => {
54
55
  </div>
55
56
  )
56
57
  })
57
- PlacesApp.displayName = "PlacesApp"
58
+ CarouselResource.displayName = "CarouselResource"
@@ -1,5 +1,3 @@
1
- import '../../styles/globals.css';
2
-
3
1
  import { useWidgetState } from 'sunpeak';
4
2
  import { Button } from '@openai/apps-sdk-ui/components/Button';
5
3
 
@@ -8,19 +6,12 @@ interface CounterState extends Record<string, unknown> {
8
6
  }
9
7
 
10
8
  /**
11
- * Welcome to your Sunpeak App!
12
- *
13
- * This is a simple counter app to get you started.
14
- * Try building your own app here!
9
+ * Production-ready Counter Resource
15
10
  *
16
- * Tips:
17
- * - Use the Component dropdown in the sidebar to see example apps
18
- * - Check out the components folder for reusable components
19
- * - Use sunpeak hooks for state management and display modes
20
- * - Edit this file and see your changes live
21
- * - Edit ../../simulations/app-simulation.tsx to customize your simulation
11
+ * This resource displays a counter to demonstrate useWidgetState.
12
+ * Can be dropped into any production environment without changes.
22
13
  */
23
- export function App() {
14
+ export function CounterResource() {
24
15
  const [widgetState, setWidgetState] = useWidgetState<CounterState>(() => ({
25
16
  count: 0,
26
17
  }));
@@ -46,7 +37,7 @@ export function App() {
46
37
  Welcome to Sunpeak!
47
38
  </h1>
48
39
  <p className="text-secondary">
49
- Build your ChatGPT app here
40
+ Build your MCP resource here
50
41
  </p>
51
42
  </div>
52
43
 
@@ -0,0 +1,3 @@
1
+ export { CounterResource } from './CounterResource';
2
+ export { AlbumsResource } from './AlbumsResource';
3
+ export { CarouselResource } from './CarouselResource';
@@ -0,0 +1,11 @@
1
+ import '../src/styles/globals.css';
2
+ // @ts-expect-error - Template file with placeholders
3
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
4
+ import { createRoot } from 'react-dom/client';
5
+ // RESOURCE_IMPORT
6
+
7
+ // Mount the resource
8
+ const root = document.getElementById('root');
9
+ if (root) {
10
+ // RESOURCE_MOUNT
11
+ }
@@ -116,14 +116,40 @@ const albumsData = {
116
116
  ]
117
117
  };
118
118
 
119
- export const albumsSimulationConfig = {
120
- appName: 'Pizzaz',
121
- value: 'albums',
122
- label: 'Albums',
123
- appIcon: '🍕',
119
+ export const albumsSimulation = {
124
120
  userMessage: 'Pizza time',
125
- toolOutput: albumsData,
126
- widgetState: null,
127
- toolName: 'show-app',
128
- toolDescription: 'Show photo albums',
121
+
122
+ // MCP Tool protocol - official Tool type from MCP SDK used in ListTools response
123
+ tool: {
124
+ name: 'show-albums',
125
+ description: 'Show photo albums',
126
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false } as const,
127
+ title: 'Show Albums',
128
+ annotations: { readOnlyHint: true },
129
+ _meta: {
130
+ 'openai/outputTemplate': 'ui://AlbumsResource.tsx',
131
+ 'openai/toolInvocation/invoking': 'Loading albums',
132
+ 'openai/toolInvocation/invoked': 'Album loaded',
133
+ 'openai/widgetAccessible': true,
134
+ 'openai/resultCanProduceWidget': true,
135
+ },
136
+ },
137
+
138
+ // MCP Resource protocol - official Resource type from MCP SDK used in ListResources response
139
+ // resource.name is used as the simulation identifier
140
+ // resource.title is used as the simulation display label
141
+ resource: {
142
+ uri: 'ui://AlbumsResource.tsx',
143
+ name: 'albums',
144
+ title: 'Albums',
145
+ description: 'Show photo albums widget markup',
146
+ mimeType: 'text/html+skybridge',
147
+ _meta: {},
148
+ },
149
+
150
+ // MCP CallTool protocol - data for CallTool response
151
+ toolCall: {
152
+ structuredContent: albumsData,
153
+ _meta: {},
154
+ },
129
155
  } as const;
@@ -53,14 +53,40 @@ const placesData = {
53
53
  ]
54
54
  };
55
55
 
56
- export const carouselSimulationConfig = {
57
- appName: 'Splorin',
58
- value: 'carousel',
59
- label: 'Carousel',
60
- appIcon: '✈️',
56
+ export const carouselSimulation = {
61
57
  userMessage: 'Show me popular places to visit in Austin Texas',
62
- toolOutput: placesData,
63
- widgetState: null,
64
- toolName: 'show-app',
65
- toolDescription: 'Show popular places to visit in Austin, Texas',
58
+
59
+ // MCP Tool protocol - official Tool type from MCP SDK used in ListTools response
60
+ tool: {
61
+ name: 'show-carousel',
62
+ description: 'Show popular places to visit',
63
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false } as const,
64
+ title: 'Show Carousel',
65
+ annotations: { readOnlyHint: true },
66
+ _meta: {
67
+ 'openai/outputTemplate': 'ui://CarouselResource.tsx',
68
+ 'openai/toolInvocation/invoking': 'Loading carousel',
69
+ 'openai/toolInvocation/invoked': 'Carousel loaded',
70
+ 'openai/widgetAccessible': true,
71
+ 'openai/resultCanProduceWidget': true,
72
+ },
73
+ },
74
+
75
+ // MCP Resource protocol - official Resource type from MCP SDK used in ListResources response
76
+ // resource.name is used as the simulation identifier
77
+ // resource.title is used as the simulation display label
78
+ resource: {
79
+ uri: 'ui://CarouselResource.tsx',
80
+ name: 'carousel',
81
+ title: 'Carousel',
82
+ description: 'Show popular places to visit widget markup',
83
+ mimeType: 'text/html+skybridge',
84
+ _meta: {},
85
+ },
86
+
87
+ // MCP CallTool protocol - data for CallTool response
88
+ toolCall: {
89
+ structuredContent: placesData,
90
+ _meta: {},
91
+ },
66
92
  } as const;
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Server-safe configuration for the counter simulation.
3
+ * This file contains only metadata and doesn't import React components or CSS.
4
+ */
5
+ export const counterSimulation = {
6
+ userMessage: 'Help me count something',
7
+
8
+ // MCP Tool protocol - official Tool type from MCP SDK used in ListTools response
9
+ tool: {
10
+ name: 'show-counter',
11
+ description: 'Show a simple counter tool',
12
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false } as const,
13
+ title: 'Show Counter',
14
+ annotations: { readOnlyHint: true },
15
+ _meta: {
16
+ 'openai/outputTemplate': 'ui://CounterResource.tsx',
17
+ 'openai/toolInvocation/invoking': 'Counting beans',
18
+ 'openai/toolInvocation/invoked': 'Beans counted',
19
+ 'openai/widgetAccessible': true,
20
+ 'openai/resultCanProduceWidget': true,
21
+ },
22
+ },
23
+
24
+ // MCP Resource protocol - official Resource type from MCP SDK used in ListResources response
25
+ // resource.name is used as the simulation identifier
26
+ // resource.title is used as the simulation display label
27
+ resource: {
28
+ uri: 'ui://CounterResource.tsx',
29
+ name: 'counter',
30
+ title: 'Counter',
31
+ description: 'Show a simple counter tool widget markup',
32
+ mimeType: 'text/html+skybridge',
33
+ _meta: {},
34
+ },
35
+
36
+ // MCP CallTool protocol - data for CallTool response
37
+ toolCall: {
38
+ structuredContent: null,
39
+ _meta: {},
40
+ },
41
+ } as const;
@@ -1,16 +1,16 @@
1
1
  /**
2
- * Simulation configurations
2
+ * Server-safe simulation configurations
3
3
  *
4
- * These configs contain only metadata and can be safely imported in Node.js contexts
4
+ * This file contains only metadata and can be safely imported in Node.js contexts
5
5
  * (like MCP servers) without causing issues with CSS imports or React components.
6
6
  */
7
7
 
8
- export { appSimulationConfig } from './app-simulation';
9
- export { carouselSimulationConfig } from './carousel-simulation';
10
- export { albumsSimulationConfig } from './albums-simulation';
8
+ import { counterSimulation } from './counter-simulation';
9
+ import { albumsSimulation } from './albums-simulation';
10
+ import { carouselSimulation } from './carousel-simulation';
11
11
 
12
- /**
13
- * Simulations - DO NOT import in Node.js/MCP server contexts!
14
- * These include React components and CSS imports.
15
- */
16
- export { appSimulation, albumsSimulation, carouselSimulation, simulations } from './simulations';
12
+ export const SIMULATIONS = {
13
+ counter: counterSimulation,
14
+ albums: albumsSimulation,
15
+ carousel: carouselSimulation,
16
+ } as const;
@@ -2,30 +2,35 @@ import { defineConfig } from 'vite';
2
2
  import react from '@vitejs/plugin-react';
3
3
  import path from 'path';
4
4
  import tailwindcss from '@tailwindcss/vite';
5
- import { readFileSync, writeFileSync, unlinkSync, existsSync } from 'fs';
5
+ import { readFileSync, writeFileSync, unlinkSync, existsSync, readdirSync } from 'fs';
6
6
 
7
7
  // Check if we're in the sunpeak workspace (directory is named "template")
8
8
  const isTemplate = path.basename(__dirname) === 'template';
9
9
  const parentSrc = path.resolve(__dirname, '../src');
10
10
 
11
- // Plugin to inline CSS into the JS bundle
11
+ // Plugin to inline CSS into the JS bundle for all output files
12
12
  function inlineCssPlugin() {
13
13
  return {
14
14
  name: 'inline-css',
15
15
  closeBundle() {
16
- const distDir = path.resolve(__dirname, 'dist/chatgpt');
17
- const jsFile = path.join(distDir, 'index.js');
16
+ const distDir = path.resolve(__dirname, process.env.OUT_DIR || 'dist/chatgpt');
18
17
  const cssFile = path.join(distDir, 'style.css');
19
18
 
20
- if (existsSync(cssFile) && existsSync(jsFile)) {
19
+ if (existsSync(cssFile)) {
21
20
  const css = readFileSync(cssFile, 'utf-8');
22
- const js = readFileSync(jsFile, 'utf-8');
23
-
24
- // Inject CSS at the start of the JS file
25
21
  const injectCss = `(function(){var s=document.createElement('style');s.textContent=${JSON.stringify(css)};document.head.appendChild(s);})();`;
26
- writeFileSync(jsFile, injectCss + js);
27
22
 
28
- // Remove the separate CSS file
23
+ // Find all .js files in the dist directory and inject CSS
24
+ const files = readdirSync(distDir);
25
+ files.forEach(file => {
26
+ if (file.endsWith('.js')) {
27
+ const jsFile = path.join(distDir, file);
28
+ const js = readFileSync(jsFile, 'utf-8');
29
+ writeFileSync(jsFile, injectCss + js);
30
+ }
31
+ });
32
+
33
+ // Remove the separate CSS file after injecting into all bundles
29
34
  unlinkSync(cssFile);
30
35
  }
31
36
  },
@@ -49,18 +54,19 @@ export default defineConfig({
49
54
  },
50
55
  build: {
51
56
  target: 'es2020',
57
+ outDir: process.env.OUT_DIR || 'dist/chatgpt',
58
+ emptyOutDir: process.env.OUT_DIR ? true : false,
59
+ cssCodeSplit: false,
52
60
  lib: {
53
- entry: path.resolve(__dirname, 'src/index.chatgpt.tsx'),
61
+ entry: path.resolve(__dirname, process.env.ENTRY_FILE || 'src/index-app.tsx'),
54
62
  name: 'SunpeakApp',
55
63
  formats: ['iife'],
56
- fileName: () => 'index.js',
64
+ fileName: () => process.env.OUTPUT_FILE || 'app.js',
57
65
  },
58
- outDir: 'dist/chatgpt',
59
- cssCodeSplit: false,
60
66
  rollupOptions: {
61
67
  output: {
62
68
  inlineDynamicImports: true,
63
- assetFileNames: '[name][extname]',
69
+ assetFileNames: 'style.css',
64
70
  },
65
71
  },
66
72
  minify: true,
@@ -1,25 +0,0 @@
1
- import { Resource, Tool } from '@modelcontextprotocol/sdk/types.js';
2
- import { MCPProviderImplementation, WidgetDescriptorMeta, WidgetInvocationMeta } from '../mcp/types.js';
3
- /**
4
- * ChatGPT MCP provider implementation.
5
- */
6
- export declare class ChatGPTMCPProvider implements MCPProviderImplementation {
7
- getWidgetDescriptorMeta(): WidgetDescriptorMeta;
8
- getWidgetInvocationMeta(): WidgetInvocationMeta;
9
- readWidgetContent(distPath: string): string;
10
- getWidgetMimeType(): string;
11
- getWidgetResourceUri(): string;
12
- createTool(config: {
13
- name: string;
14
- description: string;
15
- inputSchema: Tool["inputSchema"];
16
- }): Tool;
17
- createResource(config: {
18
- name: string;
19
- description: string;
20
- }): Resource;
21
- }
22
- /**
23
- * Get the ChatGPT MCP provider instance.
24
- */
25
- export declare function getChatGPTMCPProvider(): MCPProviderImplementation;