sunpeak 0.3.6 โ 0.3.8
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 +26 -33
- package/bin/sunpeak.js +2 -2
- package/package.json +5 -2
- package/template/README.md +12 -17
- package/template/package.json +3 -2
- package/template/scripts/validate.mjs +171 -0
package/README.md
CHANGED
|
@@ -17,9 +17,17 @@
|
|
|
17
17
|
|
|
18
18
|
The ChatGPT Apps SDK.
|
|
19
19
|
|
|
20
|
-
Build and test your
|
|
20
|
+
Build and test your MCP App UI locally with OpenAI apps-sdk-ui React components.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
[Documentation](https://docs.sunpeak.ai/)
|
|
23
|
+
|
|
24
|
+
<div align="center">
|
|
25
|
+
<a href="https://docs.sunpeak.ai/library/chatgpt-simulator">
|
|
26
|
+
<picture>
|
|
27
|
+
<img alt="ChatGPT Simulator" src="https://sunpeak.ai/images/chatgpt-simulator.png">
|
|
28
|
+
</picture>
|
|
29
|
+
</a>
|
|
30
|
+
</div>
|
|
23
31
|
|
|
24
32
|
## Quickstart
|
|
25
33
|
|
|
@@ -28,30 +36,17 @@ Build and test your ChatGPT App UI locally with OpenAI apps-sdk-ui React compone
|
|
|
28
36
|
Requirements: Node (20+), pnpm (10+)
|
|
29
37
|
|
|
30
38
|
```bash
|
|
31
|
-
pnpm dlx sunpeak
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Existing Projects
|
|
35
|
-
|
|
36
|
-
Requirements: React (18+), Tailwind 4
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
pnpm add sunpeak
|
|
39
|
+
pnpm dlx sunpeak new
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
```tsx
|
|
45
|
-
import 'sunpeak/style.css';
|
|
46
|
-
import { ChatGPTSimulator } from 'sunpeak';
|
|
47
|
-
```
|
|
42
|
+
To add sunpeak to an existing project, refer to the [documentation](https://docs.sunpeak.ai/add-to-existing-project).
|
|
48
43
|
|
|
49
44
|
## Key Features
|
|
50
|
-
- ๐บ ChatGPT simulator for local UI component development.
|
|
51
|
-
- ๐ Pre-built component library built on [openai/apps-sdk-ui](https://github.com/openai/apps-sdk-ui).
|
|
52
|
-
- ๐ฑ
|
|
53
|
-
- ๐ Basic MCP server to serve your UI to ChatGPT prod out-of-the-box.
|
|
54
|
-
- ๐งช Testing framework that replicates advanced ChatGPT behavior locally.
|
|
45
|
+
- ๐บ [ChatGPT simulator](https://docs.sunpeak.ai/library/chatgpt-simulator) for local UI component development.
|
|
46
|
+
- ๐ [Pre-built component library](https://docs.sunpeak.ai/template/ui-components) built on [openai/apps-sdk-ui](https://github.com/openai/apps-sdk-ui).
|
|
47
|
+
- ๐ฑ [Multi-platform interface](https://docs.sunpeak.ai/library/multi-platform-apis) for portable MCP UI App development.
|
|
48
|
+
- ๐ [Basic MCP server](https://docs.sunpeak.ai/library/mcp-server) to serve your UI to ChatGPT prod out-of-the-box.
|
|
49
|
+
- ๐งช [Testing framework](https://docs.sunpeak.ai/guides/testing) that replicates advanced ChatGPT behavior locally.
|
|
55
50
|
|
|
56
51
|
## Example Component
|
|
57
52
|
```tsx
|
|
@@ -74,21 +69,18 @@ export default function App() {
|
|
|
74
69
|
}
|
|
75
70
|
```
|
|
76
71
|
|
|
77
|
-
## Supported Platforms
|
|
78
|
-
|
|
79
|
-
- โ
**OpenAI ChatGPT** - Fully supported ([design guidelines](https://developers.openai.com/apps-sdk/concepts/design-guidelines))
|
|
80
|
-
- ๐ **Google Gemini** - Design system available (SDK support coming soon)
|
|
81
|
-
- ๐ **Anthropic Claude** - Design system available (SDK support coming soon)
|
|
82
|
-
- ๐ง **Custom platforms** - Implement your own platform adapter
|
|
83
|
-
|
|
84
72
|
## What is sunpeak exactly?
|
|
85
73
|
|
|
86
74
|
sunpeak is an npm package consisting of:
|
|
87
75
|
|
|
88
76
|
1. **A CLI utility** for working with sunpeak (`./bin`).
|
|
89
|
-
2. **A
|
|
90
|
-
|
|
91
|
-
|
|
77
|
+
2. **A templated npm package** (`./template`). This template includes:
|
|
78
|
+
1. Project scaffold - Complete development setup with build, test, and dev tooling.
|
|
79
|
+
2. Pre-built UI components - Production-ready components following ChatGPT design guidelines.
|
|
80
|
+
3. **The `sunpeak` library** (`./src`). This library contains:
|
|
81
|
+
1. Multi-platform APIs - Abstraction layer for future platform support (Gemini, Claude).
|
|
82
|
+
2. ChatGPT simulator - Local development environment replicating ChatGPT's widget runtime.
|
|
83
|
+
3. MCP server - View local widgets in the real ChatGPT.
|
|
92
84
|
|
|
93
85
|
## Contributing
|
|
94
86
|
|
|
@@ -99,6 +91,7 @@ For development quickstart on this package, see [DEVELOPMENT.md](./DEVELOPMENT.m
|
|
|
99
91
|
## Resources
|
|
100
92
|
|
|
101
93
|
- [ChatGPT Apps SDK Design Guidelines](https://developers.openai.com/apps-sdk/concepts/design-guidelines)
|
|
102
|
-
- [ChatGPT Apps SDK UI
|
|
94
|
+
- [ChatGPT Apps SDK Documentation - UI](https://developers.openai.com/apps-sdk/build/chatgpt-ui)
|
|
103
95
|
- [ChatGPT Apps SDK window.openai Reference](https://developers.openai.com/apps-sdk/build/mcp-server#understand-the-windowopenai-widget-runtime)
|
|
104
96
|
- [ChatGPT Apps SDK Examples](https://github.com/openai/openai-apps-sdk-examples)
|
|
97
|
+
- [ChatGPT Apps SDK UI Documentation](https://openai.github.io/apps-sdk-ui/)
|
package/bin/sunpeak.js
CHANGED
|
@@ -80,13 +80,13 @@ See README.md for more details.
|
|
|
80
80
|
|
|
81
81
|
const [,, command, ...args] = process.argv;
|
|
82
82
|
|
|
83
|
-
if (command === '
|
|
83
|
+
if (command === 'new') {
|
|
84
84
|
init(args[0]);
|
|
85
85
|
} else {
|
|
86
86
|
console.log(`
|
|
87
87
|
sunpeak - ChatGPT Apps UI SDK
|
|
88
88
|
|
|
89
89
|
Commands:
|
|
90
|
-
|
|
90
|
+
new [name] Create a new project from template
|
|
91
91
|
`);
|
|
92
92
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sunpeak",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.8",
|
|
4
4
|
"description": "The ChatGPT Apps UI SDK. Build and test your ChatGPT App UI locally with OpenAI apps-sdk-ui components.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"zod": "^3.23.8"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
+
"@playwright/test": "^1.56.1",
|
|
69
70
|
"@tailwindcss/vite": "^4.1.17",
|
|
70
71
|
"@testing-library/jest-dom": "^6.9.1",
|
|
71
72
|
"@testing-library/react": "^16.3.0",
|
|
@@ -105,6 +106,8 @@
|
|
|
105
106
|
"dev": "pnpm --filter my-sunpeak-app dev",
|
|
106
107
|
"lint": "eslint . --ext .ts,.tsx --fix",
|
|
107
108
|
"typecheck": "tsc --noEmit",
|
|
108
|
-
"test": "vitest run"
|
|
109
|
+
"test": "vitest run",
|
|
110
|
+
"test:e2e": "playwright test",
|
|
111
|
+
"validate": "node scripts/validate.mjs"
|
|
109
112
|
}
|
|
110
113
|
}
|
package/template/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
A ChatGPT App UI built with [sunpeak](https://github.com/Sunpeak-AI/sunpeak).
|
|
4
4
|
|
|
5
|
+
For an initial overview of your new app and the sunpeak API, refer to the [documentation](https://docs.sunpeak.ai/template/project-structure).
|
|
6
|
+
|
|
5
7
|
## Quickstart
|
|
6
8
|
|
|
7
9
|
```bash
|
|
@@ -12,31 +14,24 @@ Edit [src/App.tsx](./src/App.tsx) to build your app UI.
|
|
|
12
14
|
|
|
13
15
|
## Development
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
## Testing
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
src/
|
|
19
|
-
โโโ App.tsx # Your main app component
|
|
20
|
-
โโโ components/ # Your React components
|
|
19
|
+
### Testing Locally
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
โโโ server.ts # MCP server for testing in ChatGPT
|
|
21
|
+
Run all the checks with the following:
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
```bash
|
|
24
|
+
pnpm validate
|
|
27
25
|
```
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
This will:
|
|
28
|
+
- Run linting, typechecking, and unit tests
|
|
29
|
+
- Build your app
|
|
30
|
+
- Verify that build outputs are created correctly
|
|
32
31
|
|
|
33
|
-
|
|
32
|
+
For manual QA of the UI, run:
|
|
34
33
|
|
|
35
34
|
```bash
|
|
36
|
-
pnpm lint
|
|
37
|
-
pnpm typecheck
|
|
38
|
-
pnpm test
|
|
39
|
-
pnpm build
|
|
40
35
|
pnpm dev
|
|
41
36
|
```
|
|
42
37
|
|
package/template/package.json
CHANGED
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"build": "vite build --config vite.config.build.ts",
|
|
8
|
-
"dev": "vite",
|
|
8
|
+
"dev": "vite --port ${PORT:-6767}",
|
|
9
9
|
"mcp": "tsx mcp/server.ts",
|
|
10
10
|
"lint": "eslint . --ext .ts,.tsx --fix",
|
|
11
11
|
"typecheck": "tsc --noEmit",
|
|
12
|
-
"test": "vitest run"
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"validate": "node scripts/validate.mjs"
|
|
13
14
|
},
|
|
14
15
|
"dependencies": {
|
|
15
16
|
"@openai/apps-sdk-ui": "^0.2.0",
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Local testing script for Sunpeak project.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { execSync, spawn } from 'child_process';
|
|
8
|
+
import { existsSync, readdirSync } from 'fs';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
import { dirname, join } from 'path';
|
|
11
|
+
import http from 'http';
|
|
12
|
+
|
|
13
|
+
// Color codes for output
|
|
14
|
+
const colors = {
|
|
15
|
+
red: '\x1b[0;31m',
|
|
16
|
+
green: '\x1b[0;32m',
|
|
17
|
+
blue: '\x1b[0;34m',
|
|
18
|
+
yellow: '\x1b[1;33m',
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Get project root
|
|
23
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
24
|
+
const __dirname = dirname(__filename);
|
|
25
|
+
const PROJECT_ROOT = join(__dirname, '..');
|
|
26
|
+
|
|
27
|
+
function printSuccess(text) {
|
|
28
|
+
console.log(`${colors.green}โ ${text}${colors.reset}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function printError(text) {
|
|
32
|
+
console.log(`${colors.red}โ ${text}${colors.reset}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function runCommand(command, cwd) {
|
|
36
|
+
try {
|
|
37
|
+
execSync(command, {
|
|
38
|
+
cwd,
|
|
39
|
+
stdio: 'inherit',
|
|
40
|
+
env: { ...process.env, FORCE_COLOR: '1' },
|
|
41
|
+
});
|
|
42
|
+
return true;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function waitForServer(port, timeout = 10000) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const startTime = Date.now();
|
|
51
|
+
const checkServer = () => {
|
|
52
|
+
const req = http.get(`http://localhost:${port}`, () => {
|
|
53
|
+
resolve();
|
|
54
|
+
});
|
|
55
|
+
req.on('error', () => {
|
|
56
|
+
if (Date.now() - startTime > timeout) {
|
|
57
|
+
reject(new Error(`Server did not start within ${timeout}ms`));
|
|
58
|
+
} else {
|
|
59
|
+
setTimeout(checkServer, 500);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
req.end();
|
|
63
|
+
};
|
|
64
|
+
checkServer();
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Main testing flow
|
|
69
|
+
console.log(`${colors.yellow}Starting local testing for Sunpeak project...${colors.reset}`);
|
|
70
|
+
console.log(`Project root: ${PROJECT_ROOT}\n`);
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
console.log('Running: pnpm install');
|
|
74
|
+
if (!runCommand('pnpm install', PROJECT_ROOT)) {
|
|
75
|
+
throw new Error('pnpm install failed');
|
|
76
|
+
}
|
|
77
|
+
console.log()
|
|
78
|
+
printSuccess('pnpm install');
|
|
79
|
+
|
|
80
|
+
console.log('\nRunning: pnpm lint');
|
|
81
|
+
if (!runCommand('pnpm lint', PROJECT_ROOT)) {
|
|
82
|
+
throw new Error('pnpm lint failed');
|
|
83
|
+
}
|
|
84
|
+
printSuccess('pnpm lint');
|
|
85
|
+
|
|
86
|
+
console.log('\nRunning: pnpm typecheck');
|
|
87
|
+
if (!runCommand('pnpm typecheck', PROJECT_ROOT)) {
|
|
88
|
+
throw new Error('pnpm typecheck failed');
|
|
89
|
+
}
|
|
90
|
+
printSuccess('pnpm typecheck');
|
|
91
|
+
|
|
92
|
+
console.log('\nRunning: pnpm test');
|
|
93
|
+
if (!runCommand('pnpm test', PROJECT_ROOT)) {
|
|
94
|
+
throw new Error('pnpm test failed');
|
|
95
|
+
}
|
|
96
|
+
printSuccess('pnpm test');
|
|
97
|
+
|
|
98
|
+
console.log('\nRunning: pnpm build');
|
|
99
|
+
if (!runCommand('pnpm build', PROJECT_ROOT)) {
|
|
100
|
+
throw new Error('pnpm build failed');
|
|
101
|
+
}
|
|
102
|
+
const chatgptDir = join(PROJECT_ROOT, 'dist', 'chatgpt');
|
|
103
|
+
const builtFile = join(chatgptDir, 'index.js');
|
|
104
|
+
if (!existsSync(builtFile)) {
|
|
105
|
+
printError('Missing expected file: ./dist/chatgpt/index.js');
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
const files = readdirSync(chatgptDir);
|
|
109
|
+
if (files.length !== 1 || files[0] !== 'index.js') {
|
|
110
|
+
printError(`Unexpected files in ./dist/chatgpt/: ${files.join(', ')}`);
|
|
111
|
+
printError('Expected only: index.js');
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
console.log()
|
|
115
|
+
printSuccess('pnpm build');
|
|
116
|
+
|
|
117
|
+
// MCP Server Check
|
|
118
|
+
console.log('\nRunning: pnpm mcp');
|
|
119
|
+
const mcpProcess = spawn('pnpm', ['mcp'], {
|
|
120
|
+
cwd: PROJECT_ROOT,
|
|
121
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
122
|
+
env: { ...process.env, FORCE_COLOR: '1' },
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
const mcpErrors = [];
|
|
126
|
+
|
|
127
|
+
mcpProcess.stderr.on('data', (data) => {
|
|
128
|
+
const message = data.toString();
|
|
129
|
+
if (message.includes('error') || message.includes('Error')) {
|
|
130
|
+
mcpErrors.push(message.trim());
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
// Store process for cleanup
|
|
135
|
+
process.on('exit', () => {
|
|
136
|
+
if (mcpProcess && !mcpProcess.killed) {
|
|
137
|
+
mcpProcess.kill();
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
console.log('\nWaiting for MCP server to start on port 6766...');
|
|
143
|
+
await waitForServer(6766, 10000);
|
|
144
|
+
|
|
145
|
+
// Give it a moment to ensure no immediate errors
|
|
146
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
147
|
+
|
|
148
|
+
if (mcpErrors.length > 0) {
|
|
149
|
+
printError('MCP server started but reported errors:');
|
|
150
|
+
mcpErrors.forEach(err => console.log(` ${err}`));
|
|
151
|
+
throw new Error('MCP server has errors');
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
} catch (error) {
|
|
155
|
+
printError(`MCP server failed to start: ${error.message}`);
|
|
156
|
+
throw error;
|
|
157
|
+
} finally {
|
|
158
|
+
console.log('Stopping MCP server...');
|
|
159
|
+
mcpProcess.kill();
|
|
160
|
+
// Give it a moment to shut down
|
|
161
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
162
|
+
}
|
|
163
|
+
console.log()
|
|
164
|
+
printSuccess('pnpm mcp\n');
|
|
165
|
+
|
|
166
|
+
printSuccess('All systems GO!\n\n');
|
|
167
|
+
process.exit(0);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.error(`\n${colors.red}Error: ${error.message}${colors.reset}\n`);
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|