alpic 0.0.0-dev.f15e096 → 0.0.0-dev.f1a6945
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/__tests__/auth.e2e.test.d.ts +1 -0
- package/dist/__tests__/auth.e2e.test.js +142 -0
- package/dist/__tests__/auth.e2e.test.js.map +1 -0
- package/dist/__tests__/deploy.e2e.test.js +39 -72
- package/dist/__tests__/deploy.e2e.test.js.map +1 -1
- package/dist/__tests__/git.e2e.test.d.ts +1 -0
- package/dist/__tests__/git.e2e.test.js +218 -0
- package/dist/__tests__/git.e2e.test.js.map +1 -0
- package/dist/__tests__/mock-server.js +142 -3
- package/dist/__tests__/mock-server.js.map +1 -1
- package/dist/__tests__/utils.d.ts +32 -0
- package/dist/__tests__/utils.js +108 -0
- package/dist/__tests__/utils.js.map +1 -1
- package/dist/api.d.ts +0 -1
- package/dist/api.js +5 -9
- package/dist/api.js.map +1 -1
- package/dist/commands/deploy.d.ts +2 -2
- package/dist/commands/deploy.js +6 -10
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/git/connect.d.ts +9 -0
- package/dist/commands/git/connect.js +59 -0
- package/dist/commands/git/connect.js.map +1 -0
- package/dist/commands/git/disconnect.d.ts +9 -0
- package/dist/commands/git/disconnect.js +51 -0
- package/dist/commands/git/disconnect.js.map +1 -0
- package/dist/commands/git.d.ts +6 -0
- package/dist/commands/git.js +17 -0
- package/dist/commands/git.js.map +1 -0
- package/dist/commands/login.d.ts +6 -0
- package/dist/commands/login.js +39 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +6 -0
- package/dist/commands/logout.js +20 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/whoami.d.ts +6 -0
- package/dist/commands/whoami.js +25 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/env.d.ts +4 -0
- package/dist/env.js +10 -0
- package/dist/env.js.map +1 -0
- package/dist/lib/alpic-command.d.ts +4 -0
- package/dist/lib/alpic-command.js +17 -0
- package/dist/lib/alpic-command.js.map +1 -0
- package/dist/lib/auth/auth.d.ts +2 -0
- package/dist/lib/auth/auth.js +21 -0
- package/dist/lib/auth/auth.js.map +1 -0
- package/dist/lib/auth/oauth/client.d.ts +28 -0
- package/dist/lib/auth/oauth/client.js +110 -0
- package/dist/lib/auth/oauth/client.js.map +1 -0
- package/dist/lib/auth/oauth/constants.d.ts +2 -0
- package/dist/lib/auth/oauth/constants.js +3 -0
- package/dist/lib/auth/oauth/constants.js.map +1 -0
- package/dist/lib/auth/oauth/server/assets/alpic-mountain.png +0 -0
- package/dist/lib/auth/oauth/server/assets/authorize.html +195 -0
- package/dist/lib/auth/oauth/server/assets/callback.html +88 -0
- package/dist/lib/auth/oauth/server/index.d.ts +8 -0
- package/dist/lib/auth/oauth/server/index.js +102 -0
- package/dist/lib/auth/oauth/server/index.js.map +1 -0
- package/dist/lib/auth/whoami.d.ts +28 -0
- package/dist/lib/auth/whoami.js +35 -0
- package/dist/lib/auth/whoami.js.map +1 -0
- package/dist/lib/deployment.d.ts +2 -3
- package/dist/lib/deployment.js +13 -8
- package/dist/lib/deployment.js.map +1 -1
- package/dist/lib/git.d.ts +14 -0
- package/dist/lib/git.js +105 -0
- package/dist/lib/git.js.map +1 -0
- package/dist/lib/global-store.d.ts +28 -0
- package/dist/lib/global-store.js +75 -0
- package/dist/lib/global-store.js.map +1 -0
- package/dist/lib/project.js +6 -2
- package/dist/lib/project.js.map +1 -1
- package/dist/lib/telemetry.js +6 -6
- package/dist/lib/telemetry.js.map +1 -1
- package/package.json +24 -17
- package/dist/lib/global-config.d.ts +0 -9
- package/dist/lib/global-config.js +0 -48
- package/dist/lib/global-config.js.map +0 -1
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Sign in to Alpic</title>
|
|
7
|
+
<link
|
|
8
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Mozilla+Text:wght@200..700&display=swap"
|
|
9
|
+
rel="stylesheet"
|
|
10
|
+
/>
|
|
11
|
+
<style>
|
|
12
|
+
*,
|
|
13
|
+
*::before,
|
|
14
|
+
*::after {
|
|
15
|
+
margin: 0;
|
|
16
|
+
padding: 0;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:root {
|
|
21
|
+
--midnight: #051413;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
font-family: "Inter", system-ui, sans-serif;
|
|
26
|
+
background-color: var(--midnight);
|
|
27
|
+
min-height: 100vh;
|
|
28
|
+
display: flex;
|
|
29
|
+
background-image: url("/assets/alpic-mountain.png");
|
|
30
|
+
background-repeat: no-repeat;
|
|
31
|
+
background-position: right bottom;
|
|
32
|
+
background-size: auto;
|
|
33
|
+
padding: 64px;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.card {
|
|
37
|
+
background: #fff;
|
|
38
|
+
border-radius: 2px;
|
|
39
|
+
padding: 48px;
|
|
40
|
+
width: 50%;
|
|
41
|
+
min-height: calc(100vh - 128px);
|
|
42
|
+
display: flex;
|
|
43
|
+
flex-direction: column;
|
|
44
|
+
align-items: center;
|
|
45
|
+
justify-content: center;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
h1 {
|
|
49
|
+
font-family: "Mozilla Text", system-ui, sans-serif;
|
|
50
|
+
font-weight: 400;
|
|
51
|
+
font-size: 48px;
|
|
52
|
+
color: var(--midnight);
|
|
53
|
+
margin-bottom: 8px;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.subtitle {
|
|
57
|
+
font-size: 20px;
|
|
58
|
+
color: var(--midnight);
|
|
59
|
+
margin-bottom: 32px;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.buttons {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
gap: 12px;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.btn {
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
justify-content: center;
|
|
72
|
+
gap: 8px;
|
|
73
|
+
height: 48px;
|
|
74
|
+
width: 256px;
|
|
75
|
+
padding: 0 16px;
|
|
76
|
+
border-radius: 2px;
|
|
77
|
+
border: 1px solid var(--midnight);
|
|
78
|
+
font-family: "Inter", system-ui, sans-serif;
|
|
79
|
+
font-size: 16px;
|
|
80
|
+
font-weight: 500;
|
|
81
|
+
cursor: pointer;
|
|
82
|
+
transition: background 0.15s;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.btn:focus-visible {
|
|
86
|
+
outline: none;
|
|
87
|
+
box-shadow: 0 0 0 2px var(--midnight);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.btn svg {
|
|
91
|
+
width: 20px;
|
|
92
|
+
height: 20px;
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.btn-github {
|
|
97
|
+
background: var(--midnight);
|
|
98
|
+
color: #fff;
|
|
99
|
+
}
|
|
100
|
+
.btn-github svg {
|
|
101
|
+
fill: #fff;
|
|
102
|
+
}
|
|
103
|
+
.btn-github:hover {
|
|
104
|
+
background: rgba(5, 20, 19, 0.9);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.btn-google {
|
|
108
|
+
background: #fff;
|
|
109
|
+
color: var(--midnight);
|
|
110
|
+
}
|
|
111
|
+
.btn-google svg {
|
|
112
|
+
fill: var(--midnight);
|
|
113
|
+
}
|
|
114
|
+
.btn-google:hover {
|
|
115
|
+
background: rgba(5, 20, 19, 0.1);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
.btn-loading {
|
|
119
|
+
opacity: 0.7;
|
|
120
|
+
pointer-events: none;
|
|
121
|
+
cursor: default;
|
|
122
|
+
transition: opacity 0.2s;
|
|
123
|
+
}
|
|
124
|
+
.spinner {
|
|
125
|
+
width: 20px;
|
|
126
|
+
height: 20px;
|
|
127
|
+
animation: spin 0.8s linear infinite;
|
|
128
|
+
}
|
|
129
|
+
@keyframes spin {
|
|
130
|
+
to {
|
|
131
|
+
transform: rotate(360deg);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@media (max-width: 768px) {
|
|
136
|
+
body {
|
|
137
|
+
padding: 16px;
|
|
138
|
+
}
|
|
139
|
+
.card {
|
|
140
|
+
width: 100%;
|
|
141
|
+
height: auto;
|
|
142
|
+
padding: 24px;
|
|
143
|
+
}
|
|
144
|
+
h1 {
|
|
145
|
+
font-size: 36px;
|
|
146
|
+
}
|
|
147
|
+
.subtitle {
|
|
148
|
+
font-size: 18px;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
</style>
|
|
152
|
+
</head>
|
|
153
|
+
<body>
|
|
154
|
+
<div class="card">
|
|
155
|
+
<h1>Welcome to Alpic</h1>
|
|
156
|
+
<p class="subtitle">Sign in to use Alpic CLI</p>
|
|
157
|
+
<div class="buttons">
|
|
158
|
+
<button class="btn btn-github" onclick="login('Github', this)" type="button">
|
|
159
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
160
|
+
<title>GitHub</title>
|
|
161
|
+
<path
|
|
162
|
+
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
|
163
|
+
/>
|
|
164
|
+
</svg>
|
|
165
|
+
Sign in with GitHub
|
|
166
|
+
</button>
|
|
167
|
+
<button class="btn btn-google" onclick="login('google', this)" type="button">
|
|
168
|
+
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
169
|
+
<title>Google</title>
|
|
170
|
+
<path
|
|
171
|
+
d="M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.8 4.133-1.147 1.147-2.933 2.4-6.04 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z"
|
|
172
|
+
/>
|
|
173
|
+
</svg>
|
|
174
|
+
Sign in with Google
|
|
175
|
+
</button>
|
|
176
|
+
</div>
|
|
177
|
+
</div>
|
|
178
|
+
<script>
|
|
179
|
+
var loaderSvg =
|
|
180
|
+
'<svg class="spinner" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2v4"/><path d="m16.2 7.8 2.9-2.9"/><path d="M18 12h4"/><path d="m16.2 16.2 2.9 2.9"/><path d="M12 18v4"/><path d="m4.9 19.1 2.9-2.9"/><path d="M2 12h4"/><path d="m4.9 4.9 2.9 2.9"/></svg>';
|
|
181
|
+
// biome-ignore lint/correctness/noUnusedVariables: The function is used within the buttons onclick handlers
|
|
182
|
+
function login(provider, btn) {
|
|
183
|
+
const icon = btn.querySelector("svg");
|
|
184
|
+
const wrap = document.createElement("div");
|
|
185
|
+
wrap.innerHTML = loaderSvg;
|
|
186
|
+
icon.replaceWith(wrap.firstChild);
|
|
187
|
+
btn.classList.add("btn-loading");
|
|
188
|
+
btn.disabled = true;
|
|
189
|
+
const url = new URL("%AUTH_BASE_URL%");
|
|
190
|
+
url.searchParams.set("identity_provider", provider);
|
|
191
|
+
window.location.href = url.toString();
|
|
192
|
+
}
|
|
193
|
+
</script>
|
|
194
|
+
</body>
|
|
195
|
+
</html>
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Sign in to Alpic</title>
|
|
7
|
+
<link
|
|
8
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Mozilla+Text:wght@200..700&display=swap"
|
|
9
|
+
rel="stylesheet"
|
|
10
|
+
/>
|
|
11
|
+
<style>
|
|
12
|
+
*,
|
|
13
|
+
*::before,
|
|
14
|
+
*::after {
|
|
15
|
+
margin: 0;
|
|
16
|
+
padding: 0;
|
|
17
|
+
box-sizing: border-box;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
:root {
|
|
21
|
+
--midnight: #051413;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
body {
|
|
25
|
+
font-family: "Inter", system-ui, sans-serif;
|
|
26
|
+
background-color: var(--midnight);
|
|
27
|
+
min-height: 100vh;
|
|
28
|
+
display: flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
justify-content: center;
|
|
31
|
+
padding: 64px;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.card {
|
|
35
|
+
background: #fff;
|
|
36
|
+
border-radius: 2px;
|
|
37
|
+
padding: 48px;
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-items: center;
|
|
41
|
+
justify-content: center;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.message {
|
|
45
|
+
text-align: center;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.message-title {
|
|
49
|
+
font-size: 28px;
|
|
50
|
+
color: var(--midnight);
|
|
51
|
+
display: block;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.message-sub {
|
|
55
|
+
display: block;
|
|
56
|
+
margin-top: 0.5em;
|
|
57
|
+
color: #6b7280;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.message.error .message-title {
|
|
61
|
+
color: #b91c1c;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.message.error .message-sub {
|
|
65
|
+
display: none;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@media (max-width: 768px) {
|
|
69
|
+
body {
|
|
70
|
+
padding: 16px;
|
|
71
|
+
}
|
|
72
|
+
.card {
|
|
73
|
+
width: 100%;
|
|
74
|
+
height: auto;
|
|
75
|
+
padding: 24px;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
</style>
|
|
79
|
+
</head>
|
|
80
|
+
<body>
|
|
81
|
+
<div class="card">
|
|
82
|
+
<p class="message __CALLBACK_STATUS__">
|
|
83
|
+
<span class="message-title">__CALLBACK_TITLE__</span>
|
|
84
|
+
<span class="message-sub">__CALLBACK_SUB__</span>
|
|
85
|
+
</p>
|
|
86
|
+
</div>
|
|
87
|
+
</body>
|
|
88
|
+
</html>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Credentials } from "../../../global-store.js";
|
|
2
|
+
export declare const getLoginPageUrl: () => string;
|
|
3
|
+
export declare const listenToOAuthCallback: ({ state, nonce, codeVerifier, authorizeUrl, }: {
|
|
4
|
+
state: string;
|
|
5
|
+
nonce: string;
|
|
6
|
+
codeVerifier: string;
|
|
7
|
+
authorizeUrl?: string;
|
|
8
|
+
}) => Promise<Credentials>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { oAuthClient } from "../client.js";
|
|
7
|
+
import { LOOPBACK_HOST, LOOPBACK_PORT } from "../constants.js";
|
|
8
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const ASSETS_DIR = path.join(__dirname, "assets");
|
|
10
|
+
const createCallbackServer = ({ state, nonce, codeVerifier, authorizeUrl, onSuccess, onError, }) => createServer(async (req, res) => {
|
|
11
|
+
const url = req.url ?? "/";
|
|
12
|
+
const pathname = url.split("?")[0] ?? "/";
|
|
13
|
+
if (pathname === "/login") {
|
|
14
|
+
try {
|
|
15
|
+
const htmlPath = path.join(ASSETS_DIR, "authorize.html");
|
|
16
|
+
let html = await readFile(htmlPath, "utf-8");
|
|
17
|
+
if (authorizeUrl) {
|
|
18
|
+
html = html.replace(/%AUTH_BASE_URL%/g, authorizeUrl);
|
|
19
|
+
}
|
|
20
|
+
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
21
|
+
res.end(html);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
p.log.error(e instanceof Error ? e.message : String(e));
|
|
26
|
+
res.writeHead(500).end();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (pathname === "/assets/alpic-mountain.png") {
|
|
31
|
+
const filePath = path.join(ASSETS_DIR, "alpic-mountain.png");
|
|
32
|
+
const content = await readFile(filePath);
|
|
33
|
+
res.writeHead(200, { "Content-Type": "image/png", Connection: "close" });
|
|
34
|
+
res.end(content);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!url.startsWith("/callback")) {
|
|
38
|
+
res.writeHead(404).end();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const tokens = await oAuthClient.exchangeAuthorizationCode({
|
|
43
|
+
url: new URL(url, `http://${req.headers.host}`),
|
|
44
|
+
codeVerifier,
|
|
45
|
+
state,
|
|
46
|
+
nonce,
|
|
47
|
+
});
|
|
48
|
+
const sub = tokens.claims()?.sub;
|
|
49
|
+
if (!sub || !tokens.refresh_token) {
|
|
50
|
+
throw new Error("ID token did not contain sub");
|
|
51
|
+
}
|
|
52
|
+
const stored = {
|
|
53
|
+
access_token: tokens.access_token,
|
|
54
|
+
refresh_token: tokens.refresh_token,
|
|
55
|
+
expires_at: oAuthClient.getExpiresAt(tokens.expires_in ?? 0),
|
|
56
|
+
sub,
|
|
57
|
+
};
|
|
58
|
+
p.log.success("✅ Authentication successful");
|
|
59
|
+
const successHtmlPath = path.join(ASSETS_DIR, "callback.html");
|
|
60
|
+
let html = await readFile(successHtmlPath, "utf-8");
|
|
61
|
+
html = html
|
|
62
|
+
.replace(/__CALLBACK_STATUS__/g, "success")
|
|
63
|
+
.replace(/__CALLBACK_TITLE__/g, "You are logged in")
|
|
64
|
+
.replace(/__CALLBACK_SUB__/g, "You can close this page.");
|
|
65
|
+
res.writeHead(200, { "Content-Type": "text/html", Connection: "close" });
|
|
66
|
+
res.end(html);
|
|
67
|
+
onSuccess(stored);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
p.log.error(e instanceof Error ? e.message : String(e));
|
|
71
|
+
const callbackHtmlPath = path.join(ASSETS_DIR, "callback.html");
|
|
72
|
+
let html = await readFile(callbackHtmlPath, "utf-8");
|
|
73
|
+
html = html
|
|
74
|
+
.replace(/__CALLBACK_STATUS__/g, "error")
|
|
75
|
+
.replace(/__CALLBACK_TITLE__/g, "An error occurred. You can close this page.")
|
|
76
|
+
.replace(/__CALLBACK_SUB__/g, "");
|
|
77
|
+
res.writeHead(500, { "Content-Type": "text/html", Connection: "close" });
|
|
78
|
+
res.end(html);
|
|
79
|
+
onError(e instanceof Error ? e : new Error(String(e)));
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
export const getLoginPageUrl = () => `http://${LOOPBACK_HOST}:${LOOPBACK_PORT}/login`;
|
|
83
|
+
export const listenToOAuthCallback = ({ state, nonce, codeVerifier, authorizeUrl, }) => {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const callbackServer = createCallbackServer({
|
|
86
|
+
state,
|
|
87
|
+
nonce,
|
|
88
|
+
codeVerifier,
|
|
89
|
+
authorizeUrl,
|
|
90
|
+
onSuccess: (storedToken) => {
|
|
91
|
+
callbackServer.close();
|
|
92
|
+
resolve(storedToken);
|
|
93
|
+
},
|
|
94
|
+
onError: (err) => {
|
|
95
|
+
callbackServer.close();
|
|
96
|
+
reject(err);
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
callbackServer.listen(LOOPBACK_PORT, LOOPBACK_HOST);
|
|
100
|
+
});
|
|
101
|
+
};
|
|
102
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../src/lib/auth/oauth/server/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export type WhoamiInfo = {
|
|
2
|
+
method: "api_key";
|
|
3
|
+
team: {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
} | {
|
|
8
|
+
method: "oauth";
|
|
9
|
+
email: string;
|
|
10
|
+
name: string;
|
|
11
|
+
} | null;
|
|
12
|
+
export declare function getWhoamiInfo(): Promise<{
|
|
13
|
+
method: string;
|
|
14
|
+
team: {
|
|
15
|
+
id: string;
|
|
16
|
+
name: string;
|
|
17
|
+
createdAt: Date;
|
|
18
|
+
hasStripeAccount: boolean;
|
|
19
|
+
hasActiveSubscription: boolean;
|
|
20
|
+
} | undefined;
|
|
21
|
+
email?: undefined;
|
|
22
|
+
name?: undefined;
|
|
23
|
+
} | {
|
|
24
|
+
method: string;
|
|
25
|
+
email: string | undefined;
|
|
26
|
+
name: string | undefined;
|
|
27
|
+
team?: undefined;
|
|
28
|
+
} | null>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { api } from "../../api.js";
|
|
2
|
+
import { oAuthClient } from "./oauth/client.js";
|
|
3
|
+
export async function getWhoamiInfo() {
|
|
4
|
+
if (process.env.ALPIC_API_KEY !== undefined) {
|
|
5
|
+
const team = await getApiKeyTeam();
|
|
6
|
+
return {
|
|
7
|
+
method: "api_key",
|
|
8
|
+
team,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const token = await oAuthClient.getValidAccessToken();
|
|
12
|
+
if (!token)
|
|
13
|
+
return null;
|
|
14
|
+
const userInfo = await fetchOAuthUserInfo(token);
|
|
15
|
+
if (!userInfo)
|
|
16
|
+
return null;
|
|
17
|
+
return {
|
|
18
|
+
method: "oauth",
|
|
19
|
+
email: userInfo.email,
|
|
20
|
+
name: userInfo.name,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
async function fetchOAuthUserInfo(credentials) {
|
|
24
|
+
try {
|
|
25
|
+
return await oAuthClient.fetchUserInfo(credentials);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async function getApiKeyTeam() {
|
|
32
|
+
const teams = await api.teams.list.v1();
|
|
33
|
+
return teams[0];
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=whoami.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"whoami.js","sourceRoot":"","sources":["../../../src/lib/auth/whoami.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,cAAc,CAAC;AAEnC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAOhD,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;QAC5C,MAAM,IAAI,GAAG,MAAM,aAAa,EAAE,CAAC;QACnC,OAAO;YACL,MAAM,EAAE,SAAS;YACjB,IAAI;SACL,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,mBAAmB,EAAE,CAAC;IACtD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACjD,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE3B,OAAO;QACL,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,QAAQ,CAAC,KAAK;QACrB,IAAI,EAAE,QAAQ,CAAC,IAAI;KACpB,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,WAAwB;IACxD,IAAI,CAAC;QACH,OAAO,MAAM,WAAW,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,KAAK,UAAU,aAAa;IAC1B,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;IACxC,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
package/dist/lib/deployment.d.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import type { RouterOutput } from "@alpic-ai/api";
|
|
2
2
|
export declare function formatElapsed(ms: number): string;
|
|
3
|
-
export declare function deployAndWait({ initial, startedAt,
|
|
3
|
+
export declare function deployAndWait({ initial, startedAt, }: {
|
|
4
4
|
initial: RouterOutput["deployments"]["get"]["v1"];
|
|
5
5
|
startedAt: number;
|
|
6
|
-
teamId: string;
|
|
7
|
-
projectId: string;
|
|
8
6
|
}): Promise<{
|
|
9
7
|
deployment: {
|
|
10
8
|
id: string;
|
|
@@ -16,6 +14,7 @@ export declare function deployAndWait({ initial, startedAt, teamId, projectId, }
|
|
|
16
14
|
authorAvatarUrl: string | null;
|
|
17
15
|
startedAt: Date | null;
|
|
18
16
|
completedAt: Date | null;
|
|
17
|
+
deploymentPageUrl: string | null;
|
|
19
18
|
};
|
|
20
19
|
elapsedMs: number;
|
|
21
20
|
}>;
|
package/dist/lib/deployment.js
CHANGED
|
@@ -1,31 +1,36 @@
|
|
|
1
1
|
import * as p from "@clack/prompts";
|
|
2
2
|
import chalk from "chalk";
|
|
3
|
-
import { api
|
|
3
|
+
import { api } from "../api.js";
|
|
4
|
+
import { env } from "../env.js";
|
|
4
5
|
export function formatElapsed(ms) {
|
|
5
6
|
const totalSeconds = Math.floor(ms / 1000);
|
|
6
7
|
const minutes = Math.floor(totalSeconds / 60);
|
|
7
8
|
const seconds = totalSeconds % 60;
|
|
8
9
|
return minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`;
|
|
9
10
|
}
|
|
10
|
-
export async function deployAndWait({ initial, startedAt,
|
|
11
|
+
export async function deployAndWait({ initial, startedAt, }) {
|
|
11
12
|
const spinner = p.spinner();
|
|
13
|
+
let deployment = initial;
|
|
14
|
+
const deploymentPageUrl = deployment.deploymentPageUrl;
|
|
15
|
+
if (deploymentPageUrl) {
|
|
16
|
+
p.note(`🔗 ${deploymentPageUrl}`, "View deployment details:", { format: (line) => line });
|
|
17
|
+
}
|
|
12
18
|
spinner.start("Deployment in progress");
|
|
13
|
-
const
|
|
14
|
-
p.note(deploymentPageUrl, "View deployment details:");
|
|
19
|
+
const FIFTEEN_MINUTES_IN_MS = 15 * 60 * 1000; // 15 minutes
|
|
15
20
|
const elapsedInterval = setInterval(() => {
|
|
16
21
|
const elapsed = formatElapsed(Date.now() - startedAt);
|
|
17
22
|
spinner.message(`Deployment in progress — ${elapsed}`);
|
|
18
23
|
}, 1000);
|
|
19
|
-
const timeoutMs = 15 * 60 * 1000; // 15 minutes
|
|
20
|
-
let deployment = await api.deployments.get.v1({ deploymentId: initial.id });
|
|
21
24
|
try {
|
|
22
25
|
while (deployment.status === "ongoing") {
|
|
23
26
|
const elapsedMs = Date.now() - startedAt;
|
|
24
|
-
if (elapsedMs >=
|
|
27
|
+
if (elapsedMs >= FIFTEEN_MINUTES_IN_MS) {
|
|
25
28
|
throw new Error(`Deployment aborted after 15 minutes. View status: ${deploymentPageUrl}`);
|
|
26
29
|
}
|
|
27
30
|
deployment = await api.deployments.get.v1({ deploymentId: deployment.id });
|
|
28
|
-
|
|
31
|
+
if (deployment.status === "ongoing") {
|
|
32
|
+
await new Promise((resolve) => setTimeout(resolve, 10_000));
|
|
33
|
+
}
|
|
29
34
|
}
|
|
30
35
|
return { deployment, elapsedMs: Date.now() - startedAt };
|
|
31
36
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../src/lib/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAE,GAAG,EAAE,
|
|
1
|
+
{"version":3,"file":"deployment.js","sourceRoot":"","sources":["../../src/lib/deployment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,gBAAgB,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAChC,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAEhC,MAAM,UAAU,aAAa,CAAC,EAAU;IACtC,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,EAAE,CAAC,CAAC;IAC9C,MAAM,OAAO,GAAG,YAAY,GAAG,EAAE,CAAC;IAClC,OAAO,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,KAAK,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,CAAC;AACjE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,EAClC,OAAO,EACP,SAAS,GAIV;IACC,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAE5B,IAAI,UAAU,GAAG,OAAO,CAAC;IACzB,MAAM,iBAAiB,GAAG,UAAU,CAAC,iBAAiB,CAAC;IACvD,IAAI,iBAAiB,EAAE,CAAC;QACtB,CAAC,CAAC,IAAI,CAAC,MAAM,iBAAiB,EAAE,EAAE,0BAA0B,EAAE,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5F,CAAC;IAED,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;IACxC,MAAM,qBAAqB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,aAAa;IAC3D,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;QACvC,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,CAAC;QACtD,OAAO,CAAC,OAAO,CAAC,4BAA4B,OAAO,EAAE,CAAC,CAAC;IACzD,CAAC,EAAE,IAAI,CAAC,CAAC;IACT,IAAI,CAAC;QACH,OAAO,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACvC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACzC,IAAI,SAAS,IAAI,qBAAqB,EAAE,CAAC;gBACvC,MAAM,IAAI,KAAK,CAAC,qDAAqD,iBAAiB,EAAE,CAAC,CAAC;YAC5F,CAAC;YACD,UAAU,GAAG,MAAM,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,YAAY,EAAE,UAAU,CAAC,EAAE,EAAE,CAAC,CAAC;YAC3E,IAAI,UAAU,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;gBACpC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;YAC9D,CAAC;QACH,CAAC;QACD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,EAAE,CAAC;IAC3D,CAAC;YAAS,CAAC;QACT,MAAM,OAAO,GACX,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC1G,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,aAAa,CAAC,eAAe,CAAC,CAAC;IACjC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type GitRemote = {
|
|
2
|
+
name: string;
|
|
3
|
+
url: string;
|
|
4
|
+
sourceRepository: string;
|
|
5
|
+
};
|
|
6
|
+
export declare function isGitRepository(cwd: string): boolean;
|
|
7
|
+
export declare function listGithubRemotes(cwd: string): GitRemote[];
|
|
8
|
+
export type ProjectWithSourceRepository = {
|
|
9
|
+
name: string;
|
|
10
|
+
sourceRepository: string | null;
|
|
11
|
+
};
|
|
12
|
+
export declare function confirmLinkAnotherIfAlreadyConnected(project: ProjectWithSourceRepository): Promise<boolean>;
|
|
13
|
+
export declare function selectRemoteSourceRepository(remotes: GitRemote[]): Promise<string | null>;
|
|
14
|
+
export declare function showGitSetupInstructions(): void;
|
package/dist/lib/git.js
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { execFileSync } from "node:child_process";
|
|
4
|
+
function runGit(args, cwd) {
|
|
5
|
+
return execFileSync("git", args, {
|
|
6
|
+
cwd,
|
|
7
|
+
encoding: "utf8",
|
|
8
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
9
|
+
}).trim();
|
|
10
|
+
}
|
|
11
|
+
export function isGitRepository(cwd) {
|
|
12
|
+
try {
|
|
13
|
+
return runGit(["rev-parse", "--is-inside-work-tree"], cwd) === "true";
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function listGithubRemotes(cwd) {
|
|
20
|
+
let output;
|
|
21
|
+
try {
|
|
22
|
+
output = runGit(["remote", "-v"], cwd);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
const remotes = new Map();
|
|
28
|
+
for (const line of output.split(/\r?\n/)) {
|
|
29
|
+
const match = line.match(/^(\S+)\s+(\S+)\s+\((fetch|push)\)$/);
|
|
30
|
+
if (!match)
|
|
31
|
+
continue;
|
|
32
|
+
const name = match[1];
|
|
33
|
+
const url = match[2];
|
|
34
|
+
const kind = match[3];
|
|
35
|
+
if (!name || !url || !kind)
|
|
36
|
+
continue;
|
|
37
|
+
if (kind !== "fetch")
|
|
38
|
+
continue;
|
|
39
|
+
const key = `${name}:${url}`;
|
|
40
|
+
const sourceRepository = parseGitHubSourceRepository(url);
|
|
41
|
+
if (sourceRepository) {
|
|
42
|
+
remotes.set(key, {
|
|
43
|
+
name,
|
|
44
|
+
url,
|
|
45
|
+
sourceRepository,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return [...remotes.values()];
|
|
50
|
+
}
|
|
51
|
+
export async function confirmLinkAnotherIfAlreadyConnected(project) {
|
|
52
|
+
if (!project.sourceRepository) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
p.log.info(`Project "${project.name}" is already connected to "${project.sourceRepository}".`);
|
|
56
|
+
const confirm = await p.confirm({
|
|
57
|
+
message: chalk.bold("Do you want to link another repository?"),
|
|
58
|
+
initialValue: false,
|
|
59
|
+
});
|
|
60
|
+
if (p.isCancel(confirm) || !confirm) {
|
|
61
|
+
p.cancel("Git connect cancelled");
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
export async function selectRemoteSourceRepository(remotes) {
|
|
67
|
+
if (remotes.length === 0) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
const selected = await p.select({
|
|
71
|
+
message: chalk.bold("Choose the remote source to connect"),
|
|
72
|
+
options: remotes.map((remote) => ({
|
|
73
|
+
value: remote.sourceRepository,
|
|
74
|
+
label: `${remote.sourceRepository} (${remote.name})`,
|
|
75
|
+
hint: remote.url,
|
|
76
|
+
})),
|
|
77
|
+
});
|
|
78
|
+
if (p.isCancel(selected)) {
|
|
79
|
+
p.cancel("Git connect cancelled");
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
return selected;
|
|
83
|
+
}
|
|
84
|
+
export function showGitSetupInstructions() {
|
|
85
|
+
p.note([
|
|
86
|
+
`Run ${chalk.cyan("git init")} in this directory`,
|
|
87
|
+
`Add a remote: ${chalk.cyan("git remote add origin <your-repo-url>")}`,
|
|
88
|
+
`Then run ${chalk.cyan("alpic git connect")} again`,
|
|
89
|
+
].join("\n"), "To set up a git repository:", { format: (line) => line });
|
|
90
|
+
}
|
|
91
|
+
function parseGitHubSourceRepository(url) {
|
|
92
|
+
const patterns = [
|
|
93
|
+
/^git@github\.com:([^/\s]+\/[^/\s]+?)(?:\.git)?$/,
|
|
94
|
+
/^https:\/\/github\.com\/([^/\s]+\/[^/\s]+?)(?:\.git)?\/?$/,
|
|
95
|
+
/^ssh:\/\/git@github\.com\/([^/\s]+\/[^/\s]+?)(?:\.git)?\/?$/,
|
|
96
|
+
];
|
|
97
|
+
for (const pattern of patterns) {
|
|
98
|
+
const match = url.match(pattern);
|
|
99
|
+
if (match) {
|
|
100
|
+
return match[1] ?? null;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=git.js.map
|