onflyt-cli 0.1.0-beta
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/index.js +13093 -0
- package/package.json +61 -0
- package/src/App.tsx +13 -0
- package/src/commands/credits.tsx +151 -0
- package/src/commands/delete.tsx +315 -0
- package/src/commands/deploy.tsx +1039 -0
- package/src/commands/deployments.tsx +331 -0
- package/src/commands/help.tsx +74 -0
- package/src/commands/init.tsx +587 -0
- package/src/commands/login.tsx +207 -0
- package/src/commands/logout.tsx +31 -0
- package/src/commands/logs.tsx +447 -0
- package/src/commands/projects.tsx +287 -0
- package/src/commands/rollback.tsx +455 -0
- package/src/commands/teams.tsx +113 -0
- package/src/commands/whoami.tsx +48 -0
- package/src/components/Loading.tsx +68 -0
- package/src/index.tsx +130 -0
- package/src/lib/api.ts +152 -0
- package/src/lib/config.ts +90 -0
- package/src/lib/deploy-api.ts +515 -0
- package/src/lib/deploy.ts +260 -0
- package/src/lib/framework.ts +227 -0
- package/src/lib/git.ts +179 -0
- package/src/lib/scaffold.ts +225 -0
- package/src/shared.ts +353 -0
- package/src/types.d.ts +5 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
|
+
import { Text, Box, useInput } from "ink";
|
|
3
|
+
import { isLoggedIn, getConfig } from "../lib/config.js";
|
|
4
|
+
import { api } from "../lib/api.js";
|
|
5
|
+
import { Logo } from "../components/Loading.js";
|
|
6
|
+
import Spinner from "ink-spinner";
|
|
7
|
+
|
|
8
|
+
interface DeploymentsProps {
|
|
9
|
+
projectName?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type Step =
|
|
13
|
+
| "loading"
|
|
14
|
+
| "loading-projects"
|
|
15
|
+
| "loading-deployments"
|
|
16
|
+
| "team"
|
|
17
|
+
| "project"
|
|
18
|
+
| "display"
|
|
19
|
+
| "error";
|
|
20
|
+
|
|
21
|
+
const Deployments: React.FC<DeploymentsProps> = ({ projectName }) => {
|
|
22
|
+
const [step, setStep] = useState<Step>("loading");
|
|
23
|
+
|
|
24
|
+
const [teams, setTeams] = useState<any[]>([]);
|
|
25
|
+
const [projects, setProjects] = useState<any[]>([]);
|
|
26
|
+
const [deployments, setDeployments] = useState<any[]>([]);
|
|
27
|
+
const [targetProject, setTargetProject] = useState<any>(null);
|
|
28
|
+
|
|
29
|
+
const [selectedTeamIndex, setSelectedTeamIndex] = useState(0);
|
|
30
|
+
const [selectedProjectIndex, setSelectedProjectIndex] = useState(0);
|
|
31
|
+
const [errorMsg, setErrorMsg] = useState("");
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const handleSigInt = () => process.exit(0);
|
|
35
|
+
process.on("SIGINT", handleSigInt);
|
|
36
|
+
return () => {
|
|
37
|
+
process.off("SIGINT", handleSigInt);
|
|
38
|
+
};
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
useInput((input, key) => {
|
|
42
|
+
if (input === "q" || input === "Q" || (key.ctrl && input === "c")) {
|
|
43
|
+
process.exit(0);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (step === "team") {
|
|
47
|
+
if (key.upArrow) {
|
|
48
|
+
setSelectedTeamIndex((i) => Math.max(0, i - 1));
|
|
49
|
+
} else if (key.downArrow) {
|
|
50
|
+
setSelectedTeamIndex((i) => Math.min(teams.length - 1, i + 1));
|
|
51
|
+
} else if (key.return) {
|
|
52
|
+
setStep("loading-projects");
|
|
53
|
+
loadProjects(teams[selectedTeamIndex].team.id);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (step === "project") {
|
|
58
|
+
if (key.upArrow) {
|
|
59
|
+
setSelectedProjectIndex((i) => Math.max(0, i - 1));
|
|
60
|
+
} else if (key.downArrow) {
|
|
61
|
+
setSelectedProjectIndex((i) => Math.min(projects.length - 1, i + 1));
|
|
62
|
+
} else if (key.return) {
|
|
63
|
+
setStep("loading-deployments");
|
|
64
|
+
loadDeployments(projects[selectedProjectIndex]);
|
|
65
|
+
} else if (key.escape) {
|
|
66
|
+
setStep("team");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
useEffect(() => {
|
|
72
|
+
if (!isLoggedIn()) {
|
|
73
|
+
setErrorMsg("Not logged in. Run 'onflyt login' first.");
|
|
74
|
+
setStep("error");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
loadTeams();
|
|
79
|
+
}, []);
|
|
80
|
+
|
|
81
|
+
const loadTeams = async () => {
|
|
82
|
+
try {
|
|
83
|
+
const config = getConfig();
|
|
84
|
+
api.setToken(config.token!);
|
|
85
|
+
const meData = await api.get<any>("/auth/me");
|
|
86
|
+
const userTeams = meData.teams || [];
|
|
87
|
+
|
|
88
|
+
if (userTeams.length === 0) {
|
|
89
|
+
setErrorMsg("No teams found");
|
|
90
|
+
setStep("error");
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
setTeams(userTeams);
|
|
95
|
+
|
|
96
|
+
if (userTeams.length === 1) {
|
|
97
|
+
setSelectedTeamIndex(0);
|
|
98
|
+
setStep("loading-projects");
|
|
99
|
+
loadProjects(userTeams[0].team.id);
|
|
100
|
+
} else {
|
|
101
|
+
setStep("team");
|
|
102
|
+
}
|
|
103
|
+
} catch (err: any) {
|
|
104
|
+
setErrorMsg(err.message);
|
|
105
|
+
setStep("error");
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const loadProjects = async (teamId: string) => {
|
|
110
|
+
try {
|
|
111
|
+
const projectsRes = await api.get<any>(`/projects/team/${teamId}`);
|
|
112
|
+
const teamProjects = projectsRes.projects || [];
|
|
113
|
+
|
|
114
|
+
if (teamProjects.length === 0) {
|
|
115
|
+
setErrorMsg("No projects found in this team");
|
|
116
|
+
setStep("error");
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setProjects(teamProjects);
|
|
121
|
+
setSelectedProjectIndex(0);
|
|
122
|
+
setStep("project");
|
|
123
|
+
} catch (err: any) {
|
|
124
|
+
setErrorMsg(err.message);
|
|
125
|
+
setStep("error");
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const loadDeployments = async (project: any) => {
|
|
130
|
+
try {
|
|
131
|
+
setTargetProject(project);
|
|
132
|
+
const depsRes = await api.get<any>(`/deployments/${project.id}?limit=50`);
|
|
133
|
+
const allDeployments = depsRes.deployments || [];
|
|
134
|
+
|
|
135
|
+
setDeployments(allDeployments);
|
|
136
|
+
setStep("display");
|
|
137
|
+
} catch (err: any) {
|
|
138
|
+
setErrorMsg(err.message);
|
|
139
|
+
setStep("error");
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
if (
|
|
144
|
+
step === "loading" ||
|
|
145
|
+
step === "loading-projects" ||
|
|
146
|
+
step === "loading-deployments"
|
|
147
|
+
) {
|
|
148
|
+
return (
|
|
149
|
+
<Box flexDirection="column" padding={1}>
|
|
150
|
+
<Logo />
|
|
151
|
+
<Box marginTop={1}>
|
|
152
|
+
<Text bold>Deployments</Text>
|
|
153
|
+
</Box>
|
|
154
|
+
<Box marginTop={1}>
|
|
155
|
+
<Text dimColor>
|
|
156
|
+
Loading
|
|
157
|
+
{step === "loading-projects"
|
|
158
|
+
? " projects..."
|
|
159
|
+
: step === "loading-deployments"
|
|
160
|
+
? " deployments..."
|
|
161
|
+
: "..."}
|
|
162
|
+
</Text>
|
|
163
|
+
</Box>
|
|
164
|
+
<Box marginTop={1}>
|
|
165
|
+
<Text>
|
|
166
|
+
<Spinner />
|
|
167
|
+
</Text>
|
|
168
|
+
</Box>
|
|
169
|
+
</Box>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (step === "error") {
|
|
174
|
+
return (
|
|
175
|
+
<Box flexDirection="column">
|
|
176
|
+
<Logo />
|
|
177
|
+
<Box marginTop={1}>
|
|
178
|
+
<Text bold color="red">
|
|
179
|
+
✖ Error
|
|
180
|
+
</Text>
|
|
181
|
+
</Box>
|
|
182
|
+
<Box marginTop={1}>
|
|
183
|
+
<Text color="red">{errorMsg}</Text>
|
|
184
|
+
</Box>
|
|
185
|
+
</Box>
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (step === "team") {
|
|
190
|
+
return (
|
|
191
|
+
<Box flexDirection="column" padding={1}>
|
|
192
|
+
<Logo />
|
|
193
|
+
<Box marginTop={1}>
|
|
194
|
+
<Text bold>Deployments</Text>
|
|
195
|
+
</Box>
|
|
196
|
+
<Box marginTop={1}>
|
|
197
|
+
<Text dimColor>
|
|
198
|
+
Step 1/2: Select Team (↑↓ navigate, Enter select)
|
|
199
|
+
</Text>
|
|
200
|
+
</Box>
|
|
201
|
+
|
|
202
|
+
<Box marginTop={1} flexDirection="column">
|
|
203
|
+
{teams.map((t, idx) => (
|
|
204
|
+
<Box key={t.team.id} marginTop={1}>
|
|
205
|
+
<Text color={idx === selectedTeamIndex ? "cyan" : "gray"}>
|
|
206
|
+
{idx === selectedTeamIndex ? "▶ " : " "}
|
|
207
|
+
</Text>
|
|
208
|
+
<Text bold={idx === selectedTeamIndex}>{t.team.name}</Text>
|
|
209
|
+
</Box>
|
|
210
|
+
))}
|
|
211
|
+
</Box>
|
|
212
|
+
</Box>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (step === "project") {
|
|
217
|
+
return (
|
|
218
|
+
<Box flexDirection="column" padding={1}>
|
|
219
|
+
<Logo />
|
|
220
|
+
<Box marginTop={1}>
|
|
221
|
+
<Text bold>Deployments</Text>
|
|
222
|
+
</Box>
|
|
223
|
+
<Box marginTop={1}>
|
|
224
|
+
<Text dimColor>
|
|
225
|
+
Step 2/2: Select Project - {teams[selectedTeamIndex]?.team.name}
|
|
226
|
+
</Text>
|
|
227
|
+
</Box>
|
|
228
|
+
<Box>
|
|
229
|
+
<Text dimColor>(↑↓ navigate, Enter select, Esc go back)</Text>
|
|
230
|
+
</Box>
|
|
231
|
+
|
|
232
|
+
<Box marginTop={1} flexDirection="column">
|
|
233
|
+
{projects.map((p, idx) => (
|
|
234
|
+
<Box key={p.id} marginTop={1}>
|
|
235
|
+
<Text color={idx === selectedProjectIndex ? "cyan" : "gray"}>
|
|
236
|
+
{idx === selectedProjectIndex ? "▶ " : " "}
|
|
237
|
+
</Text>
|
|
238
|
+
<Text bold={idx === selectedProjectIndex}>{p.name}</Text>
|
|
239
|
+
</Box>
|
|
240
|
+
))}
|
|
241
|
+
</Box>
|
|
242
|
+
</Box>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const formatDate = (dateStr: string) => {
|
|
247
|
+
const date = new Date(dateStr);
|
|
248
|
+
return date.toLocaleDateString() + " " + date.toLocaleTimeString();
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const getStatusColor = (status: string) => {
|
|
252
|
+
switch (status.toLowerCase()) {
|
|
253
|
+
case "live":
|
|
254
|
+
case "deployed":
|
|
255
|
+
case "success":
|
|
256
|
+
return "green";
|
|
257
|
+
case "building":
|
|
258
|
+
case "queued":
|
|
259
|
+
case "provisioning":
|
|
260
|
+
return "cyan";
|
|
261
|
+
case "failed":
|
|
262
|
+
return "red";
|
|
263
|
+
default:
|
|
264
|
+
return "gray";
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<Box flexDirection="column" padding={1}>
|
|
270
|
+
<Logo />
|
|
271
|
+
|
|
272
|
+
<Box marginTop={1}>
|
|
273
|
+
<Text bold>Deployments</Text>
|
|
274
|
+
</Box>
|
|
275
|
+
<Box>
|
|
276
|
+
<Text dimColor>
|
|
277
|
+
{targetProject?.name} - {teams[selectedTeamIndex]?.team.name}
|
|
278
|
+
</Text>
|
|
279
|
+
</Box>
|
|
280
|
+
|
|
281
|
+
<Box marginTop={1}>
|
|
282
|
+
<Text dimColor>
|
|
283
|
+
{deployments.length === 0
|
|
284
|
+
? "No deployments found"
|
|
285
|
+
: `Found ${deployments.length} deployment(s)`}
|
|
286
|
+
</Text>
|
|
287
|
+
</Box>
|
|
288
|
+
|
|
289
|
+
<Box marginTop={1} flexDirection="column">
|
|
290
|
+
{deployments.map((dep, idx) => (
|
|
291
|
+
<Box
|
|
292
|
+
key={dep.id}
|
|
293
|
+
marginTop={1}
|
|
294
|
+
flexDirection="column"
|
|
295
|
+
borderStyle="round"
|
|
296
|
+
borderDimColor
|
|
297
|
+
paddingX={1}
|
|
298
|
+
>
|
|
299
|
+
<Box>
|
|
300
|
+
<Text bold color={getStatusColor(dep.status)}>
|
|
301
|
+
{dep.status.toUpperCase()}
|
|
302
|
+
</Text>
|
|
303
|
+
<Text dimColor> - {formatDate(dep.createdAt)}</Text>
|
|
304
|
+
</Box>
|
|
305
|
+
<Box>
|
|
306
|
+
<Text dimColor>ID: {dep.id}</Text>
|
|
307
|
+
</Box>
|
|
308
|
+
{dep.commitMessage && (
|
|
309
|
+
<Box>
|
|
310
|
+
<Text dimColor wrap="wrap">
|
|
311
|
+
{dep.commitMessage}
|
|
312
|
+
</Text>
|
|
313
|
+
</Box>
|
|
314
|
+
)}
|
|
315
|
+
{dep.runtimeStatus && (
|
|
316
|
+
<Box>
|
|
317
|
+
<Text dimColor>Runtime: {dep.runtimeStatus}</Text>
|
|
318
|
+
</Box>
|
|
319
|
+
)}
|
|
320
|
+
</Box>
|
|
321
|
+
))}
|
|
322
|
+
</Box>
|
|
323
|
+
|
|
324
|
+
<Box marginTop={2}>
|
|
325
|
+
<Text dimColor>Press Q or Ctrl+C to exit</Text>
|
|
326
|
+
</Box>
|
|
327
|
+
</Box>
|
|
328
|
+
);
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
export default Deployments;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import React, { useEffect } from "react";
|
|
2
|
+
|
|
3
|
+
const Help: React.FC = () => {
|
|
4
|
+
useEffect(() => {
|
|
5
|
+
console.log(`
|
|
6
|
+
\x1b[38;2;255;191;0m ⬡ ONFLYT \x1b[0m\x1b[1m Deploy CLI v0.1.0-beta\x1b[0m
|
|
7
|
+
|
|
8
|
+
\x1b[1mUSAGE\x1b[0m
|
|
9
|
+
$ onflyt <command> [options]
|
|
10
|
+
|
|
11
|
+
\x1b[1mCOMMANDS\x1b[0m
|
|
12
|
+
\x1b[36mlogin\x1b[0m Authenticate with GitHub OAuth
|
|
13
|
+
\x1b[36mlogout\x1b[0m Sign out
|
|
14
|
+
\x1b[36mwhoami\x1b[0m Show current user info
|
|
15
|
+
|
|
16
|
+
\x1b[33mPROJECT\x1b[0m
|
|
17
|
+
\x1b[36minit\x1b[0m Initialize new project (creates onflyt.json)
|
|
18
|
+
\x1b[36mdeploy\x1b[0m Deploy project (ZIP upload)
|
|
19
|
+
\x1b[36mprojects\x1b[0m List projects by team
|
|
20
|
+
\x1b[36mdelete\x1b[0m Delete a project
|
|
21
|
+
|
|
22
|
+
\x1b[33mDEPLOYMENTS\x1b[0m
|
|
23
|
+
\x1b[36mlogs\x1b[0m View deployment logs
|
|
24
|
+
\x1b[36mdeployments\x1b[0m List project deployments
|
|
25
|
+
\x1b[36mrollback\x1b[0m Rollback to previous deployment
|
|
26
|
+
|
|
27
|
+
\x1b[33mTEAM & BILLING\x1b[0m
|
|
28
|
+
\x1b[36mteams\x1b[0m List your teams
|
|
29
|
+
\x1b[36mcredits\x1b[0m Check credits balance
|
|
30
|
+
|
|
31
|
+
\x1b[1mGLOBAL OPTIONS\x1b[0m
|
|
32
|
+
\x1b[36m-h, --help\x1b[0m Show this help
|
|
33
|
+
\x1b[36m-v, --version\x1b[0m Show CLI version
|
|
34
|
+
\x1b[36m-t, --team <id>\x1b[0m Target specific team
|
|
35
|
+
\x1b[36m--no-open\x1b[0m Don't auto-open browser on login
|
|
36
|
+
|
|
37
|
+
\x1b[1mINIT OPTIONS\x1b[0m
|
|
38
|
+
\x1b[36m--name <name>\x1b[0m Project name
|
|
39
|
+
\x1b[36m--template <id>\x1b[0m Template (blank, nextjs, react-vite, etc.)
|
|
40
|
+
\x1b[36m--framework <fw>\x1b[0m Framework (nextjs, react, node, etc.)
|
|
41
|
+
\x1b[36m--package-manager <pm>\x1b[0m Package manager (npm, bun, yarn, pnpm)
|
|
42
|
+
\x1b[36m--git\x1b[0m Connect Git repository
|
|
43
|
+
\x1b[36m--no-git\x1b[0m Skip Git connection
|
|
44
|
+
\x1b[36m-y, --yes\x1b[0m Skip all prompts (use defaults)
|
|
45
|
+
|
|
46
|
+
\x1b[1mLOGS OPTIONS\x1b[0m
|
|
47
|
+
\x1b[36m-l, --live\x1b[0m Stream live logs (SSE)
|
|
48
|
+
|
|
49
|
+
\x1b[1mQUICK START\x1b[0m
|
|
50
|
+
$ onflyt login Authenticate
|
|
51
|
+
$ onflyt init Create onflyt.json
|
|
52
|
+
$ onflyt deploy Deploy project
|
|
53
|
+
|
|
54
|
+
\x1b[1mKEYBOARD SHORTCUTS\x1b[0m
|
|
55
|
+
\x1b[36m↑↓\x1b[0m Navigate selection
|
|
56
|
+
\x1b[36mEnter\x1b[0m Select / Confirm
|
|
57
|
+
\x1b[36mEsc\x1b[0m Go back / Cancel
|
|
58
|
+
\x1b[36mQ\x1b[0m Quit
|
|
59
|
+
\x1b[36mCtrl+C\x1b[0m Quit
|
|
60
|
+
|
|
61
|
+
\x1b[1mEXAMPLES\x1b[0m
|
|
62
|
+
$ onflyt deploy --team tm_xxx Deploy to specific team
|
|
63
|
+
$ onflyt logs dep_xxx --live Stream live logs
|
|
64
|
+
$ onflyt init --name myapp --yes Quick init with defaults
|
|
65
|
+
|
|
66
|
+
\x1b[1mDOCS\x1b[0m
|
|
67
|
+
https://docs.onflyt.com/cli
|
|
68
|
+
`);
|
|
69
|
+
}, []);
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export default Help;
|