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.
@@ -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
- }