@pushto/cli 0.0.9 → 0.1.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/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +32 -10
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/domain.d.ts.map +1 -1
- package/dist/commands/domain.js +25 -14
- package/dist/commands/domain.js.map +1 -1
- package/dist/commands/env.d.ts.map +1 -1
- package/dist/commands/env.js +43 -1
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/upgrade.d.ts +2 -0
- package/dist/commands/upgrade.d.ts.map +1 -0
- package/dist/commands/upgrade.js +19 -0
- package/dist/commands/upgrade.js.map +1 -0
- package/dist/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +35 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +19 -2
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
- package/.turbo/turbo-build.log +0 -4
- package/bin/pushto.js +0 -2
- package/src/commands/deploy.ts +0 -135
- package/src/commands/doctor.ts +0 -160
- package/src/commands/domain.ts +0 -54
- package/src/commands/env.ts +0 -129
- package/src/commands/init.ts +0 -66
- package/src/commands/login.ts +0 -97
- package/src/commands/logs.ts +0 -24
- package/src/commands/open.ts +0 -27
- package/src/commands/rollback.ts +0 -43
- package/src/commands/status.ts +0 -59
- package/src/index.ts +0 -88
- package/src/lib/api.ts +0 -25
- package/src/lib/config.ts +0 -30
- package/src/lib/resolve-slug.ts +0 -23
- package/src/lib/safe-json.ts +0 -15
- package/tsconfig.json +0 -18
package/src/commands/doctor.ts
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { getToken } from '../lib/config.js';
|
|
5
|
-
import { resolveSlug } from '../lib/resolve-slug.js';
|
|
6
|
-
import { api } from '../lib/api.js';
|
|
7
|
-
|
|
8
|
-
interface Check {
|
|
9
|
-
name: string;
|
|
10
|
-
status: 'pass' | 'warn' | 'fail';
|
|
11
|
-
message: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const doctor = async (): Promise<void> => {
|
|
15
|
-
console.log(chalk.green('> pushto doctor\n'));
|
|
16
|
-
|
|
17
|
-
const checks: Check[] = [];
|
|
18
|
-
const cwd = process.cwd();
|
|
19
|
-
|
|
20
|
-
// 1. Auth check
|
|
21
|
-
const token = getToken();
|
|
22
|
-
checks.push(token
|
|
23
|
-
? { name: 'auth', status: 'pass', message: 'logged in' }
|
|
24
|
-
: { name: 'auth', status: 'fail', message: 'not logged in. run pushto login' },
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
// 2. Project linked
|
|
28
|
-
const slug = resolveSlug();
|
|
29
|
-
checks.push(slug
|
|
30
|
-
? { name: 'project', status: 'pass', message: `linked to ${slug}` }
|
|
31
|
-
: { name: 'project', status: 'fail', message: 'no .pushto file. run pushto init <name>' },
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
// 3. Package.json exists
|
|
35
|
-
const pkgPath = path.join(cwd, 'package.json');
|
|
36
|
-
const hasPkg = fs.existsSync(pkgPath);
|
|
37
|
-
checks.push(hasPkg
|
|
38
|
-
? { name: 'package.json', status: 'pass', message: 'found' }
|
|
39
|
-
: { name: 'package.json', status: 'warn', message: 'not found. static site? that works too.' },
|
|
40
|
-
);
|
|
41
|
-
|
|
42
|
-
// 4. Framework detection
|
|
43
|
-
if (hasPkg) {
|
|
44
|
-
try {
|
|
45
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
46
|
-
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
47
|
-
const framework =
|
|
48
|
-
deps['next'] ? 'Next.js' :
|
|
49
|
-
deps['react'] ? 'React' :
|
|
50
|
-
deps['vue'] ? 'Vue' :
|
|
51
|
-
deps['svelte'] ? 'Svelte' :
|
|
52
|
-
deps['@sveltejs/kit'] ? 'SvelteKit' :
|
|
53
|
-
deps['astro'] ? 'Astro' :
|
|
54
|
-
deps['nuxt'] ? 'Nuxt' :
|
|
55
|
-
deps['express'] ? 'Express' :
|
|
56
|
-
deps['fastify'] ? 'Fastify' :
|
|
57
|
-
deps['hono'] ? 'Hono' :
|
|
58
|
-
null;
|
|
59
|
-
|
|
60
|
-
checks.push(framework
|
|
61
|
-
? { name: 'framework', status: 'pass', message: `detected ${framework}` }
|
|
62
|
-
: { name: 'framework', status: 'warn', message: 'no known framework detected. nixpacks will figure it out.' },
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
// 5. Build script
|
|
66
|
-
const hasBuild = pkg.scripts?.build;
|
|
67
|
-
checks.push(hasBuild
|
|
68
|
-
? { name: 'build script', status: 'pass', message: `"${hasBuild}"` }
|
|
69
|
-
: { name: 'build script', status: 'warn', message: 'no build script. might be fine for static sites.' },
|
|
70
|
-
);
|
|
71
|
-
|
|
72
|
-
// 6. Node version
|
|
73
|
-
const nodeVersion = pkg.engines?.node;
|
|
74
|
-
if (nodeVersion) {
|
|
75
|
-
checks.push({ name: 'node version', status: 'pass', message: nodeVersion });
|
|
76
|
-
}
|
|
77
|
-
} catch {
|
|
78
|
-
checks.push({ name: 'package.json', status: 'warn', message: 'could not parse package.json' });
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 7. .gitignore check
|
|
83
|
-
const gitignorePath = path.join(cwd, '.gitignore');
|
|
84
|
-
if (fs.existsSync(gitignorePath)) {
|
|
85
|
-
const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
|
|
86
|
-
if (!gitignore.includes('node_modules')) {
|
|
87
|
-
checks.push({ name: '.gitignore', status: 'warn', message: 'node_modules not in .gitignore. deploy will be slow.' });
|
|
88
|
-
}
|
|
89
|
-
if (!gitignore.includes('.env')) {
|
|
90
|
-
checks.push({ name: '.gitignore', status: 'warn', message: '.env not in .gitignore. secrets might leak.' });
|
|
91
|
-
}
|
|
92
|
-
if (!gitignore.includes('.pushto')) {
|
|
93
|
-
checks.push({ name: '.gitignore', status: 'warn', message: '.pushto not in .gitignore. add it.' });
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 8. Env vars check (if project linked and authed)
|
|
98
|
-
if (token && slug) {
|
|
99
|
-
try {
|
|
100
|
-
const res = await api(`/cli/projects/${slug}`);
|
|
101
|
-
if (res.ok) {
|
|
102
|
-
checks.push({ name: 'api', status: 'pass', message: 'pushto.host reachable' });
|
|
103
|
-
} else {
|
|
104
|
-
checks.push({ name: 'api', status: 'fail', message: 'project not found on pushto.host' });
|
|
105
|
-
}
|
|
106
|
-
} catch {
|
|
107
|
-
checks.push({ name: 'api', status: 'fail', message: 'cannot reach pushto.host' });
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 9. Large files check
|
|
112
|
-
const largeFiles: string[] = [];
|
|
113
|
-
const checkDir = (dir: string, depth = 0) => {
|
|
114
|
-
if (depth > 3) return;
|
|
115
|
-
try {
|
|
116
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
117
|
-
for (const entry of entries) {
|
|
118
|
-
if (entry.name === 'node_modules' || entry.name === '.git' || entry.name === '.next') continue;
|
|
119
|
-
const full = path.join(dir, entry.name);
|
|
120
|
-
if (entry.isFile()) {
|
|
121
|
-
const stat = fs.statSync(full);
|
|
122
|
-
if (stat.size > 10 * 1024 * 1024) {
|
|
123
|
-
largeFiles.push(`${entry.name} (${Math.round(stat.size / 1024 / 1024)}MB)`);
|
|
124
|
-
}
|
|
125
|
-
} else if (entry.isDirectory()) {
|
|
126
|
-
checkDir(full, depth + 1);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
} catch { /* permission error, skip */ }
|
|
130
|
-
};
|
|
131
|
-
checkDir(cwd);
|
|
132
|
-
|
|
133
|
-
if (largeFiles.length > 0) {
|
|
134
|
-
checks.push({ name: 'large files', status: 'warn', message: `found: ${largeFiles.join(', ')}. will slow deploy.` });
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Print results
|
|
138
|
-
console.log();
|
|
139
|
-
for (const check of checks) {
|
|
140
|
-
const icon = check.status === 'pass' ? chalk.green('✓')
|
|
141
|
-
: check.status === 'warn' ? chalk.yellow('⚠')
|
|
142
|
-
: chalk.red('✗');
|
|
143
|
-
const color = check.status === 'pass' ? chalk.dim
|
|
144
|
-
: check.status === 'warn' ? chalk.yellow
|
|
145
|
-
: chalk.red;
|
|
146
|
-
console.log(` ${icon} ${chalk.dim(check.name.padEnd(16))} ${color(check.message)}`);
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
const fails = checks.filter((c) => c.status === 'fail');
|
|
150
|
-
const warns = checks.filter((c) => c.status === 'warn');
|
|
151
|
-
|
|
152
|
-
console.log();
|
|
153
|
-
if (fails.length > 0) {
|
|
154
|
-
console.log(chalk.red(` ${fails.length} issue${fails.length > 1 ? 's' : ''} to fix before deploying.`));
|
|
155
|
-
} else if (warns.length > 0) {
|
|
156
|
-
console.log(chalk.yellow(` ${warns.length} warning${warns.length > 1 ? 's' : ''}. probably fine. deploy when ready.`));
|
|
157
|
-
} else {
|
|
158
|
-
console.log(chalk.green(' all good. run pushto to deploy.'));
|
|
159
|
-
}
|
|
160
|
-
};
|
package/src/commands/domain.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { getToken } from '../lib/config.js';
|
|
3
|
-
import { resolveSlug } from '../lib/resolve-slug.js';
|
|
4
|
-
|
|
5
|
-
export const domain = async (action: string, domainName?: string): Promise<void> => {
|
|
6
|
-
const token = getToken();
|
|
7
|
-
if (!token) {
|
|
8
|
-
console.log(chalk.red('> not logged in.'));
|
|
9
|
-
process.exit(1);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const slug = resolveSlug();
|
|
13
|
-
if (!slug) {
|
|
14
|
-
console.log(chalk.red('> no project here. run pushto init first.'));
|
|
15
|
-
process.exit(1);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (action === 'add') {
|
|
19
|
-
if (!domainName) {
|
|
20
|
-
console.log(chalk.red('> usage: pushto domain add yourdomain.com'));
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
console.log(chalk.green(`> setting up ${domainName} for ${slug}\n`));
|
|
25
|
-
console.log(chalk.dim(' add this DNS record at your domain provider:\n'));
|
|
26
|
-
console.log(` ${chalk.cyan('Type')} ${chalk.cyan('Name')} ${chalk.cyan('Value')}`);
|
|
27
|
-
console.log(` CNAME @ ${slug}.pushto.host`);
|
|
28
|
-
console.log();
|
|
29
|
-
console.log(chalk.dim(' or if your provider doesn\'t support CNAME on root:\n'));
|
|
30
|
-
console.log(` A @ 76.76.21.21`);
|
|
31
|
-
console.log();
|
|
32
|
-
|
|
33
|
-
// TODO: when Cloudflare DNS API is wired, this will:
|
|
34
|
-
// 1. Register the domain in our system
|
|
35
|
-
// 2. Provision SSL via Let's Encrypt
|
|
36
|
-
// 3. Poll DNS until verified
|
|
37
|
-
// 4. Update the project's customDomain field
|
|
38
|
-
|
|
39
|
-
console.log(chalk.yellow(' auto-verification coming soon.'));
|
|
40
|
-
console.log(chalk.dim(' for now, add the domain in your dashboard too:'));
|
|
41
|
-
console.log(chalk.cyan(` https://pushto.host/dashboard/projects/${slug}/settings`));
|
|
42
|
-
|
|
43
|
-
} else if (action === 'remove' || action === 'rm') {
|
|
44
|
-
console.log(chalk.green(`> removing custom domain from ${slug}`));
|
|
45
|
-
console.log(chalk.yellow(' domain management coming soon via CLI.'));
|
|
46
|
-
console.log(chalk.dim(' use the dashboard for now:'));
|
|
47
|
-
console.log(chalk.cyan(` https://pushto.host/dashboard/projects/${slug}/settings`));
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
console.log(chalk.dim(' usage:'));
|
|
51
|
-
console.log(chalk.cyan(' pushto domain add yourdomain.com'));
|
|
52
|
-
console.log(chalk.cyan(' pushto domain remove'));
|
|
53
|
-
}
|
|
54
|
-
};
|
package/src/commands/env.ts
DELETED
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { getToken } from '../lib/config.js';
|
|
5
|
-
import { resolveSlug } from '../lib/resolve-slug.js';
|
|
6
|
-
import { api } from '../lib/api.js';
|
|
7
|
-
import { safeJson } from '../lib/safe-json.js';
|
|
8
|
-
|
|
9
|
-
export const env = async (action: string, args: string[]): Promise<void> => {
|
|
10
|
-
const token = getToken();
|
|
11
|
-
if (!token) {
|
|
12
|
-
console.log(chalk.red('> not logged in.'));
|
|
13
|
-
console.log(chalk.dim(' run ') + chalk.cyan('pushto login') + chalk.dim(' first.'));
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const slug = resolveSlug();
|
|
18
|
-
if (!slug) {
|
|
19
|
-
console.log(chalk.red('> no project here. run pushto init first.'));
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (action === 'set') {
|
|
24
|
-
// pushto env set KEY=value
|
|
25
|
-
const pair = args[0];
|
|
26
|
-
if (!pair || !pair.includes('=')) {
|
|
27
|
-
console.log(chalk.red('> usage: pushto env set KEY=value'));
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const eqIdx = pair.indexOf('=');
|
|
32
|
-
const key = pair.slice(0, eqIdx);
|
|
33
|
-
const value = pair.slice(eqIdx + 1);
|
|
34
|
-
|
|
35
|
-
if (!key) {
|
|
36
|
-
console.log(chalk.red('> key is empty.'));
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// Call the tRPC mutation via REST-like endpoint
|
|
41
|
-
// We'll use a simple CLI API endpoint for this
|
|
42
|
-
const res = await api(`/cli/projects/${slug}/env`, {
|
|
43
|
-
method: 'POST',
|
|
44
|
-
body: JSON.stringify({ key, value }),
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
if (!res.ok) {
|
|
48
|
-
const data = (await safeJson<{ error?: string }>(res));
|
|
49
|
-
console.log(chalk.red(`> ${data.error ?? 'failed to set env var.'}`));
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
console.log(chalk.green(`> ${key} set.`));
|
|
54
|
-
|
|
55
|
-
} else if (action === 'list' || action === 'ls') {
|
|
56
|
-
// pushto env list
|
|
57
|
-
const res = await api(`/cli/projects/${slug}/env`);
|
|
58
|
-
|
|
59
|
-
if (!res.ok) {
|
|
60
|
-
const data = (await safeJson<{ error?: string }>(res));
|
|
61
|
-
console.log(chalk.red(`> ${data.error ?? 'failed to list env vars.'}`));
|
|
62
|
-
process.exit(1);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const vars = await safeJson<{ key: string; value: string }[]>(res);
|
|
66
|
-
|
|
67
|
-
if (vars.length === 0) {
|
|
68
|
-
console.log(chalk.dim('> no env vars set.'));
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
console.log(chalk.green(`> ${slug} env vars\n`));
|
|
73
|
-
for (const v of vars) {
|
|
74
|
-
console.log(` ${chalk.cyan(v.key)}=${chalk.dim('••••••••')}`);
|
|
75
|
-
}
|
|
76
|
-
console.log(chalk.dim(`\n ${vars.length} variable${vars.length > 1 ? 's' : ''}`));
|
|
77
|
-
|
|
78
|
-
} else if (action === 'pull') {
|
|
79
|
-
// pushto env pull — download to .env
|
|
80
|
-
const res = await api(`/cli/projects/${slug}/env`);
|
|
81
|
-
|
|
82
|
-
if (!res.ok) {
|
|
83
|
-
const data = (await safeJson<{ error?: string }>(res));
|
|
84
|
-
console.log(chalk.red(`> ${data.error ?? 'failed to pull env vars.'}`));
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const vars = await safeJson<{ key: string; value: string }[]>(res);
|
|
89
|
-
|
|
90
|
-
if (vars.length === 0) {
|
|
91
|
-
console.log(chalk.dim('> no env vars to pull.'));
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const envContent = vars.map((v) => `${v.key}=${v.value}`).join('\n') + '\n';
|
|
96
|
-
const envPath = path.join(process.cwd(), '.env');
|
|
97
|
-
|
|
98
|
-
fs.writeFileSync(envPath, envContent);
|
|
99
|
-
console.log(chalk.green(`> pulled ${vars.length} var${vars.length > 1 ? 's' : ''} to .env`));
|
|
100
|
-
console.log(chalk.dim(' make sure .env is in your .gitignore.'));
|
|
101
|
-
|
|
102
|
-
} else if (action === 'rm' || action === 'remove' || action === 'delete') {
|
|
103
|
-
const key = args[0];
|
|
104
|
-
if (!key) {
|
|
105
|
-
console.log(chalk.red('> usage: pushto env rm KEY'));
|
|
106
|
-
process.exit(1);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
const res = await api(`/cli/projects/${slug}/env`, {
|
|
110
|
-
method: 'DELETE',
|
|
111
|
-
body: JSON.stringify({ key }),
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
if (!res.ok) {
|
|
115
|
-
const data = (await safeJson<{ error?: string }>(res));
|
|
116
|
-
console.log(chalk.red(`> ${data.error ?? 'failed to delete env var.'}`));
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
console.log(chalk.green(`> ${key} removed.`));
|
|
121
|
-
|
|
122
|
-
} else {
|
|
123
|
-
console.log(chalk.dim(' usage:'));
|
|
124
|
-
console.log(chalk.cyan(' pushto env set KEY=value'));
|
|
125
|
-
console.log(chalk.cyan(' pushto env list'));
|
|
126
|
-
console.log(chalk.cyan(' pushto env pull'));
|
|
127
|
-
console.log(chalk.cyan(' pushto env rm KEY'));
|
|
128
|
-
}
|
|
129
|
-
};
|
package/src/commands/init.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { getToken, setProjectSlug } from '../lib/config.js';
|
|
6
|
-
import { api } from '../lib/api.js';
|
|
7
|
-
import { safeJson } from '../lib/safe-json.js';
|
|
8
|
-
|
|
9
|
-
export const init = async (name: string): Promise<void> => {
|
|
10
|
-
const token = getToken();
|
|
11
|
-
if (!token) {
|
|
12
|
-
console.log(chalk.red('> not logged in.'));
|
|
13
|
-
console.log(chalk.dim(' run ') + chalk.cyan('pushto login') + chalk.dim(' first.'));
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const slug = name
|
|
18
|
-
.toLowerCase()
|
|
19
|
-
.replace(/[^a-z0-9-]/g, '-')
|
|
20
|
-
.replace(/-+/g, '-')
|
|
21
|
-
.replace(/^-|-$/g, '');
|
|
22
|
-
|
|
23
|
-
if (slug.length < 3) {
|
|
24
|
-
console.log(chalk.red('> name too short. needs at least 3 characters.'));
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const spinner = ora('creating project...').start();
|
|
29
|
-
|
|
30
|
-
try {
|
|
31
|
-
const res = await api('/cli/projects', {
|
|
32
|
-
method: 'POST',
|
|
33
|
-
body: JSON.stringify({ name, slug }),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
const data = await safeJson<{
|
|
37
|
-
error?: string;
|
|
38
|
-
project?: { id: string };
|
|
39
|
-
message?: string;
|
|
40
|
-
}>(res);
|
|
41
|
-
|
|
42
|
-
if (!res.ok) {
|
|
43
|
-
spinner.fail(chalk.red(`> ${data.error ?? 'something went wrong.'}`));
|
|
44
|
-
process.exit(1);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Write .pushto file in current directory
|
|
48
|
-
const pushtoConfig = { slug, projectId: data.project?.id };
|
|
49
|
-
fs.writeFileSync(
|
|
50
|
-
path.join(process.cwd(), '.pushto'),
|
|
51
|
-
JSON.stringify(pushtoConfig, null, 2) + '\n',
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
// Also store globally so bare `pushto` works from this dir
|
|
55
|
-
setProjectSlug(slug);
|
|
56
|
-
|
|
57
|
-
spinner.succeed(chalk.green(`> ${slug} is ready.`));
|
|
58
|
-
console.log(chalk.dim(` ${data.message ?? 'project created.'}`));
|
|
59
|
-
console.log();
|
|
60
|
-
console.log(chalk.dim(' deploy anytime with just: ') + chalk.cyan('pushto'));
|
|
61
|
-
console.log(chalk.dim(' your url will be: ') + chalk.cyan(`${slug}.pushto.host`));
|
|
62
|
-
} catch (err) {
|
|
63
|
-
spinner.fail(chalk.red('> could not reach pushto.host. check your internet.'));
|
|
64
|
-
process.exit(1);
|
|
65
|
-
}
|
|
66
|
-
};
|
package/src/commands/login.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import http from 'node:http';
|
|
3
|
-
import { execSync } from 'node:child_process';
|
|
4
|
-
import { setToken, config } from '../lib/config.js';
|
|
5
|
-
import { api } from '../lib/api.js';
|
|
6
|
-
|
|
7
|
-
export const login = async (): Promise<void> => {
|
|
8
|
-
const baseUrl = config.get('apiUrl');
|
|
9
|
-
|
|
10
|
-
// Start a local server to receive the callback
|
|
11
|
-
const server = http.createServer((req, res) => {
|
|
12
|
-
// Handle CORS preflight
|
|
13
|
-
if (req.method === 'OPTIONS') {
|
|
14
|
-
res.writeHead(200, {
|
|
15
|
-
'Access-Control-Allow-Origin': baseUrl,
|
|
16
|
-
'Access-Control-Allow-Methods': 'POST, OPTIONS',
|
|
17
|
-
'Access-Control-Allow-Headers': 'Content-Type',
|
|
18
|
-
});
|
|
19
|
-
res.end();
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (req.method === 'POST' && req.url === '/callback') {
|
|
24
|
-
let body = '';
|
|
25
|
-
req.on('data', (chunk) => (body += chunk));
|
|
26
|
-
req.on('end', async () => {
|
|
27
|
-
res.writeHead(200, {
|
|
28
|
-
'Content-Type': 'application/json',
|
|
29
|
-
'Access-Control-Allow-Origin': baseUrl,
|
|
30
|
-
});
|
|
31
|
-
res.end(JSON.stringify({ ok: true }));
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
const { access_token } = JSON.parse(body);
|
|
35
|
-
if (!access_token) {
|
|
36
|
-
console.log(chalk.red('\n> auth failed. no token received.'));
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
setToken(access_token);
|
|
41
|
-
|
|
42
|
-
// Verify the token
|
|
43
|
-
const meRes = await api('/cli/me');
|
|
44
|
-
if (meRes.ok) {
|
|
45
|
-
const me = (await meRes.json()) as { email: string; tier: string };
|
|
46
|
-
console.log(chalk.green(`\n> logged in as ${me.email}`));
|
|
47
|
-
console.log(chalk.dim(` tier: ${me.tier.toLowerCase()}`));
|
|
48
|
-
} else {
|
|
49
|
-
console.log(chalk.green('\n> logged in.'));
|
|
50
|
-
}
|
|
51
|
-
} catch {
|
|
52
|
-
console.log(chalk.green('\n> logged in.'));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
server.close();
|
|
56
|
-
process.exit(0);
|
|
57
|
-
});
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
res.writeHead(404);
|
|
62
|
-
res.end();
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Listen on a random available port
|
|
66
|
-
server.listen(0, () => {
|
|
67
|
-
const port = (server.address() as { port: number }).port;
|
|
68
|
-
const authUrl = `${baseUrl}/cli/auth?port=${port}`;
|
|
69
|
-
|
|
70
|
-
console.log(chalk.dim('> opening browser to log you in...'));
|
|
71
|
-
console.log(chalk.dim(` if it doesn't open, go to: `) + chalk.cyan(authUrl));
|
|
72
|
-
|
|
73
|
-
// Open browser
|
|
74
|
-
const open = (url: string) => {
|
|
75
|
-
const cmd =
|
|
76
|
-
process.platform === 'darwin'
|
|
77
|
-
? `open "${url}"`
|
|
78
|
-
: process.platform === 'win32'
|
|
79
|
-
? `start "${url}"`
|
|
80
|
-
: `xdg-open "${url}"`;
|
|
81
|
-
try {
|
|
82
|
-
execSync(cmd, { stdio: 'ignore' });
|
|
83
|
-
} catch {
|
|
84
|
-
// User will see the URL in the console
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
open(authUrl);
|
|
89
|
-
|
|
90
|
-
// Timeout after 2 minutes
|
|
91
|
-
setTimeout(() => {
|
|
92
|
-
console.log(chalk.red('\n> login timed out. try again.'));
|
|
93
|
-
server.close();
|
|
94
|
-
process.exit(1);
|
|
95
|
-
}, 120_000);
|
|
96
|
-
});
|
|
97
|
-
};
|
package/src/commands/logs.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { getToken } from '../lib/config.js';
|
|
3
|
-
import { resolveSlug } from '../lib/resolve-slug.js';
|
|
4
|
-
|
|
5
|
-
export const logs = async (): Promise<void> => {
|
|
6
|
-
const token = getToken();
|
|
7
|
-
if (!token) {
|
|
8
|
-
console.log(chalk.red('> not logged in.'));
|
|
9
|
-
console.log(chalk.dim(' run ') + chalk.cyan('pushto login') + chalk.dim(' first.'));
|
|
10
|
-
process.exit(1);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
const slug = resolveSlug();
|
|
14
|
-
if (!slug) {
|
|
15
|
-
console.log(chalk.red('> no project here. run pushto init first.'));
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
console.log(chalk.dim(`> streaming logs for ${slug}...`));
|
|
20
|
-
console.log(chalk.dim(' (ctrl+c to stop)\n'));
|
|
21
|
-
|
|
22
|
-
// TODO: Phase 3 — SSE connection to log stream API
|
|
23
|
-
console.log(chalk.yellow('> log streaming coming soon. deploy first, then we\'ll show you everything.'));
|
|
24
|
-
};
|
package/src/commands/open.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { execSync } from 'node:child_process';
|
|
3
|
-
import { resolveSlug } from '../lib/resolve-slug.js';
|
|
4
|
-
|
|
5
|
-
export const open = async (): Promise<void> => {
|
|
6
|
-
const slug = resolveSlug();
|
|
7
|
-
if (!slug) {
|
|
8
|
-
console.log(chalk.red('> no project here. run pushto init first.'));
|
|
9
|
-
process.exit(1);
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
const url = `https://${slug}.pushto.host`;
|
|
13
|
-
console.log(chalk.green(`> opening ${url}`));
|
|
14
|
-
|
|
15
|
-
const cmd =
|
|
16
|
-
process.platform === 'darwin'
|
|
17
|
-
? `open "${url}"`
|
|
18
|
-
: process.platform === 'win32'
|
|
19
|
-
? `start "${url}"`
|
|
20
|
-
: `xdg-open "${url}"`;
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
execSync(cmd, { stdio: 'ignore' });
|
|
24
|
-
} catch {
|
|
25
|
-
console.log(chalk.dim(` couldn't open browser. go to: `) + chalk.cyan(url));
|
|
26
|
-
}
|
|
27
|
-
};
|
package/src/commands/rollback.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import ora from 'ora';
|
|
3
|
-
import { getToken } from '../lib/config.js';
|
|
4
|
-
import { resolveSlug } from '../lib/resolve-slug.js';
|
|
5
|
-
import { api } from '../lib/api.js';
|
|
6
|
-
|
|
7
|
-
export const rollback = async (version?: string): Promise<void> => {
|
|
8
|
-
const token = getToken();
|
|
9
|
-
if (!token) {
|
|
10
|
-
console.log(chalk.red('> not logged in.'));
|
|
11
|
-
process.exit(1);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const slug = resolveSlug();
|
|
15
|
-
if (!slug) {
|
|
16
|
-
console.log(chalk.red('> no project here. run pushto init first.'));
|
|
17
|
-
process.exit(1);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const spinner = ora('rolling back...').start();
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
// TODO: when build engine is live, this will call the rollback API
|
|
24
|
-
// which restores the previous Fly Machine image
|
|
25
|
-
spinner.info(chalk.yellow('> rollback is ready to wire up.'));
|
|
26
|
-
console.log();
|
|
27
|
-
console.log(chalk.dim(' when the build engine is live, this command will:'));
|
|
28
|
-
console.log(chalk.dim(' 1. find the last working deployment'));
|
|
29
|
-
console.log(chalk.dim(' 2. restore that container image'));
|
|
30
|
-
console.log(chalk.dim(' 3. update DNS to point to it'));
|
|
31
|
-
console.log(chalk.dim(' 4. mark the current deploy as rolled back'));
|
|
32
|
-
console.log();
|
|
33
|
-
if (version) {
|
|
34
|
-
console.log(chalk.dim(` target version: v${version}`));
|
|
35
|
-
} else {
|
|
36
|
-
console.log(chalk.dim(' target: previous working deploy'));
|
|
37
|
-
}
|
|
38
|
-
console.log(chalk.dim(" you're early. this will work soon."));
|
|
39
|
-
} catch {
|
|
40
|
-
spinner.fail(chalk.red('> rollback failed.'));
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
};
|
package/src/commands/status.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { getToken } from '../lib/config.js';
|
|
3
|
-
import { api } from '../lib/api.js';
|
|
4
|
-
import { resolveSlug } from '../lib/resolve-slug.js';
|
|
5
|
-
import { safeJson } from '../lib/safe-json.js';
|
|
6
|
-
|
|
7
|
-
const STATUS_COLORS: Record<string, (s: string) => string> = {
|
|
8
|
-
CREATED: chalk.dim,
|
|
9
|
-
BUILDING: chalk.yellow,
|
|
10
|
-
DEPLOYING: chalk.yellow,
|
|
11
|
-
LIVE: chalk.green,
|
|
12
|
-
SLEEPING: chalk.cyan,
|
|
13
|
-
FAILED: chalk.red,
|
|
14
|
-
SUSPENDED: chalk.red,
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export const status = async (): Promise<void> => {
|
|
18
|
-
const token = getToken();
|
|
19
|
-
if (!token) {
|
|
20
|
-
console.log(chalk.red('> not logged in.'));
|
|
21
|
-
console.log(chalk.dim(' run ') + chalk.cyan('pushto login') + chalk.dim(' first.'));
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const slug = resolveSlug();
|
|
26
|
-
if (!slug) {
|
|
27
|
-
console.log(chalk.red('> no project here.'));
|
|
28
|
-
console.log(chalk.dim(' run ') + chalk.cyan('pushto init <name>') + chalk.dim(' first.'));
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
const res = await api(`/cli/projects/${slug}`);
|
|
34
|
-
|
|
35
|
-
if (!res.ok) {
|
|
36
|
-
const data = await safeJson<{ error?: string }>(res);
|
|
37
|
-
console.log(chalk.red(`> ${data.error ?? 'something went wrong.'}`));
|
|
38
|
-
process.exit(1);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const project = await safeJson<{
|
|
42
|
-
name: string;
|
|
43
|
-
status: string;
|
|
44
|
-
subdomain: string;
|
|
45
|
-
customDomain?: string | null;
|
|
46
|
-
}>(res);
|
|
47
|
-
const colorFn = STATUS_COLORS[project.status] ?? chalk.dim;
|
|
48
|
-
|
|
49
|
-
console.log(chalk.green(`> ${project.name}`));
|
|
50
|
-
console.log(chalk.dim(' status: ') + colorFn(project.status.toLowerCase()));
|
|
51
|
-
console.log(chalk.dim(' url: ') + chalk.cyan(`${project.subdomain}.pushto.host`));
|
|
52
|
-
if (project.customDomain) {
|
|
53
|
-
console.log(chalk.dim(' domain: ') + chalk.cyan(project.customDomain));
|
|
54
|
-
}
|
|
55
|
-
} catch {
|
|
56
|
-
console.log(chalk.red('> could not reach pushto.host. check your internet.'));
|
|
57
|
-
process.exit(1);
|
|
58
|
-
}
|
|
59
|
-
};
|