sunpeak 0.11.1 → 0.12.1
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 +84 -12
- package/bin/commands/build.mjs +1 -11
- package/bin/commands/deploy.mjs +7 -1
- package/bin/commands/dev.mjs +3 -1
- package/bin/commands/new.mjs +211 -0
- package/bin/commands/pull.mjs +1 -1
- package/bin/commands/push.mjs +36 -13
- package/bin/lib/patterns.mjs +25 -0
- package/bin/sunpeak.js +13 -222
- package/package.json +1 -1
- package/template/.sunpeak/dev.tsx +4 -4
- package/template/README.md +21 -22
- package/template/dist/albums/albums.json +1 -1
- package/template/dist/carousel/carousel.json +1 -1
- package/template/dist/map/map.json +1 -1
- package/template/dist/review/review.json +1 -1
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Button.js +7 -7
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_SegmentedControl.js +1 -1
- package/template/node_modules/.vite/deps/@openai_apps-sdk-ui_components_Select.js +7 -7
- package/template/node_modules/.vite/deps/_metadata.json +30 -30
- package/template/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/template/vitest.config.ts +1 -1
- package/template/src/resources/albums/albums-show-simulation.json +0 -131
- package/template/src/resources/carousel/carousel-show-simulation.json +0 -68
- package/template/src/resources/map/map-show-simulation.json +0 -123
- package/template/src/resources/review/review-diff-simulation.json +0 -80
- package/template/src/resources/review/review-post-simulation.json +0 -56
- package/template/src/resources/review/review-purchase-simulation.json +0 -88
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Avatar.js +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Avatar.js.map +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Button.js +10 -10
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Button.js.map +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Checkbox.js +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Checkbox.js.map +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Icon.js +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Icon.js.map +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Input.js +1 -1
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Input.js.map +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_SegmentedControl.js +4 -4
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_SegmentedControl.js.map +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Select.js +9 -9
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Select.js.map +0 -0
- package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Textarea.js +1 -1
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_components_Textarea.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_theme.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/@openai_apps-sdk-ui_theme.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-2UDYPUBJ.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-2UDYPUBJ.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-6QVG4F2X.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-6QVG4F2X.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-BUOVMFCD.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-BUOVMFCD.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-CNYJBM5F.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-CNYJBM5F.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-EGRHWZRV.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-EGRHWZRV.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-ILHRZGIS.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-ILHRZGIS.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-JAGHY6H6.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-JAGHY6H6.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-JGVISENQ.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-JGVISENQ.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-P5LK4A7U.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-P5LK4A7U.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-QPJAV452.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-QPJAV452.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-RYYR2YMB.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-RYYR2YMB.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-UM3ZGDFR.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-UM3ZGDFR.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-XZTIOEPG.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/chunk-XZTIOEPG.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/clsx.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/clsx.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/embla-carousel-react.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/embla-carousel-react.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/embla-carousel-wheel-gestures.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/embla-carousel-wheel-gestures.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/mapbox-gl.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/mapbox-gl.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/package.json +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react-dom.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react-dom.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react-dom_client.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react-dom_client.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react_jsx-dev-runtime.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react_jsx-dev-runtime.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react_jsx-runtime.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/react_jsx-runtime.js.map +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/tailwind-merge.js +0 -0
- /package/template/node_modules/.vite-mcp/{deps_temp_c8c8077a → deps_temp_992accd8}/tailwind-merge.js.map +0 -0
- /package/template/{src/test → tests}/setup.ts +0 -0
- /package/template/{dist → tests/simulations}/albums/albums-show-simulation.json +0 -0
- /package/template/{dist → tests/simulations}/carousel/carousel-show-simulation.json +0 -0
- /package/template/{dist → tests/simulations}/map/map-show-simulation.json +0 -0
- /package/template/{dist → tests/simulations}/review/review-diff-simulation.json +0 -0
- /package/template/{dist → tests/simulations}/review/review-post-simulation.json +0 -0
- /package/template/{dist → tests/simulations}/review/review-purchase-simulation.json +0 -0
package/bin/sunpeak.js
CHANGED
|
@@ -1,49 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
mkdirSync,
|
|
6
|
-
cpSync,
|
|
7
|
-
readFileSync,
|
|
8
|
-
writeFileSync,
|
|
9
|
-
renameSync,
|
|
10
|
-
readdirSync,
|
|
11
|
-
} from 'fs';
|
|
12
|
-
import { join, dirname, basename } from 'path';
|
|
3
|
+
import { existsSync, readFileSync } from 'fs';
|
|
4
|
+
import { join, dirname } from 'path';
|
|
13
5
|
import { fileURLToPath } from 'url';
|
|
14
|
-
import {
|
|
6
|
+
import { discoverResources } from './lib/patterns.mjs';
|
|
15
7
|
|
|
16
8
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
17
9
|
const COMMANDS_DIR = join(__dirname, 'commands');
|
|
18
10
|
|
|
19
|
-
/**
|
|
20
|
-
* Auto-discover available resources from template/src/resources directories.
|
|
21
|
-
* Each subdirectory containing a {name}-resource.tsx file is a valid resource.
|
|
22
|
-
*/
|
|
23
|
-
function discoverResources() {
|
|
24
|
-
const resourcesDir = join(__dirname, '..', 'template', 'src', 'resources');
|
|
25
|
-
if (!existsSync(resourcesDir)) {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
return readdirSync(resourcesDir, { withFileTypes: true })
|
|
29
|
-
.filter((entry) => entry.isDirectory())
|
|
30
|
-
.filter((entry) => {
|
|
31
|
-
const resourceFile = join(resourcesDir, entry.name, `${entry.name}-resource.tsx`);
|
|
32
|
-
return existsSync(resourceFile);
|
|
33
|
-
})
|
|
34
|
-
.map((entry) => entry.name);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function prompt(question) {
|
|
38
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
39
|
-
return new Promise((resolve) => {
|
|
40
|
-
rl.question(question, (answer) => {
|
|
41
|
-
rl.close();
|
|
42
|
-
resolve(answer.trim());
|
|
43
|
-
});
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
|
|
47
11
|
function checkPackageJson() {
|
|
48
12
|
const pkgPath = join(process.cwd(), 'package.json');
|
|
49
13
|
if (!existsSync(pkgPath)) {
|
|
@@ -53,148 +17,6 @@ function checkPackageJson() {
|
|
|
53
17
|
}
|
|
54
18
|
}
|
|
55
19
|
|
|
56
|
-
function parseResourcesInput(input, validResources) {
|
|
57
|
-
// If no input, return all resources
|
|
58
|
-
if (!input || input.trim() === '') {
|
|
59
|
-
return validResources;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Split by comma or space and trim
|
|
63
|
-
const tokens = input
|
|
64
|
-
.toLowerCase()
|
|
65
|
-
.split(/[,\s]+/)
|
|
66
|
-
.map((s) => s.trim())
|
|
67
|
-
.filter((s) => s.length > 0);
|
|
68
|
-
|
|
69
|
-
// Validate tokens
|
|
70
|
-
const invalid = tokens.filter((t) => !validResources.includes(t));
|
|
71
|
-
if (invalid.length > 0) {
|
|
72
|
-
console.error(`Error: Invalid resource(s): ${invalid.join(', ')}`);
|
|
73
|
-
console.error(`Valid resources are: ${validResources.join(', ')}`);
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Remove duplicates
|
|
78
|
-
return [...new Set(tokens)];
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function init(projectName, resourcesArg) {
|
|
82
|
-
// Discover available resources from template
|
|
83
|
-
const availableResources = discoverResources();
|
|
84
|
-
if (availableResources.length === 0) {
|
|
85
|
-
console.error('Error: No resources found in template/src/resources/');
|
|
86
|
-
process.exit(1);
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
if (!projectName) {
|
|
90
|
-
projectName = await prompt('☀️ 🏔️ Project name [my-app]: ');
|
|
91
|
-
if (!projectName) {
|
|
92
|
-
projectName = 'my-app';
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (projectName === 'template') {
|
|
97
|
-
console.error('Error: "template" is a reserved name. Please choose another name.');
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Use resources from args or ask for them
|
|
102
|
-
let resourcesInput;
|
|
103
|
-
if (resourcesArg) {
|
|
104
|
-
resourcesInput = resourcesArg;
|
|
105
|
-
console.log(`☀️ 🏔️ Resources: ${resourcesArg}`);
|
|
106
|
-
} else {
|
|
107
|
-
resourcesInput = await prompt(
|
|
108
|
-
`☀️ 🏔️ Resources (UIs) to include [${availableResources.join(', ')}]: `
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
const selectedResources = parseResourcesInput(resourcesInput, availableResources);
|
|
112
|
-
|
|
113
|
-
const targetDir = join(process.cwd(), projectName);
|
|
114
|
-
|
|
115
|
-
if (existsSync(targetDir)) {
|
|
116
|
-
console.error(`Error: Directory "${projectName}" already exists`);
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const templateDir = join(__dirname, '..', 'template');
|
|
121
|
-
|
|
122
|
-
console.log(`☀️ 🏔️ Creating ${projectName}...`);
|
|
123
|
-
|
|
124
|
-
mkdirSync(targetDir, { recursive: true });
|
|
125
|
-
|
|
126
|
-
// Filter resource directories based on selection
|
|
127
|
-
const excludedResources = availableResources.filter((r) => !selectedResources.includes(r));
|
|
128
|
-
|
|
129
|
-
cpSync(templateDir, targetDir, {
|
|
130
|
-
recursive: true,
|
|
131
|
-
filter: (src) => {
|
|
132
|
-
const name = basename(src);
|
|
133
|
-
|
|
134
|
-
// Skip node_modules and lock file
|
|
135
|
-
if (name === 'node_modules' || name === 'pnpm-lock.yaml') {
|
|
136
|
-
return false;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
for (const resource of excludedResources) {
|
|
140
|
-
// Skip entire resource directory: src/resources/{resource}/
|
|
141
|
-
if (src.includes('/resources/') && name === resource) {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
// Skip e2e test files for excluded resources
|
|
145
|
-
if (src.includes('/tests/e2e/') && name === `${resource}.spec.ts`) {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return true;
|
|
151
|
-
},
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Rename underscore-prefixed files to dotfiles
|
|
155
|
-
const dotfiles = ['_gitignore', '_prettierignore', '_prettierrc'];
|
|
156
|
-
for (const file of dotfiles) {
|
|
157
|
-
const srcPath = join(targetDir, file);
|
|
158
|
-
const destPath = join(targetDir, file.replace(/^_/, '.'));
|
|
159
|
-
if (existsSync(srcPath)) {
|
|
160
|
-
renameSync(srcPath, destPath);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Read sunpeak version from root package.json
|
|
165
|
-
const rootPkgPath = join(__dirname, '..', 'package.json');
|
|
166
|
-
const rootPkg = JSON.parse(readFileSync(rootPkgPath, 'utf-8'));
|
|
167
|
-
const sunpeakVersion = `^${rootPkg.version}`;
|
|
168
|
-
|
|
169
|
-
// Update project package.json
|
|
170
|
-
const pkgPath = join(targetDir, 'package.json');
|
|
171
|
-
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
172
|
-
pkg.name = projectName;
|
|
173
|
-
|
|
174
|
-
// Replace workspace:* with actual version
|
|
175
|
-
if (pkg.dependencies?.sunpeak === 'workspace:*') {
|
|
176
|
-
pkg.dependencies.sunpeak = sunpeakVersion;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
180
|
-
|
|
181
|
-
console.log(`
|
|
182
|
-
Done! To get started:
|
|
183
|
-
|
|
184
|
-
cd ${projectName}
|
|
185
|
-
pnpm install
|
|
186
|
-
sunpeak dev
|
|
187
|
-
|
|
188
|
-
That's it! Your project commands:
|
|
189
|
-
|
|
190
|
-
sunpeak dev # Start dev server + MCP endpoint
|
|
191
|
-
sunpeak build # Build for production
|
|
192
|
-
pnpm test # Run tests
|
|
193
|
-
|
|
194
|
-
See README.md for more details.
|
|
195
|
-
`);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
20
|
const [, , command, ...args] = process.argv;
|
|
199
21
|
|
|
200
22
|
/**
|
|
@@ -206,40 +28,6 @@ function getVersion() {
|
|
|
206
28
|
return pkg.version;
|
|
207
29
|
}
|
|
208
30
|
|
|
209
|
-
/**
|
|
210
|
-
* Parse arguments for resource commands (push, pull, deploy)
|
|
211
|
-
*/
|
|
212
|
-
function parseResourceArgs(args) {
|
|
213
|
-
const options = { tags: [] };
|
|
214
|
-
let i = 0;
|
|
215
|
-
|
|
216
|
-
while (i < args.length) {
|
|
217
|
-
const arg = args[i];
|
|
218
|
-
|
|
219
|
-
if (arg === '--repository' || arg === '-r') {
|
|
220
|
-
options.repository = args[++i];
|
|
221
|
-
} else if (arg === '--tag' || arg === '-t') {
|
|
222
|
-
options.tags.push(args[++i]);
|
|
223
|
-
} else if (arg === '--name' || arg === '-n') {
|
|
224
|
-
options.name = args[++i];
|
|
225
|
-
} else if (arg === '--output' || arg === '-o') {
|
|
226
|
-
options.output = args[++i];
|
|
227
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
228
|
-
options.help = true;
|
|
229
|
-
} else if (!arg.startsWith('-')) {
|
|
230
|
-
// Positional argument - treat as file path
|
|
231
|
-
options.file = arg;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
i++;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Set singular tag for commands that expect it (e.g., pull)
|
|
238
|
-
options.tag = options.tags[0];
|
|
239
|
-
|
|
240
|
-
return options;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
31
|
// Main CLI handler
|
|
244
32
|
(async () => {
|
|
245
33
|
// Handle --version / -v flags early
|
|
@@ -267,7 +55,10 @@ function parseResourceArgs(args) {
|
|
|
267
55
|
|
|
268
56
|
switch (command) {
|
|
269
57
|
case 'new':
|
|
270
|
-
|
|
58
|
+
{
|
|
59
|
+
const { init } = await import(join(COMMANDS_DIR, 'new.mjs'));
|
|
60
|
+
await init(args[0], args[1]);
|
|
61
|
+
}
|
|
271
62
|
break;
|
|
272
63
|
|
|
273
64
|
case 'dev':
|
|
@@ -300,22 +91,22 @@ function parseResourceArgs(args) {
|
|
|
300
91
|
|
|
301
92
|
case 'push':
|
|
302
93
|
{
|
|
303
|
-
const { push } = await import(join(COMMANDS_DIR, 'push.mjs'));
|
|
304
|
-
await push(process.cwd(),
|
|
94
|
+
const { push, parseArgs } = await import(join(COMMANDS_DIR, 'push.mjs'));
|
|
95
|
+
await push(process.cwd(), parseArgs(args));
|
|
305
96
|
}
|
|
306
97
|
break;
|
|
307
98
|
|
|
308
99
|
case 'pull':
|
|
309
100
|
{
|
|
310
|
-
const { pull } = await import(join(COMMANDS_DIR, 'pull.mjs'));
|
|
311
|
-
await pull(process.cwd(),
|
|
101
|
+
const { pull, parseArgs } = await import(join(COMMANDS_DIR, 'pull.mjs'));
|
|
102
|
+
await pull(process.cwd(), parseArgs(args));
|
|
312
103
|
}
|
|
313
104
|
break;
|
|
314
105
|
|
|
315
106
|
case 'deploy':
|
|
316
107
|
{
|
|
317
|
-
const { deploy } = await import(join(COMMANDS_DIR, 'deploy.mjs'));
|
|
318
|
-
await deploy(process.cwd(),
|
|
108
|
+
const { deploy, parseArgs } = await import(join(COMMANDS_DIR, 'deploy.mjs'));
|
|
109
|
+
await deploy(process.cwd(), parseArgs(args));
|
|
319
110
|
}
|
|
320
111
|
break;
|
|
321
112
|
|
package/package.json
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* This file bootstraps the ChatGPT simulator for development
|
|
5
5
|
*
|
|
6
6
|
* Auto-discovers simulations and resources by file naming convention:
|
|
7
|
-
* -
|
|
8
|
-
* - resources/{resource}/{resource}-resource.json
|
|
9
|
-
* - resources/{resource}/{Resource}Resource component (PascalCase)
|
|
7
|
+
* - tests/simulations/{resource}/{resource}-{scenario}-simulation.json
|
|
8
|
+
* - src/resources/{resource}/{resource}-resource.json
|
|
9
|
+
* - src/resources/{resource}/{Resource}Resource component (PascalCase)
|
|
10
10
|
*/
|
|
11
11
|
import { StrictMode } from 'react';
|
|
12
12
|
import { createRoot } from 'react-dom/client';
|
|
@@ -16,7 +16,7 @@ import '../src/styles/globals.css';
|
|
|
16
16
|
|
|
17
17
|
// Build simulations from discovered files
|
|
18
18
|
const simulations = buildDevSimulations({
|
|
19
|
-
simulationModules: import.meta.glob('../
|
|
19
|
+
simulationModules: import.meta.glob('../tests/simulations/*/*-simulation.json', { eager: true }),
|
|
20
20
|
resourceModules: import.meta.glob('../src/resources/*/*-resource.json', { eager: true }),
|
|
21
21
|
resourceComponents: resourceComponents as Record<string, React.ComponentType>,
|
|
22
22
|
});
|
package/template/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# sunpeak-app
|
|
2
2
|
|
|
3
|
-
A ChatGPT App
|
|
3
|
+
A ChatGPT App built with [sunpeak](https://github.com/Sunpeak-AI/sunpeak).
|
|
4
4
|
|
|
5
5
|
For an initial overview of your new app and a detailed API reference, refer to the [documentation](https://docs.sunpeak.ai/template/project-structure).
|
|
6
6
|
|
|
@@ -32,15 +32,20 @@ The template includes a minimal test setup with Vitest. You can add additional t
|
|
|
32
32
|
|
|
33
33
|
## Project Structure
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
Using a Review page as an example, sunpeak projects look like:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
my-app/
|
|
39
|
+
├── src/resources/
|
|
40
|
+
│ └── review/
|
|
41
|
+
│ ├── review-resource.tsx # Review UI component.
|
|
42
|
+
│ └── review-resource.json # Review UI MCP metadata.
|
|
43
|
+
├── tests/simulations/
|
|
44
|
+
│ └── review/
|
|
45
|
+
│ ├── review-{scenario1}-simulation.json # Mock state for testing.
|
|
46
|
+
│ └── review-{scenario2}-simulation.json # Mock state for testing.
|
|
47
|
+
└── package.json
|
|
48
|
+
```
|
|
44
49
|
|
|
45
50
|
## Testing in ChatGPT
|
|
46
51
|
|
|
@@ -58,8 +63,6 @@ You can then connect to the tunnel forwarding URL at the `/mcp` path from ChatGP
|
|
|
58
63
|
|
|
59
64
|
Once your app is connected, send the name of the app and a tool, like `/sunpeak show review`, to ChatGPT.
|
|
60
65
|
|
|
61
|
-
When you make changes to the UI, refresh your app in ChatGPT after the dev server has finished rebuilding: `User > Settings > Apps & Connectors > My App > Refresh`
|
|
62
|
-
|
|
63
66
|
## Build & Deploy
|
|
64
67
|
|
|
65
68
|
Build your app for production:
|
|
@@ -73,15 +76,11 @@ This creates optimized builds in `dist/`, organized by resource:
|
|
|
73
76
|
```bash
|
|
74
77
|
dist/
|
|
75
78
|
├── albums/
|
|
76
|
-
│ ├── albums.js
|
|
77
|
-
│
|
|
78
|
-
│ └── albums-show-simulation.json # Resource mock data for testing.
|
|
79
|
+
│ ├── albums.js # Built resource component.
|
|
80
|
+
│ └── albums.json # Resource metadata.
|
|
79
81
|
├── review/
|
|
80
82
|
│ ├── review.js
|
|
81
|
-
│
|
|
82
|
-
│ ├── review-diff-simulation.json
|
|
83
|
-
│ ├── review-post-simulation.json
|
|
84
|
-
│ └── review-purchase-simulation.json
|
|
83
|
+
│ └── review.json
|
|
85
84
|
└── ...
|
|
86
85
|
```
|
|
87
86
|
|
|
@@ -89,7 +88,6 @@ Each resource folder contains:
|
|
|
89
88
|
|
|
90
89
|
- **`.js` file**: Self-contained bundle with CSS inlined
|
|
91
90
|
- **`.json` file**: Resource metadata with unique `uri` for cache-busting
|
|
92
|
-
- **`*-simulation.json` files**: All affiliated simulation files for the resource. These are not needed for the production runtime, but are used in the sunpeak repository for testing.
|
|
93
91
|
|
|
94
92
|
Host these files and reference them as resources in your production MCP server.
|
|
95
93
|
Use the sunpeak resource repository for built-in resource hosting.
|
|
@@ -103,11 +101,12 @@ src/resources/NAME/
|
|
|
103
101
|
├── NAME-resource.tsx # React component (required)
|
|
104
102
|
├── NAME-resource.json # Resource metadata (required)
|
|
105
103
|
├── NAME-resource.test.tsx # Unit tests (optional)
|
|
106
|
-
├── NAME-SCENARIO-simulation.json # Simulation data (optional)
|
|
107
104
|
└── components/ # UI components (optional)
|
|
108
105
|
```
|
|
109
106
|
|
|
110
|
-
Only the resource files (`.tsx` and `.json`) are required to generate a production build and ship a UI.
|
|
107
|
+
Only the resource files (`.tsx` and `.json`) are required to generate a production build and ship a UI.
|
|
108
|
+
|
|
109
|
+
Create the simulation file(s) in `tests/simulations/` if you want to preview your resource in `sunpeak dev`.
|
|
111
110
|
|
|
112
111
|
## Resources
|
|
113
112
|
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Check_default,
|
|
3
|
-
Copy_default
|
|
4
|
-
} from "./chunk-UM3ZGDFR.js";
|
|
5
1
|
import {
|
|
6
2
|
useTimeout
|
|
7
3
|
} from "./chunk-JAGHY6H6.js";
|
|
4
|
+
import {
|
|
5
|
+
o
|
|
6
|
+
} from "./chunk-QPJAV452.js";
|
|
8
7
|
import {
|
|
9
8
|
handlePressableMouseEnter,
|
|
10
9
|
isDev,
|
|
@@ -16,12 +15,13 @@ import {
|
|
|
16
15
|
toTransformProperty,
|
|
17
16
|
waitForAnimationFrame
|
|
18
17
|
} from "./chunk-RYYR2YMB.js";
|
|
19
|
-
import {
|
|
20
|
-
o
|
|
21
|
-
} from "./chunk-QPJAV452.js";
|
|
22
18
|
import {
|
|
23
19
|
clsx_default
|
|
24
20
|
} from "./chunk-CNYJBM5F.js";
|
|
21
|
+
import {
|
|
22
|
+
Check_default,
|
|
23
|
+
Copy_default
|
|
24
|
+
} from "./chunk-UM3ZGDFR.js";
|
|
25
25
|
import {
|
|
26
26
|
require_jsx_runtime
|
|
27
27
|
} from "./chunk-JGVISENQ.js";
|
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
handlePressableMouseEnter,
|
|
3
3
|
waitForAnimationFrame
|
|
4
4
|
} from "./chunk-6QVG4F2X.js";
|
|
5
|
+
import "./chunk-EGRHWZRV.js";
|
|
5
6
|
import {
|
|
6
7
|
useResizeObserver
|
|
7
8
|
} from "./chunk-JAGHY6H6.js";
|
|
@@ -9,7 +10,6 @@ import {
|
|
|
9
10
|
dist_exports4 as dist_exports
|
|
10
11
|
} from "./chunk-2UDYPUBJ.js";
|
|
11
12
|
import "./chunk-XZTIOEPG.js";
|
|
12
|
-
import "./chunk-EGRHWZRV.js";
|
|
13
13
|
import {
|
|
14
14
|
clsx_default
|
|
15
15
|
} from "./chunk-CNYJBM5F.js";
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Input
|
|
3
|
+
} from "./chunk-P5LK4A7U.js";
|
|
1
4
|
import {
|
|
2
5
|
handlePressableMouseEnter,
|
|
3
6
|
isDev,
|
|
@@ -6,22 +9,19 @@ import {
|
|
|
6
9
|
toCssVariables,
|
|
7
10
|
waitForAnimationFrame
|
|
8
11
|
} from "./chunk-6QVG4F2X.js";
|
|
12
|
+
import "./chunk-EGRHWZRV.js";
|
|
9
13
|
import {
|
|
10
14
|
useTimeout
|
|
11
15
|
} from "./chunk-JAGHY6H6.js";
|
|
16
|
+
import {
|
|
17
|
+
o
|
|
18
|
+
} from "./chunk-QPJAV452.js";
|
|
12
19
|
import {
|
|
13
20
|
dist_exports,
|
|
14
21
|
dist_exports3 as dist_exports2,
|
|
15
22
|
dist_exports5 as dist_exports3
|
|
16
23
|
} from "./chunk-2UDYPUBJ.js";
|
|
17
24
|
import "./chunk-XZTIOEPG.js";
|
|
18
|
-
import {
|
|
19
|
-
Input
|
|
20
|
-
} from "./chunk-P5LK4A7U.js";
|
|
21
|
-
import "./chunk-EGRHWZRV.js";
|
|
22
|
-
import {
|
|
23
|
-
o
|
|
24
|
-
} from "./chunk-QPJAV452.js";
|
|
25
25
|
import {
|
|
26
26
|
clsx_default
|
|
27
27
|
} from "./chunk-CNYJBM5F.js";
|