launchpd 1.0.3 → 1.0.6
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/LICENSE +21 -21
- package/README.md +61 -39
- package/bin/cli.js +149 -124
- package/bin/setup.js +42 -40
- package/package.json +4 -4
- package/src/commands/auth.js +516 -522
- package/src/commands/deploy.js +745 -386
- package/src/commands/index.js +14 -7
- package/src/commands/init.js +95 -72
- package/src/commands/list.js +120 -122
- package/src/commands/rollback.js +139 -102
- package/src/commands/status.js +75 -51
- package/src/commands/versions.js +153 -113
- package/src/config.js +32 -31
- package/src/utils/api.js +220 -195
- package/src/utils/credentials.js +88 -85
- package/src/utils/endpoint.js +58 -0
- package/src/utils/errors.js +79 -69
- package/src/utils/expiration.js +49 -47
- package/src/utils/id.js +5 -5
- package/src/utils/ignore.js +35 -36
- package/src/utils/index.js +10 -11
- package/src/utils/localConfig.js +39 -43
- package/src/utils/logger.js +113 -106
- package/src/utils/machineId.js +15 -19
- package/src/utils/metadata.js +113 -87
- package/src/utils/projectConfig.js +48 -45
- package/src/utils/prompt.js +91 -82
- package/src/utils/quota.js +261 -225
- package/src/utils/remoteSource.js +680 -0
- package/src/utils/upload.js +197 -127
- package/src/utils/validator.js +116 -68
package/src/commands/versions.js
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
getVersionsForSubdomain,
|
|
3
|
+
getActiveVersion
|
|
4
|
+
} from '../utils/metadata.js'
|
|
5
|
+
import { getVersions as getVersionsFromAPI } from '../utils/api.js'
|
|
6
|
+
import { isLoggedIn } from '../utils/credentials.js'
|
|
7
|
+
import {
|
|
8
|
+
success,
|
|
9
|
+
errorWithSuggestions,
|
|
10
|
+
info,
|
|
11
|
+
spinner,
|
|
12
|
+
warning,
|
|
13
|
+
formatSize,
|
|
14
|
+
log
|
|
15
|
+
} from '../utils/logger.js'
|
|
16
|
+
import chalk from 'chalk'
|
|
6
17
|
|
|
7
18
|
/**
|
|
8
19
|
* List all versions for a subdomain
|
|
@@ -11,117 +22,146 @@ import chalk from 'chalk';
|
|
|
11
22
|
* @param {boolean} options.json - Output as JSON
|
|
12
23
|
* @param {boolean} options.verbose - Show verbose error details
|
|
13
24
|
*/
|
|
14
|
-
export async function versions(subdomainInput, options) {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
export async function versions (subdomainInput, options) {
|
|
26
|
+
const subdomain = subdomainInput.toLowerCase()
|
|
27
|
+
const verbose = options.verbose || false
|
|
28
|
+
|
|
29
|
+
if (!(await isLoggedIn())) {
|
|
30
|
+
errorWithSuggestions(
|
|
31
|
+
'The versions feature is only available for authenticated users.',
|
|
32
|
+
[
|
|
33
|
+
'Run "launchpd login" to log in to your account',
|
|
34
|
+
'Run "launchpd register" to create a new account'
|
|
35
|
+
],
|
|
36
|
+
{ verbose }
|
|
37
|
+
)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (options.to) {
|
|
42
|
+
warning(`The --to option is for the ${chalk.bold('rollback')} command.`)
|
|
43
|
+
const rollbackCmd = `launchpd rollback ${subdomain} --to ${options.to}`
|
|
44
|
+
info(`Try: ${chalk.cyan(rollbackCmd)}`)
|
|
45
|
+
log('')
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const fetchSpinner = spinner(`Fetching versions for ${subdomain}...`)
|
|
50
|
+
|
|
51
|
+
let versionList = []
|
|
52
|
+
let activeVersion = 1
|
|
53
|
+
|
|
54
|
+
// Try API first
|
|
55
|
+
const apiResult = await getVersionsFromAPI(subdomain)
|
|
56
|
+
if (apiResult?.versions) {
|
|
57
|
+
versionList = apiResult.versions.map((v) => ({
|
|
58
|
+
version: v.version,
|
|
59
|
+
timestamp: v.created_at || v.timestamp,
|
|
60
|
+
fileCount: v.file_count || v.fileCount,
|
|
61
|
+
totalBytes: v.total_bytes || v.totalBytes,
|
|
62
|
+
message: v.message || ''
|
|
63
|
+
}))
|
|
64
|
+
activeVersion = apiResult.activeVersion || 1
|
|
65
|
+
} else {
|
|
66
|
+
// Fallback to R2 metadata
|
|
67
|
+
versionList = await getVersionsForSubdomain(subdomain)
|
|
68
|
+
activeVersion = await getActiveVersion(subdomain)
|
|
24
69
|
}
|
|
25
70
|
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
71
|
+
if (versionList.length === 0) {
|
|
72
|
+
fetchSpinner.fail(`No deployments found for: ${subdomain}`)
|
|
73
|
+
errorWithSuggestions(
|
|
74
|
+
`No deployments found for subdomain: ${subdomain}`,
|
|
75
|
+
[
|
|
76
|
+
'Check the subdomain name is correct',
|
|
77
|
+
'Run "launchpd list" to see your deployments',
|
|
78
|
+
'Deploy a new site with "launchpd deploy ./folder"'
|
|
79
|
+
],
|
|
80
|
+
{ verbose }
|
|
81
|
+
)
|
|
82
|
+
process.exit(1)
|
|
30
83
|
}
|
|
31
84
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}))
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
if (versionList.length === 0) {
|
|
56
|
-
fetchSpinner.fail(`No deployments found for: ${subdomain}`);
|
|
57
|
-
errorWithSuggestions(`No deployments found for subdomain: ${subdomain}`, [
|
|
58
|
-
'Check the subdomain name is correct',
|
|
59
|
-
'Run "launchpd list" to see your deployments',
|
|
60
|
-
'Deploy a new site with "launchpd deploy ./folder"',
|
|
61
|
-
], { verbose });
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
fetchSpinner.succeed(`Found ${versionList.length} version(s)`);
|
|
66
|
-
|
|
67
|
-
if (options.json) {
|
|
68
|
-
log(JSON.stringify({
|
|
69
|
-
subdomain,
|
|
70
|
-
activeVersion,
|
|
71
|
-
versions: versionList.map(v => ({
|
|
72
|
-
version: v.version,
|
|
73
|
-
timestamp: v.timestamp,
|
|
74
|
-
fileCount: v.fileCount,
|
|
75
|
-
totalBytes: v.totalBytes,
|
|
76
|
-
isActive: v.version === activeVersion,
|
|
77
|
-
message: v.message,
|
|
78
|
-
})),
|
|
79
|
-
}, null, 2));
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
log('');
|
|
84
|
-
success(`Versions for ${chalk.cyan(subdomain)}.launchpd.cloud:`);
|
|
85
|
-
log('');
|
|
86
|
-
|
|
87
|
-
// Table header
|
|
88
|
-
log(chalk.gray(' Version Date Files Size Status Message'));
|
|
89
|
-
log(chalk.gray(' ' + '─'.repeat(100)));
|
|
90
|
-
|
|
91
|
-
for (const v of versionList) {
|
|
92
|
-
const isActive = v.version === activeVersion;
|
|
93
|
-
|
|
94
|
-
// Format raw strings for correct padding calculation
|
|
95
|
-
const versionRaw = `v${v.version}`;
|
|
96
|
-
const dateRaw = new Date(v.timestamp).toLocaleString();
|
|
97
|
-
const filesRaw = `${v.fileCount} files`;
|
|
98
|
-
const sizeRaw = v.totalBytes ? formatSize(v.totalBytes) : 'unknown';
|
|
99
|
-
|
|
100
|
-
// Apply colors and padding separately
|
|
101
|
-
const versionStr = chalk.bold.cyan(versionRaw.padEnd(12));
|
|
102
|
-
const dateStr = chalk.gray(dateRaw.padEnd(25));
|
|
103
|
-
const filesStr = chalk.white(filesRaw.padEnd(10));
|
|
104
|
-
const sizeStr = chalk.white(sizeRaw.padEnd(12));
|
|
105
|
-
const statusStr = isActive
|
|
106
|
-
? chalk.green.bold('● active'.padEnd(12))
|
|
107
|
-
: chalk.gray('○ inactive'.padEnd(12));
|
|
108
|
-
|
|
109
|
-
const messageStr = chalk.italic.gray(v.message || '');
|
|
110
|
-
|
|
111
|
-
log(` ${versionStr}${dateStr}${filesStr}${sizeStr}${statusStr}${messageStr}`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
log(chalk.gray(' ' + '─'.repeat(100)));
|
|
115
|
-
log('');
|
|
116
|
-
info(`Use ${chalk.cyan(`launchpd rollback ${subdomain} --to <n>`)} to restore a version.`);
|
|
117
|
-
log('');
|
|
118
|
-
|
|
119
|
-
} catch (err) {
|
|
120
|
-
errorWithSuggestions(`Failed to list versions: ${err.message}`, [
|
|
121
|
-
'Check your internet connection',
|
|
122
|
-
'Verify the subdomain exists',
|
|
123
|
-
'Try running with --verbose for more details',
|
|
124
|
-
], { verbose, cause: err });
|
|
125
|
-
process.exit(1);
|
|
85
|
+
fetchSpinner.succeed(`Found ${versionList.length} version(s)`)
|
|
86
|
+
|
|
87
|
+
if (options.json) {
|
|
88
|
+
log(
|
|
89
|
+
JSON.stringify(
|
|
90
|
+
{
|
|
91
|
+
subdomain,
|
|
92
|
+
activeVersion,
|
|
93
|
+
versions: versionList.map((v) => ({
|
|
94
|
+
version: v.version,
|
|
95
|
+
timestamp: v.timestamp,
|
|
96
|
+
fileCount: v.fileCount,
|
|
97
|
+
totalBytes: v.totalBytes,
|
|
98
|
+
isActive: v.version === activeVersion,
|
|
99
|
+
message: v.message
|
|
100
|
+
}))
|
|
101
|
+
},
|
|
102
|
+
null,
|
|
103
|
+
2
|
|
104
|
+
)
|
|
105
|
+
)
|
|
106
|
+
return
|
|
126
107
|
}
|
|
108
|
+
|
|
109
|
+
log('')
|
|
110
|
+
success(`Versions for ${chalk.cyan(subdomain)}.launchpd.cloud:`)
|
|
111
|
+
log('')
|
|
112
|
+
|
|
113
|
+
// Table header
|
|
114
|
+
log(
|
|
115
|
+
chalk.gray(
|
|
116
|
+
' Version Date Files Size Status Message'
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
log(chalk.gray(` ${'─'.repeat(100)}`))
|
|
120
|
+
|
|
121
|
+
for (const v of versionList) {
|
|
122
|
+
const isActive = v.version === activeVersion
|
|
123
|
+
|
|
124
|
+
// Format raw strings for correct padding calculation
|
|
125
|
+
const versionRaw = `v${v.version}`
|
|
126
|
+
const dateRaw = new Date(v.timestamp).toLocaleString()
|
|
127
|
+
const filesRaw = `${v.fileCount} files`
|
|
128
|
+
const sizeRaw = v.totalBytes ? formatSize(v.totalBytes) : 'unknown'
|
|
129
|
+
|
|
130
|
+
// Apply colors and padding separately
|
|
131
|
+
const versionStr = chalk.bold.cyan(versionRaw.padEnd(12))
|
|
132
|
+
const dateStr = chalk.gray(dateRaw.padEnd(25))
|
|
133
|
+
const filesStr = chalk.white(filesRaw.padEnd(10))
|
|
134
|
+
const sizeStr = chalk.white(sizeRaw.padEnd(12))
|
|
135
|
+
const statusStr = isActive
|
|
136
|
+
? chalk.green.bold('● active'.padEnd(12))
|
|
137
|
+
: chalk.gray('○ inactive'.padEnd(12))
|
|
138
|
+
|
|
139
|
+
const messageStr = chalk.italic.gray(v.message || '')
|
|
140
|
+
|
|
141
|
+
log(
|
|
142
|
+
` ${versionStr}${dateStr}${filesStr}${sizeStr}${statusStr}${messageStr}`
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
log(chalk.gray(` ${'─'.repeat(100)}`))
|
|
147
|
+
log('')
|
|
148
|
+
info(
|
|
149
|
+
(() => {
|
|
150
|
+
const rollbackCmd = `launchpd rollback ${subdomain} --to <n>`
|
|
151
|
+
return `Use ${chalk.cyan(rollbackCmd)} to restore a version.`
|
|
152
|
+
})()
|
|
153
|
+
)
|
|
154
|
+
log('')
|
|
155
|
+
} catch (err) {
|
|
156
|
+
errorWithSuggestions(
|
|
157
|
+
`Failed to list versions: ${err.message}`,
|
|
158
|
+
[
|
|
159
|
+
'Check your internet connection',
|
|
160
|
+
'Verify the subdomain exists',
|
|
161
|
+
'Try running with --verbose for more details'
|
|
162
|
+
],
|
|
163
|
+
{ verbose, cause: err }
|
|
164
|
+
)
|
|
165
|
+
process.exit(1)
|
|
166
|
+
}
|
|
127
167
|
}
|
package/src/config.js
CHANGED
|
@@ -1,31 +1,32 @@
|
|
|
1
|
-
import { readFileSync } from 'node:fs'
|
|
2
|
-
import { join } from 'node:path'
|
|
3
|
-
import { fileURLToPath } from 'node:url'
|
|
4
|
-
|
|
5
|
-
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
|
6
|
-
|
|
7
|
-
let packageJson = { version: '1.0.0' }
|
|
8
|
-
try {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
} catch {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
*
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
1
|
+
import { readFileSync } from 'node:fs'
|
|
2
|
+
import { join } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
|
6
|
+
|
|
7
|
+
let packageJson = { version: '1.0.0' } // Default fallback
|
|
8
|
+
try {
|
|
9
|
+
const content = readFileSync(join(__dirname, '../package.json'), 'utf8')
|
|
10
|
+
if (content) {
|
|
11
|
+
packageJson = JSON.parse(content)
|
|
12
|
+
}
|
|
13
|
+
} catch (err) {
|
|
14
|
+
if (err.code !== 'ENOENT') {
|
|
15
|
+
console.warn('Warning: Could not read package.json:', err.message)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Application configuration for Launchpd CLI
|
|
21
|
+
* No credentials needed - uploads go through the API proxy
|
|
22
|
+
*/
|
|
23
|
+
export const config = {
|
|
24
|
+
// Base domain for deployments
|
|
25
|
+
domain: 'launchpd.cloud',
|
|
26
|
+
|
|
27
|
+
// API endpoint
|
|
28
|
+
apiUrl: 'https://api.launchpd.cloud',
|
|
29
|
+
|
|
30
|
+
// CLI version
|
|
31
|
+
version: packageJson.version
|
|
32
|
+
}
|