sunpeak 0.13.12 → 0.14.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.
- package/README.md +22 -25
- package/bin/commands/dev.mjs +2 -2
- package/bin/sunpeak.js +1 -53
- package/dist/cli/commands.test.new.d.ts +1 -0
- package/dist/mcp/favicon.d.ts +1 -1
- package/dist/mcp/index.cjs +5 -5
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +5 -5
- package/dist/mcp/index.js.map +1 -1
- package/package.json +2 -2
- package/template/README.md +10 -8
- package/bin/commands/deploy.mjs +0 -141
- package/bin/commands/login.mjs +0 -235
- package/bin/commands/logout.mjs +0 -101
- package/bin/commands/pull.mjs +0 -279
- package/bin/commands/push.mjs +0 -465
package/bin/commands/pull.mjs
DELETED
|
@@ -1,279 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
3
|
-
import { join } from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
|
|
6
|
-
const SUNPEAK_API_URL = process.env.SUNPEAK_API_URL || 'https://app.sunpeak.ai';
|
|
7
|
-
const CREDENTIALS_DIR = join(homedir(), '.sunpeak');
|
|
8
|
-
const CREDENTIALS_FILE = join(CREDENTIALS_DIR, 'credentials.json');
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Load credentials from disk
|
|
12
|
-
*/
|
|
13
|
-
function loadCredentialsImpl() {
|
|
14
|
-
if (!existsSync(CREDENTIALS_FILE)) {
|
|
15
|
-
return null;
|
|
16
|
-
}
|
|
17
|
-
try {
|
|
18
|
-
return JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));
|
|
19
|
-
} catch {
|
|
20
|
-
return null;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Default dependencies (real implementations)
|
|
26
|
-
*/
|
|
27
|
-
export const defaultDeps = {
|
|
28
|
-
fetch: globalThis.fetch,
|
|
29
|
-
loadCredentials: loadCredentialsImpl,
|
|
30
|
-
existsSync,
|
|
31
|
-
mkdirSync,
|
|
32
|
-
writeFileSync,
|
|
33
|
-
console,
|
|
34
|
-
process,
|
|
35
|
-
apiUrl: SUNPEAK_API_URL,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Lookup resources by tag and repository
|
|
40
|
-
* @returns {Promise<Array>} Array of matching resources
|
|
41
|
-
*/
|
|
42
|
-
async function lookupResources(tag, repository, accessToken, name = null, deps = defaultDeps) {
|
|
43
|
-
const d = { ...defaultDeps, ...deps };
|
|
44
|
-
|
|
45
|
-
const params = new URLSearchParams({ tag, repository });
|
|
46
|
-
if (name) {
|
|
47
|
-
params.set('name', name);
|
|
48
|
-
}
|
|
49
|
-
const response = await d.fetch(`${d.apiUrl}/api/v1/resources/lookup?${params}`, {
|
|
50
|
-
headers: {
|
|
51
|
-
Authorization: `Bearer ${accessToken}`,
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
if (!response.ok) {
|
|
56
|
-
const data = await response.json().catch(() => ({}));
|
|
57
|
-
throw new Error(data.message || data.error || `HTTP ${response.status}`);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const data = await response.json();
|
|
61
|
-
return data.resources;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Download the HTML file for a resource
|
|
66
|
-
*/
|
|
67
|
-
async function downloadHtmlFile(resource, deps = defaultDeps) {
|
|
68
|
-
const d = { ...defaultDeps, ...deps };
|
|
69
|
-
|
|
70
|
-
if (!resource.html_file?.url) {
|
|
71
|
-
throw new Error('Resource has no HTML file attached');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// The URL is a pre-signed S3 URL, no additional auth needed
|
|
75
|
-
const response = await d.fetch(resource.html_file.url);
|
|
76
|
-
|
|
77
|
-
if (!response.ok) {
|
|
78
|
-
throw new Error(`Failed to download HTML file: HTTP ${response.status}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return response.text();
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Main pull command
|
|
86
|
-
* @param {string} projectRoot - Project root directory
|
|
87
|
-
* @param {Object} options - Command options
|
|
88
|
-
* @param {string} options.repository - Repository name in owner/repo format (required)
|
|
89
|
-
* @param {string} options.tag - Tag name to pull (required)
|
|
90
|
-
* @param {string} options.name - Resource name to filter by (optional)
|
|
91
|
-
* @param {string} options.output - Output directory (optional, defaults to current directory)
|
|
92
|
-
* @param {Object} deps - Dependencies (for testing). Uses defaultDeps if not provided.
|
|
93
|
-
*/
|
|
94
|
-
export async function pull(projectRoot = process.cwd(), options = {}, deps = defaultDeps) {
|
|
95
|
-
const d = { ...defaultDeps, ...deps };
|
|
96
|
-
|
|
97
|
-
// Handle help flag
|
|
98
|
-
if (options.help) {
|
|
99
|
-
d.console.log(`
|
|
100
|
-
sunpeak pull - Pull resources from the Sunpeak repository
|
|
101
|
-
|
|
102
|
-
Usage:
|
|
103
|
-
sunpeak pull -r <owner/repo> -t <tag> [options]
|
|
104
|
-
|
|
105
|
-
Options:
|
|
106
|
-
-r, --repository <owner/repo> Repository name (required)
|
|
107
|
-
-t, --tag <name> Tag name to pull (required)
|
|
108
|
-
-n, --name <name> Resource name to filter by (optional)
|
|
109
|
-
-o, --output <path> Output directory (defaults to current directory)
|
|
110
|
-
-h, --help Show this help message
|
|
111
|
-
|
|
112
|
-
Examples:
|
|
113
|
-
sunpeak pull -r myorg/my-app -t prod Pull all resources tagged "prod"
|
|
114
|
-
sunpeak pull -r myorg/my-app -t prod -n review Pull only the "review" resource
|
|
115
|
-
sunpeak pull -r myorg/my-app -t v1.0.0 Pull a specific version
|
|
116
|
-
`);
|
|
117
|
-
return;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Check credentials
|
|
121
|
-
const credentials = d.loadCredentials();
|
|
122
|
-
if (!credentials?.access_token) {
|
|
123
|
-
d.console.error('Error: Not logged in. Run "sunpeak login" first.');
|
|
124
|
-
d.process.exit(1);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Require repository
|
|
128
|
-
if (!options.repository) {
|
|
129
|
-
d.console.error('Error: Repository is required. Use --repository or -r to specify a repository.');
|
|
130
|
-
d.console.error('Example: sunpeak pull -r myorg/my-app -t prod');
|
|
131
|
-
d.process.exit(1);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
// Require tag
|
|
135
|
-
if (!options.tag) {
|
|
136
|
-
d.console.error('Error: Tag is required. Use --tag or -t to specify a tag.');
|
|
137
|
-
d.console.error('Example: sunpeak pull -r myorg/my-app -t prod');
|
|
138
|
-
d.process.exit(1);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const repository = options.repository;
|
|
142
|
-
|
|
143
|
-
const nameFilter = options.name ? ` with name "${options.name}"` : '';
|
|
144
|
-
d.console.log(`Pulling resources from repository "${repository}" with tag "${options.tag}"${nameFilter}...`);
|
|
145
|
-
d.console.log();
|
|
146
|
-
|
|
147
|
-
try {
|
|
148
|
-
// Lookup resources
|
|
149
|
-
const resources = await lookupResources(options.tag, repository, credentials.access_token, options.name, d);
|
|
150
|
-
|
|
151
|
-
if (!resources || resources.length === 0) {
|
|
152
|
-
d.console.error('Error: No resources found matching the criteria.');
|
|
153
|
-
d.process.exit(1);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
d.console.log(`Found ${resources.length} resource(s):\n`);
|
|
157
|
-
|
|
158
|
-
// Determine output directory
|
|
159
|
-
const outputDir = options.output || projectRoot;
|
|
160
|
-
|
|
161
|
-
// Create output directory if it doesn't exist
|
|
162
|
-
if (!d.existsSync(outputDir)) {
|
|
163
|
-
d.mkdirSync(outputDir, { recursive: true });
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// Process each resource
|
|
167
|
-
for (const resource of resources) {
|
|
168
|
-
d.console.log(`Resource: ${resource.name}`);
|
|
169
|
-
d.console.log(` Title: ${resource.title}`);
|
|
170
|
-
d.console.log(` URI: ${resource.uri}`);
|
|
171
|
-
d.console.log(` Tags: ${resource.tags?.join(', ') || 'none'}`);
|
|
172
|
-
d.console.log(` Created: ${resource.created_at}`);
|
|
173
|
-
|
|
174
|
-
if (!resource.html_file) {
|
|
175
|
-
d.console.log(` ⚠ Skipping: No HTML file attached.\n`);
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// Download the HTML file
|
|
180
|
-
d.console.log(` Downloading HTML file...`);
|
|
181
|
-
const htmlContent = await downloadHtmlFile(resource, d);
|
|
182
|
-
|
|
183
|
-
const outputFile = join(outputDir, `${resource.name}.html`);
|
|
184
|
-
const metaFile = join(outputDir, `${resource.name}.json`);
|
|
185
|
-
|
|
186
|
-
// Write the HTML file
|
|
187
|
-
d.writeFileSync(outputFile, htmlContent);
|
|
188
|
-
d.console.log(` ✓ Saved ${resource.name}.html`);
|
|
189
|
-
|
|
190
|
-
// Write metadata JSON (reconstruct McpUiResourceMeta from server flat fields)
|
|
191
|
-
const ui = { domain: resource.widget_domain };
|
|
192
|
-
const csp = {};
|
|
193
|
-
if (resource.widget_csp_connect_domains?.length) csp.connectDomains = resource.widget_csp_connect_domains;
|
|
194
|
-
if (resource.widget_csp_resource_domains?.length) csp.resourceDomains = resource.widget_csp_resource_domains;
|
|
195
|
-
if (resource.widget_csp_frame_domains?.length) csp.frameDomains = resource.widget_csp_frame_domains;
|
|
196
|
-
if (resource.widget_csp_base_uri_domains?.length) csp.baseUriDomains = resource.widget_csp_base_uri_domains;
|
|
197
|
-
if (Object.keys(csp).length > 0) ui.csp = csp;
|
|
198
|
-
if (resource.permissions) ui.permissions = resource.permissions;
|
|
199
|
-
if (resource.prefers_border != null) ui.prefersBorder = resource.prefers_border;
|
|
200
|
-
|
|
201
|
-
const meta = {
|
|
202
|
-
uri: resource.uri,
|
|
203
|
-
name: resource.name,
|
|
204
|
-
title: resource.title,
|
|
205
|
-
description: resource.description,
|
|
206
|
-
mimeType: resource.mime_type,
|
|
207
|
-
_meta: { ui },
|
|
208
|
-
};
|
|
209
|
-
d.writeFileSync(metaFile, JSON.stringify(meta, null, 2));
|
|
210
|
-
d.console.log(` ✓ Saved ${resource.name}.json\n`);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
d.console.log(`✓ Successfully pulled ${resources.length} resource(s) to ${outputDir}`);
|
|
214
|
-
} catch (error) {
|
|
215
|
-
d.console.error(`Error: ${error.message}`);
|
|
216
|
-
d.process.exit(1);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Parse command line arguments
|
|
222
|
-
*/
|
|
223
|
-
export function parseArgs(args) {
|
|
224
|
-
const options = {};
|
|
225
|
-
let i = 0;
|
|
226
|
-
|
|
227
|
-
while (i < args.length) {
|
|
228
|
-
const arg = args[i];
|
|
229
|
-
|
|
230
|
-
if (arg === '--repository' || arg === '-r') {
|
|
231
|
-
options.repository = args[++i];
|
|
232
|
-
} else if (arg === '--tag' || arg === '-t') {
|
|
233
|
-
options.tag = args[++i];
|
|
234
|
-
} else if (arg === '--name' || arg === '-n') {
|
|
235
|
-
options.name = args[++i];
|
|
236
|
-
} else if (arg === '--output' || arg === '-o') {
|
|
237
|
-
options.output = args[++i];
|
|
238
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
239
|
-
console.log(`
|
|
240
|
-
sunpeak pull - Pull resources from the Sunpeak repository
|
|
241
|
-
|
|
242
|
-
Usage:
|
|
243
|
-
sunpeak pull -r <owner/repo> -t <tag> [options]
|
|
244
|
-
|
|
245
|
-
Options:
|
|
246
|
-
-r, --repository <owner/repo> Repository name (required)
|
|
247
|
-
-t, --tag <name> Tag name to pull (required)
|
|
248
|
-
-n, --name <name> Resource name to filter by (optional)
|
|
249
|
-
-o, --output <path> Output directory (defaults to current directory)
|
|
250
|
-
-h, --help Show this help message
|
|
251
|
-
|
|
252
|
-
Examples:
|
|
253
|
-
sunpeak pull -r myorg/my-app -t prod Pull all resources tagged "prod"
|
|
254
|
-
sunpeak pull -r myorg/my-app -t prod -n review Pull only the "review" resource
|
|
255
|
-
sunpeak pull -r myorg/my-app -t v1.0.0 Pull a specific version
|
|
256
|
-
`);
|
|
257
|
-
process.exit(0);
|
|
258
|
-
} else if (!arg.startsWith('-')) {
|
|
259
|
-
// Positional argument - treat as tag
|
|
260
|
-
if (!options.tag) {
|
|
261
|
-
options.tag = arg;
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
i++;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return options;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
// Allow running directly
|
|
272
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
273
|
-
const args = process.argv.slice(2);
|
|
274
|
-
const options = parseArgs(args);
|
|
275
|
-
pull(process.cwd(), options).catch((error) => {
|
|
276
|
-
console.error('Error:', error.message);
|
|
277
|
-
process.exit(1);
|
|
278
|
-
});
|
|
279
|
-
}
|