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/push.mjs
DELETED
|
@@ -1,465 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
3
|
-
import { join, basename } from 'path';
|
|
4
|
-
import { homedir } from 'os';
|
|
5
|
-
import { execSync } from 'child_process';
|
|
6
|
-
import { isSimulationFile, extractSimulationName } from '../lib/patterns.mjs';
|
|
7
|
-
|
|
8
|
-
const SUNPEAK_API_URL = process.env.SUNPEAK_API_URL || 'https://app.sunpeak.ai';
|
|
9
|
-
const CREDENTIALS_DIR = join(homedir(), '.sunpeak');
|
|
10
|
-
const CREDENTIALS_FILE = join(CREDENTIALS_DIR, 'credentials.json');
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Load credentials from disk
|
|
14
|
-
*/
|
|
15
|
-
function loadCredentialsImpl() {
|
|
16
|
-
if (!existsSync(CREDENTIALS_FILE)) {
|
|
17
|
-
return null;
|
|
18
|
-
}
|
|
19
|
-
try {
|
|
20
|
-
return JSON.parse(readFileSync(CREDENTIALS_FILE, 'utf-8'));
|
|
21
|
-
} catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Get the current git repository name in owner/repo format
|
|
28
|
-
*/
|
|
29
|
-
function getGitRepoNameImpl() {
|
|
30
|
-
try {
|
|
31
|
-
// Try to get the remote URL first
|
|
32
|
-
const remoteUrl = execSync('git remote get-url origin 2>/dev/null', { encoding: 'utf-8' }).trim();
|
|
33
|
-
if (remoteUrl) {
|
|
34
|
-
// Extract owner/repo from URL
|
|
35
|
-
// Handles: https://github.com/owner/repo.git, git@github.com:owner/repo.git
|
|
36
|
-
const match = remoteUrl.match(/[/:]([^/:]+\/[^/]+?)(?:\.git)?$/);
|
|
37
|
-
if (match) {
|
|
38
|
-
return match[1];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
} catch {
|
|
42
|
-
// No remote
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return null;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Default dependencies (real implementations)
|
|
50
|
-
*/
|
|
51
|
-
export const defaultDeps = {
|
|
52
|
-
fetch: globalThis.fetch,
|
|
53
|
-
loadCredentials: loadCredentialsImpl,
|
|
54
|
-
getGitRepoName: getGitRepoNameImpl,
|
|
55
|
-
existsSync,
|
|
56
|
-
readFileSync,
|
|
57
|
-
readdirSync,
|
|
58
|
-
console,
|
|
59
|
-
process,
|
|
60
|
-
apiUrl: SUNPEAK_API_URL,
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Find simulation files for a resource in the simulations directory
|
|
65
|
-
* Expects files in: tests/simulations/{resourceName}/{resourceName}-*-simulation.json
|
|
66
|
-
* Returns array of parsed simulation objects
|
|
67
|
-
*/
|
|
68
|
-
function findSimulations(simulationsDir, resourceName, deps = defaultDeps) {
|
|
69
|
-
const d = { ...defaultDeps, ...deps };
|
|
70
|
-
|
|
71
|
-
const resourceSimDir = join(simulationsDir, resourceName);
|
|
72
|
-
if (!d.existsSync(resourceSimDir)) {
|
|
73
|
-
return [];
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const entries = d.readdirSync(resourceSimDir);
|
|
77
|
-
const simulations = [];
|
|
78
|
-
|
|
79
|
-
for (const entry of entries) {
|
|
80
|
-
if (isSimulationFile(entry, resourceName)) {
|
|
81
|
-
const simPath = join(resourceSimDir, entry);
|
|
82
|
-
try {
|
|
83
|
-
const simData = JSON.parse(d.readFileSync(simPath, 'utf-8'));
|
|
84
|
-
const simName = extractSimulationName(entry, resourceName);
|
|
85
|
-
simulations.push({ ...simData, name: simName });
|
|
86
|
-
} catch {
|
|
87
|
-
d.console.warn(`Warning: Could not parse simulation file ${entry}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return simulations;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Find all resources in a directory
|
|
97
|
-
* Expects folder structure: dist/{resource}/{resource}.html
|
|
98
|
-
* Simulations are loaded from tests/simulations/{resource}/
|
|
99
|
-
* Returns array of { name, htmlPath, metaPath, meta, simulations }
|
|
100
|
-
*/
|
|
101
|
-
export function findResources(distDir, simulationsDir, deps = defaultDeps) {
|
|
102
|
-
const d = { ...defaultDeps, ...deps };
|
|
103
|
-
|
|
104
|
-
if (!d.existsSync(distDir)) {
|
|
105
|
-
return [];
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const entries = d.readdirSync(distDir, { withFileTypes: true });
|
|
109
|
-
const resources = [];
|
|
110
|
-
|
|
111
|
-
for (const entry of entries) {
|
|
112
|
-
if (entry.isDirectory()) {
|
|
113
|
-
const resourceName = entry.name;
|
|
114
|
-
const resourceDir = join(distDir, resourceName);
|
|
115
|
-
const htmlPath = join(resourceDir, `${resourceName}.html`);
|
|
116
|
-
const metaPath = join(resourceDir, `${resourceName}.json`);
|
|
117
|
-
|
|
118
|
-
if (d.existsSync(htmlPath) && d.existsSync(metaPath)) {
|
|
119
|
-
let meta = null;
|
|
120
|
-
try {
|
|
121
|
-
meta = JSON.parse(d.readFileSync(metaPath, 'utf-8'));
|
|
122
|
-
} catch {
|
|
123
|
-
d.console.warn(`Warning: Could not parse ${resourceName}.json`);
|
|
124
|
-
}
|
|
125
|
-
const simulations = findSimulations(simulationsDir, resourceName, d);
|
|
126
|
-
resources.push({ name: resourceName, dir: resourceDir, htmlPath, metaPath, meta, simulations });
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return resources;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Build a resource from a resource directory path
|
|
136
|
-
* Expects structure: dir/{name}.html, dir/{name}.json
|
|
137
|
-
* Simulations are loaded from tests/simulations/{name}/
|
|
138
|
-
* Returns { name, htmlPath, metaPath, meta, simulations }
|
|
139
|
-
*/
|
|
140
|
-
function buildResourceFromDir(resourceDir, simulationsDir, deps = defaultDeps) {
|
|
141
|
-
const d = { ...defaultDeps, ...deps };
|
|
142
|
-
|
|
143
|
-
// Remove trailing slash if present
|
|
144
|
-
const dir = resourceDir.replace(/\/$/, '');
|
|
145
|
-
|
|
146
|
-
if (!d.existsSync(dir)) {
|
|
147
|
-
d.console.error(`Error: Directory not found: ${dir}`);
|
|
148
|
-
d.process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Extract resource name from directory name
|
|
152
|
-
const name = basename(dir);
|
|
153
|
-
const htmlPath = join(dir, `${name}.html`);
|
|
154
|
-
const metaPath = join(dir, `${name}.json`);
|
|
155
|
-
|
|
156
|
-
if (!d.existsSync(htmlPath)) {
|
|
157
|
-
d.console.error(`Error: Resource HTML file not found: ${htmlPath}`);
|
|
158
|
-
d.process.exit(1);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
if (!d.existsSync(metaPath)) {
|
|
162
|
-
d.console.error(`Error: Resource metadata file not found: ${metaPath}`);
|
|
163
|
-
d.process.exit(1);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
let meta = null;
|
|
167
|
-
try {
|
|
168
|
-
meta = JSON.parse(d.readFileSync(metaPath, 'utf-8'));
|
|
169
|
-
} catch {
|
|
170
|
-
d.console.warn(`Warning: Could not parse ${name}.json`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const simulations = findSimulations(simulationsDir, name, d);
|
|
174
|
-
|
|
175
|
-
return { name, htmlPath, metaPath, meta, simulations };
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Push a single resource to the API
|
|
180
|
-
*/
|
|
181
|
-
async function pushResource(resource, repository, tags, accessToken, deps = defaultDeps) {
|
|
182
|
-
const d = { ...defaultDeps, ...deps };
|
|
183
|
-
|
|
184
|
-
if (!resource.meta?.uri) {
|
|
185
|
-
throw new Error('Resource is missing URI. Run "sunpeak build" to generate URIs.');
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const htmlContent = d.readFileSync(resource.htmlPath);
|
|
189
|
-
const htmlBlob = new Blob([htmlContent], { type: 'text/html' });
|
|
190
|
-
|
|
191
|
-
// Build form data
|
|
192
|
-
const formData = new FormData();
|
|
193
|
-
formData.append('repository', repository);
|
|
194
|
-
formData.append('html_file', htmlBlob, `${resource.name}.html`);
|
|
195
|
-
|
|
196
|
-
// Add metadata fields
|
|
197
|
-
if (resource.meta) {
|
|
198
|
-
formData.append('name', resource.meta.name || resource.name);
|
|
199
|
-
formData.append('title', resource.meta.title || resource.name);
|
|
200
|
-
if (resource.meta.description) {
|
|
201
|
-
formData.append('description', resource.meta.description);
|
|
202
|
-
}
|
|
203
|
-
formData.append('mime_type', resource.meta.mimeType || 'text/html+skybridge');
|
|
204
|
-
formData.append('uri', resource.meta.uri);
|
|
205
|
-
|
|
206
|
-
// Handle UI resource metadata (_meta.ui matches McpUiResourceMeta from ext-apps SDK)
|
|
207
|
-
if (resource.meta._meta?.ui) {
|
|
208
|
-
const ui = resource.meta._meta.ui;
|
|
209
|
-
if (ui.domain) {
|
|
210
|
-
formData.append('widget_domain', ui.domain);
|
|
211
|
-
}
|
|
212
|
-
if (ui.prefersBorder != null) {
|
|
213
|
-
formData.append('prefers_border', String(ui.prefersBorder));
|
|
214
|
-
}
|
|
215
|
-
if (ui.permissions) {
|
|
216
|
-
formData.append('permissions', JSON.stringify(ui.permissions));
|
|
217
|
-
}
|
|
218
|
-
if (ui.csp) {
|
|
219
|
-
if (ui.csp.connectDomains) {
|
|
220
|
-
ui.csp.connectDomains.forEach((domain) => {
|
|
221
|
-
formData.append('widget_csp_connect_domains[]', domain);
|
|
222
|
-
});
|
|
223
|
-
}
|
|
224
|
-
if (ui.csp.resourceDomains) {
|
|
225
|
-
ui.csp.resourceDomains.forEach((domain) => {
|
|
226
|
-
formData.append('widget_csp_resource_domains[]', domain);
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
if (ui.csp.frameDomains) {
|
|
230
|
-
ui.csp.frameDomains.forEach((domain) => {
|
|
231
|
-
formData.append('widget_csp_frame_domains[]', domain);
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
if (ui.csp.baseUriDomains) {
|
|
235
|
-
ui.csp.baseUriDomains.forEach((domain) => {
|
|
236
|
-
formData.append('widget_csp_base_uri_domains[]', domain);
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
} else {
|
|
242
|
-
// Fallback metadata
|
|
243
|
-
formData.append('name', resource.name);
|
|
244
|
-
formData.append('title', resource.name);
|
|
245
|
-
formData.append('mime_type', 'text/html+skybridge');
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// Add tags if provided
|
|
249
|
-
if (tags && tags.length > 0) {
|
|
250
|
-
tags.forEach((tag) => {
|
|
251
|
-
formData.append('tags[]', tag);
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Add simulations if present
|
|
256
|
-
if (resource.simulations && resource.simulations.length > 0) {
|
|
257
|
-
formData.append('simulations', JSON.stringify(resource.simulations));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
const response = await d.fetch(`${d.apiUrl}/api/v1/resources`, {
|
|
261
|
-
method: 'POST',
|
|
262
|
-
headers: {
|
|
263
|
-
Authorization: `Bearer ${accessToken}`,
|
|
264
|
-
},
|
|
265
|
-
body: formData,
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
if (!response.ok) {
|
|
269
|
-
const data = await response.json().catch(() => ({}));
|
|
270
|
-
let errorMessage = data.message || data.error || `HTTP ${response.status}`;
|
|
271
|
-
if (data.errors && Array.isArray(data.errors) && data.errors.length > 0) {
|
|
272
|
-
errorMessage += ': ' + data.errors.join(', ');
|
|
273
|
-
}
|
|
274
|
-
throw new Error(errorMessage);
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
return response.json();
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Main push command
|
|
282
|
-
* @param {string} projectRoot - Project root directory
|
|
283
|
-
* @param {Object} options - Command options
|
|
284
|
-
* @param {string} options.repository - Repository name (optional, defaults to git repo name)
|
|
285
|
-
* @param {string} options.dir - Path to a specific resource directory (optional)
|
|
286
|
-
* @param {string[]} options.tags - Tags to assign to the pushed resources (optional)
|
|
287
|
-
* @param {Object} deps - Dependencies (for testing). Uses defaultDeps if not provided.
|
|
288
|
-
*/
|
|
289
|
-
export async function push(projectRoot = process.cwd(), options = {}, deps = defaultDeps) {
|
|
290
|
-
const d = { ...defaultDeps, ...deps };
|
|
291
|
-
|
|
292
|
-
// Handle help flag
|
|
293
|
-
if (options.help) {
|
|
294
|
-
d.console.log(`
|
|
295
|
-
sunpeak push - Push resources to the Sunpeak repository
|
|
296
|
-
|
|
297
|
-
Usage:
|
|
298
|
-
sunpeak push [directory] [options]
|
|
299
|
-
|
|
300
|
-
Options:
|
|
301
|
-
-r, --repository <owner/repo> Repository name (defaults to git remote origin)
|
|
302
|
-
-t, --tag <name> Tag to assign (can be specified multiple times)
|
|
303
|
-
--no-simulations Skip pushing simulations, only push resources
|
|
304
|
-
-h, --help Show this help message
|
|
305
|
-
|
|
306
|
-
Arguments:
|
|
307
|
-
directory Optional resource directory to push (e.g., dist/carousel)
|
|
308
|
-
If not provided, pushes all resources from dist/
|
|
309
|
-
|
|
310
|
-
Examples:
|
|
311
|
-
sunpeak push Push all resources from dist/
|
|
312
|
-
sunpeak push dist/carousel Push a single resource
|
|
313
|
-
sunpeak push -r myorg/my-app Push to "myorg/my-app" repository
|
|
314
|
-
sunpeak push -t v1.0.0 Push with a version tag
|
|
315
|
-
sunpeak push -t v1.0.0 -t prod Push with multiple tags
|
|
316
|
-
sunpeak push --no-simulations Push resources without simulations
|
|
317
|
-
`);
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
// Check credentials
|
|
322
|
-
const credentials = d.loadCredentials();
|
|
323
|
-
if (!credentials?.access_token) {
|
|
324
|
-
d.console.error('Error: Not logged in. Run "sunpeak login" first.');
|
|
325
|
-
d.process.exit(1);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// Determine repository name (owner/repo format)
|
|
329
|
-
const repository = options.repository || d.getGitRepoName();
|
|
330
|
-
if (!repository) {
|
|
331
|
-
d.console.error('Error: Could not determine repository name.');
|
|
332
|
-
d.console.error('Please provide a repository name: sunpeak push --repository <owner/repo>');
|
|
333
|
-
d.console.error('Or run this command from within a git repository with a remote origin.');
|
|
334
|
-
d.process.exit(1);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// Find resources - either a specific directory or all from dist directory
|
|
338
|
-
const simulationsDir = join(projectRoot, 'tests/simulations');
|
|
339
|
-
let resources;
|
|
340
|
-
if (options.dir) {
|
|
341
|
-
// Push a single specific resource from directory
|
|
342
|
-
resources = [buildResourceFromDir(options.dir, simulationsDir, d)];
|
|
343
|
-
} else {
|
|
344
|
-
// Default: find all resources in dist directory
|
|
345
|
-
const distDir = join(projectRoot, 'dist');
|
|
346
|
-
if (!d.existsSync(distDir)) {
|
|
347
|
-
d.console.error(`Error: dist/ directory not found`);
|
|
348
|
-
d.console.error('Run "sunpeak build" first to build your resources.');
|
|
349
|
-
d.process.exit(1);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
resources = findResources(distDir, simulationsDir, d);
|
|
353
|
-
if (resources.length === 0) {
|
|
354
|
-
d.console.error(`Error: No resources found in dist/`);
|
|
355
|
-
d.console.error('Run "sunpeak build" first to build your resources.');
|
|
356
|
-
d.process.exit(1);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
// Clear simulations if --no-simulations flag is set
|
|
361
|
-
if (options.noSimulations) {
|
|
362
|
-
for (const resource of resources) {
|
|
363
|
-
resource.simulations = [];
|
|
364
|
-
}
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
d.console.log(`Pushing ${resources.length} resource(s) to repository "${repository}"...`);
|
|
368
|
-
if (options.tags && options.tags.length > 0) {
|
|
369
|
-
d.console.log(`Tags: ${options.tags.join(', ')}`);
|
|
370
|
-
}
|
|
371
|
-
if (options.noSimulations) {
|
|
372
|
-
d.console.log('Simulations: skipped');
|
|
373
|
-
}
|
|
374
|
-
d.console.log();
|
|
375
|
-
|
|
376
|
-
// Push each resource
|
|
377
|
-
let successCount = 0;
|
|
378
|
-
for (const resource of resources) {
|
|
379
|
-
try {
|
|
380
|
-
const result = await pushResource(resource, repository, options.tags, credentials.access_token, d);
|
|
381
|
-
const simCount = resource.simulations?.length || 0;
|
|
382
|
-
const simInfo = simCount > 0 ? `, ${simCount} simulation(s)` : '';
|
|
383
|
-
const tagInfo = result.tags?.length > 0 ? `, tag(s): ${result.tags.join(', ')}` : '';
|
|
384
|
-
d.console.log(`✓ Pushed ${resource.name}${simInfo}${tagInfo}`);
|
|
385
|
-
d.console.log(` ${d.apiUrl}/resources/${result.id}`);
|
|
386
|
-
successCount++;
|
|
387
|
-
} catch (error) {
|
|
388
|
-
d.console.error(`✗ Failed to push ${resource.name}: ${error.message}`);
|
|
389
|
-
if (error.message.includes('Uri must be unique')) {
|
|
390
|
-
d.console.error(' You are trying to push a build that has already been pushed.');
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
d.console.log();
|
|
396
|
-
if (successCount === resources.length) {
|
|
397
|
-
d.console.log(`✓ Successfully pushed ${successCount} resource(s).`);
|
|
398
|
-
} else {
|
|
399
|
-
d.console.log(`Pushed ${successCount}/${resources.length} resource(s).`);
|
|
400
|
-
d.process.exit(1);
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Parse command line arguments
|
|
406
|
-
*/
|
|
407
|
-
export function parseArgs(args) {
|
|
408
|
-
const options = { tags: [] };
|
|
409
|
-
let i = 0;
|
|
410
|
-
|
|
411
|
-
while (i < args.length) {
|
|
412
|
-
const arg = args[i];
|
|
413
|
-
|
|
414
|
-
if (arg === '--repository' || arg === '-r') {
|
|
415
|
-
options.repository = args[++i];
|
|
416
|
-
} else if (arg === '--tag' || arg === '-t') {
|
|
417
|
-
options.tags.push(args[++i]);
|
|
418
|
-
} else if (arg === '--no-simulations') {
|
|
419
|
-
options.noSimulations = true;
|
|
420
|
-
} else if (arg === '--help' || arg === '-h') {
|
|
421
|
-
console.log(`
|
|
422
|
-
sunpeak push - Push resources to the Sunpeak repository
|
|
423
|
-
|
|
424
|
-
Usage:
|
|
425
|
-
sunpeak push [directory] [options]
|
|
426
|
-
|
|
427
|
-
Options:
|
|
428
|
-
-r, --repository <owner/repo> Repository name (defaults to git remote origin)
|
|
429
|
-
-t, --tag <name> Tag to assign (can be specified multiple times)
|
|
430
|
-
--no-simulations Skip pushing simulations, only push resources
|
|
431
|
-
-h, --help Show this help message
|
|
432
|
-
|
|
433
|
-
Arguments:
|
|
434
|
-
directory Optional resource directory to push (e.g., dist/carousel)
|
|
435
|
-
If not provided, pushes all resources from dist/
|
|
436
|
-
|
|
437
|
-
Examples:
|
|
438
|
-
sunpeak push Push all resources from dist/
|
|
439
|
-
sunpeak push dist/carousel Push a single resource
|
|
440
|
-
sunpeak push -r myorg/my-app Push to "myorg/my-app" repository
|
|
441
|
-
sunpeak push -t v1.0.0 Push with a version tag
|
|
442
|
-
sunpeak push -t v1.0.0 -t prod Push with multiple tags
|
|
443
|
-
sunpeak push --no-simulations Push resources without simulations
|
|
444
|
-
`);
|
|
445
|
-
process.exit(0);
|
|
446
|
-
} else if (!arg.startsWith('-')) {
|
|
447
|
-
// Positional argument - treat as directory path
|
|
448
|
-
options.dir = arg;
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
i++;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
return options;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
// Allow running directly
|
|
458
|
-
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
459
|
-
const args = process.argv.slice(2);
|
|
460
|
-
const options = parseArgs(args);
|
|
461
|
-
push(process.cwd(), options).catch((error) => {
|
|
462
|
-
console.error('Error:', error.message);
|
|
463
|
-
process.exit(1);
|
|
464
|
-
});
|
|
465
|
-
}
|