@base44-preview/cli 0.0.1-pr.10.0e28f2b → 0.0.1-pr.10.1d6afbb
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/index.js +47 -140
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -108,6 +108,51 @@ const authClient = ky.create({
|
|
|
108
108
|
});
|
|
109
109
|
var authClient_default = authClient;
|
|
110
110
|
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/core/auth/api.ts
|
|
113
|
+
async function generateDeviceCode() {
|
|
114
|
+
const response = await authClient_default.post("oauth/device/code", {
|
|
115
|
+
json: {
|
|
116
|
+
client_id: AUTH_CLIENT_ID,
|
|
117
|
+
scope: "apps:read apps:write"
|
|
118
|
+
},
|
|
119
|
+
throwHttpErrors: false
|
|
120
|
+
});
|
|
121
|
+
if (!response.ok) throw new AuthApiError(`Failed to generate device code: ${response.status} ${response.statusText}`);
|
|
122
|
+
const result = DeviceCodeResponseSchema.safeParse(await response.json());
|
|
123
|
+
if (!result.success) throw new AuthValidationError(`Invalid device code response from server: ${result.error.message}`);
|
|
124
|
+
return result.data;
|
|
125
|
+
}
|
|
126
|
+
async function getTokenFromDeviceCode(deviceCode) {
|
|
127
|
+
const searchParams = new URLSearchParams();
|
|
128
|
+
searchParams.set("grant_type", "urn:ietf:params:oauth:grant-type:device_code");
|
|
129
|
+
searchParams.set("device_code", deviceCode);
|
|
130
|
+
searchParams.set("client_id", AUTH_CLIENT_ID);
|
|
131
|
+
const response = await authClient_default.post("oauth/token", {
|
|
132
|
+
body: searchParams.toString(),
|
|
133
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
134
|
+
throwHttpErrors: false
|
|
135
|
+
});
|
|
136
|
+
const json = await response.json();
|
|
137
|
+
if (!response.ok) {
|
|
138
|
+
const errorResult = OAuthErrorSchema.safeParse(json);
|
|
139
|
+
if (!errorResult.success) throw new AuthValidationError(`Token request failed: ${errorResult.error.message}`);
|
|
140
|
+
const { error, error_description } = errorResult.data;
|
|
141
|
+
if (error === "authorization_pending" || error === "slow_down") return null;
|
|
142
|
+
throw new AuthApiError(error_description ?? `OAuth error: ${error}`);
|
|
143
|
+
}
|
|
144
|
+
const result = TokenResponseSchema.safeParse(json);
|
|
145
|
+
if (!result.success) throw new AuthValidationError(`Invalid token response from server: ${result.error.message}`);
|
|
146
|
+
return result.data;
|
|
147
|
+
}
|
|
148
|
+
async function getUserInfo(accessToken) {
|
|
149
|
+
const response = await authClient_default.get("oauth/userinfo", { headers: { Authorization: `Bearer ${accessToken}` } });
|
|
150
|
+
if (!response.ok) throw new AuthApiError(`Failed to fetch user info: ${response.status}`);
|
|
151
|
+
const result = UserInfoSchema.safeParse(await response.json());
|
|
152
|
+
if (!result.success) throw new AuthValidationError(`Invalid UserInfo response from server: ${result.error.message}`);
|
|
153
|
+
return result.data;
|
|
154
|
+
}
|
|
155
|
+
|
|
111
156
|
//#endregion
|
|
112
157
|
//#region src/core/utils/fs.ts
|
|
113
158
|
function pathExists(path) {
|
|
@@ -150,7 +195,6 @@ async function deleteFile(filePath) {
|
|
|
150
195
|
//#endregion
|
|
151
196
|
//#region src/core/auth/config.ts
|
|
152
197
|
const TOKEN_REFRESH_BUFFER_MS = 60 * 1e3;
|
|
153
|
-
let refreshPromise = null;
|
|
154
198
|
async function readAuth() {
|
|
155
199
|
try {
|
|
156
200
|
const parsed = await readJsonFile(getAuthFilePath());
|
|
@@ -179,143 +223,6 @@ async function deleteAuth() {
|
|
|
179
223
|
throw new Error(`Failed to delete authentication file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
180
224
|
}
|
|
181
225
|
}
|
|
182
|
-
/**
|
|
183
|
-
* Checks if the access token is expired or about to expire.
|
|
184
|
-
*/
|
|
185
|
-
function isTokenExpired(auth) {
|
|
186
|
-
return Date.now() >= auth.expiresAt - TOKEN_REFRESH_BUFFER_MS;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Refreshes the access token and saves the new tokens.
|
|
190
|
-
* Returns the new access token, or null if refresh failed.
|
|
191
|
-
* Uses a lock to prevent concurrent refresh requests.
|
|
192
|
-
*/
|
|
193
|
-
async function refreshAndSaveTokens() {
|
|
194
|
-
if (refreshPromise) return refreshPromise;
|
|
195
|
-
refreshPromise = (async () => {
|
|
196
|
-
try {
|
|
197
|
-
const auth = await readAuth();
|
|
198
|
-
const tokenResponse = await renewAccessToken(auth.refreshToken);
|
|
199
|
-
await writeAuth({
|
|
200
|
-
...auth,
|
|
201
|
-
accessToken: tokenResponse.accessToken,
|
|
202
|
-
refreshToken: tokenResponse.refreshToken,
|
|
203
|
-
expiresAt: Date.now() + tokenResponse.expiresIn * 1e3
|
|
204
|
-
});
|
|
205
|
-
return tokenResponse.accessToken;
|
|
206
|
-
} catch {
|
|
207
|
-
await deleteAuth();
|
|
208
|
-
return null;
|
|
209
|
-
} finally {
|
|
210
|
-
refreshPromise = null;
|
|
211
|
-
}
|
|
212
|
-
})();
|
|
213
|
-
return refreshPromise;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
//#endregion
|
|
217
|
-
//#region src/core/utils/httpClient.ts
|
|
218
|
-
const retriedRequests = /* @__PURE__ */ new WeakSet();
|
|
219
|
-
/**
|
|
220
|
-
* Handles 401 responses by refreshing the token and retrying the request.
|
|
221
|
-
* Only retries once per request to prevent infinite loops.
|
|
222
|
-
*/
|
|
223
|
-
async function handleUnauthorized(request, _options, response) {
|
|
224
|
-
if (response.status !== 401) return;
|
|
225
|
-
if (retriedRequests.has(request)) return;
|
|
226
|
-
const newAccessToken = await refreshAndSaveTokens();
|
|
227
|
-
if (!newAccessToken) return;
|
|
228
|
-
retriedRequests.add(request);
|
|
229
|
-
request.headers.set("Authorization", `Bearer ${newAccessToken}`);
|
|
230
|
-
return ky(request);
|
|
231
|
-
}
|
|
232
|
-
const httpClient = ky.create({
|
|
233
|
-
prefixUrl: getBase44ApiUrl(),
|
|
234
|
-
headers: { "User-Agent": "Base44 CLI" },
|
|
235
|
-
hooks: {
|
|
236
|
-
beforeRequest: [async (request) => {
|
|
237
|
-
try {
|
|
238
|
-
const auth = await readAuth();
|
|
239
|
-
if (isTokenExpired(auth)) {
|
|
240
|
-
const newAccessToken = await refreshAndSaveTokens();
|
|
241
|
-
if (newAccessToken) {
|
|
242
|
-
request.headers.set("Authorization", `Bearer ${newAccessToken}`);
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
request.headers.set("Authorization", `Bearer ${auth.accessToken}`);
|
|
247
|
-
} catch {}
|
|
248
|
-
}],
|
|
249
|
-
afterResponse: [handleUnauthorized]
|
|
250
|
-
}
|
|
251
|
-
});
|
|
252
|
-
var httpClient_default = httpClient;
|
|
253
|
-
|
|
254
|
-
//#endregion
|
|
255
|
-
//#region src/core/auth/api.ts
|
|
256
|
-
async function generateDeviceCode() {
|
|
257
|
-
const response = await authClient_default.post("oauth/device/code", {
|
|
258
|
-
json: {
|
|
259
|
-
client_id: AUTH_CLIENT_ID,
|
|
260
|
-
scope: "apps:read apps:write"
|
|
261
|
-
},
|
|
262
|
-
throwHttpErrors: false
|
|
263
|
-
});
|
|
264
|
-
if (!response.ok) throw new AuthApiError(`Failed to generate device code: ${response.status} ${response.statusText}`);
|
|
265
|
-
const result = DeviceCodeResponseSchema.safeParse(await response.json());
|
|
266
|
-
if (!result.success) throw new AuthValidationError(`Invalid device code response from server: ${result.error.message}`);
|
|
267
|
-
return result.data;
|
|
268
|
-
}
|
|
269
|
-
async function getTokenFromDeviceCode(deviceCode) {
|
|
270
|
-
const searchParams = new URLSearchParams();
|
|
271
|
-
searchParams.set("grant_type", "urn:ietf:params:oauth:grant-type:device_code");
|
|
272
|
-
searchParams.set("device_code", deviceCode);
|
|
273
|
-
searchParams.set("client_id", AUTH_CLIENT_ID);
|
|
274
|
-
const response = await authClient_default.post("oauth/token", {
|
|
275
|
-
body: searchParams.toString(),
|
|
276
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
277
|
-
throwHttpErrors: false
|
|
278
|
-
});
|
|
279
|
-
const json = await response.json();
|
|
280
|
-
if (!response.ok) {
|
|
281
|
-
const errorResult = OAuthErrorSchema.safeParse(json);
|
|
282
|
-
if (!errorResult.success) throw new AuthValidationError(`Token request failed: ${errorResult.error.message}`);
|
|
283
|
-
const { error, error_description } = errorResult.data;
|
|
284
|
-
if (error === "authorization_pending" || error === "slow_down") return null;
|
|
285
|
-
throw new AuthApiError(error_description ?? `OAuth error: ${error}`);
|
|
286
|
-
}
|
|
287
|
-
const result = TokenResponseSchema.safeParse(json);
|
|
288
|
-
if (!result.success) throw new AuthValidationError(`Invalid token response from server: ${result.error.message}`);
|
|
289
|
-
return result.data;
|
|
290
|
-
}
|
|
291
|
-
async function renewAccessToken(refreshToken) {
|
|
292
|
-
const searchParams = new URLSearchParams();
|
|
293
|
-
searchParams.set("grant_type", "refresh_token");
|
|
294
|
-
searchParams.set("refresh_token", refreshToken);
|
|
295
|
-
searchParams.set("client_id", AUTH_CLIENT_ID);
|
|
296
|
-
const response = await authClient_default.post("oauth/token", {
|
|
297
|
-
body: searchParams.toString(),
|
|
298
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
299
|
-
throwHttpErrors: false
|
|
300
|
-
});
|
|
301
|
-
const json = await response.json();
|
|
302
|
-
if (!response.ok) {
|
|
303
|
-
const errorResult = OAuthErrorSchema.safeParse(json);
|
|
304
|
-
if (!errorResult.success) throw new AuthApiError(`Token refresh failed: ${response.statusText}`);
|
|
305
|
-
const { error, error_description } = errorResult.data;
|
|
306
|
-
throw new AuthApiError(error_description ?? `OAuth error: ${error}`);
|
|
307
|
-
}
|
|
308
|
-
const result = TokenResponseSchema.safeParse(json);
|
|
309
|
-
if (!result.success) throw new AuthValidationError(`Invalid token response from server: ${result.error.message}`);
|
|
310
|
-
return result.data;
|
|
311
|
-
}
|
|
312
|
-
async function getUserInfo() {
|
|
313
|
-
const response = await httpClient_default.get("oauth/userinfo");
|
|
314
|
-
if (!response.ok) throw new AuthApiError(`Failed to fetch user info: ${response.status}`);
|
|
315
|
-
const result = UserInfoSchema.safeParse(await response.json());
|
|
316
|
-
if (!result.success) throw new AuthValidationError(`Invalid UserInfo response from server: ${result.error.message}`);
|
|
317
|
-
return result.data;
|
|
318
|
-
}
|
|
319
226
|
|
|
320
227
|
//#endregion
|
|
321
228
|
//#region src/cli/utils/runCommand.ts
|
|
@@ -412,9 +319,9 @@ async function saveAuthData(response, userInfo) {
|
|
|
412
319
|
async function login() {
|
|
413
320
|
const deviceCodeResponse = await generateAndDisplayDeviceCode();
|
|
414
321
|
const token = await waitForAuthentication(deviceCodeResponse.deviceCode, deviceCodeResponse.expiresIn, deviceCodeResponse.interval);
|
|
415
|
-
const userInfo = await getUserInfo();
|
|
322
|
+
const userInfo = await getUserInfo(token.accessToken);
|
|
416
323
|
await saveAuthData(token, userInfo);
|
|
417
|
-
log.success(`Successfully logged as ${chalk.bold(userInfo.email)}`);
|
|
324
|
+
log.success(`Successfully logged in as ${chalk.bold(userInfo.email)}`);
|
|
418
325
|
}
|
|
419
326
|
const loginCommand = new Command("login").description("Authenticate with Base44").action(async () => {
|
|
420
327
|
await runCommand(login);
|
package/package.json
CHANGED