vibecheck-ai 1.0.7 → 1.0.8
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/README.md +348 -348
- package/dist/index.js +710 -291
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.js
CHANGED
|
@@ -5,6 +5,8 @@ import * as path18 from 'path';
|
|
|
5
5
|
import path18__default, { resolve, join, basename, dirname, isAbsolute, extname, sep } from 'path';
|
|
6
6
|
import * as fs102 from 'fs/promises';
|
|
7
7
|
import fs102__default, { readFile, copyFile, unlink, stat, mkdir, readdir, writeFile, access, constants } from 'fs/promises';
|
|
8
|
+
import * as os5 from 'os';
|
|
9
|
+
import os5__default from 'os';
|
|
8
10
|
import * as crypto52 from 'crypto';
|
|
9
11
|
import { randomUUID, createHash, createHmac } from 'crypto';
|
|
10
12
|
import { glob } from 'glob';
|
|
@@ -12,8 +14,6 @@ import fg from 'fast-glob';
|
|
|
12
14
|
import * as fs15 from 'fs';
|
|
13
15
|
import fs15__default, { existsSync, promises, realpathSync } from 'fs';
|
|
14
16
|
import { spawn, execSync, exec, execFileSync } from 'child_process';
|
|
15
|
-
import * as os5 from 'os';
|
|
16
|
-
import os5__default from 'os';
|
|
17
17
|
import { promisify } from 'util';
|
|
18
18
|
import { gzip, gunzip } from 'zlib';
|
|
19
19
|
import * as net from 'net';
|
|
@@ -34,7 +34,7 @@ import 'ink';
|
|
|
34
34
|
import 'react/jsx-runtime';
|
|
35
35
|
import 'ink-spinner';
|
|
36
36
|
import 'react';
|
|
37
|
-
import * as
|
|
37
|
+
import * as p4 from '@clack/prompts';
|
|
38
38
|
import { createConsola } from 'consola';
|
|
39
39
|
import { Listr } from 'listr2';
|
|
40
40
|
import ora from 'ora';
|
|
@@ -1080,6 +1080,457 @@ var init_config = __esm({
|
|
|
1080
1080
|
}
|
|
1081
1081
|
});
|
|
1082
1082
|
|
|
1083
|
+
// src/lib/credentials.ts
|
|
1084
|
+
var credentials_exports = {};
|
|
1085
|
+
__export(credentials_exports, {
|
|
1086
|
+
clearCredentials: () => clearCredentials,
|
|
1087
|
+
getApiUrl: () => getApiUrl,
|
|
1088
|
+
getAuthToken: () => getAuthToken,
|
|
1089
|
+
getCredentialsPath: () => getCredentialsPath,
|
|
1090
|
+
getCurrentUser: () => getCurrentUser,
|
|
1091
|
+
getWebUrl: () => getWebUrl,
|
|
1092
|
+
hasCredentials: () => hasCredentials,
|
|
1093
|
+
isLoggedIn: () => isLoggedIn,
|
|
1094
|
+
loadCredentials: () => loadCredentials,
|
|
1095
|
+
loginWithApiKey: () => loginWithApiKey,
|
|
1096
|
+
loginWithEmailPassword: () => loginWithEmailPassword,
|
|
1097
|
+
loginWithOAuth: () => loginWithOAuth,
|
|
1098
|
+
loginWithOAuthToken: () => loginWithOAuthToken,
|
|
1099
|
+
requestMagicLink: () => requestMagicLink,
|
|
1100
|
+
saveCredentials: () => saveCredentials,
|
|
1101
|
+
verifyMagicLink: () => verifyMagicLink
|
|
1102
|
+
});
|
|
1103
|
+
function obfuscate(data) {
|
|
1104
|
+
const key = crypto52.createHash("sha256").update(OBFUSCATION_KEY).digest();
|
|
1105
|
+
const iv = crypto52.randomBytes(16);
|
|
1106
|
+
const cipher = crypto52.createCipheriv("aes-256-cbc", key, iv);
|
|
1107
|
+
let encrypted = cipher.update(data, "utf8", "base64");
|
|
1108
|
+
encrypted += cipher.final("base64");
|
|
1109
|
+
return iv.toString("base64") + ":" + encrypted;
|
|
1110
|
+
}
|
|
1111
|
+
function deobfuscate(data) {
|
|
1112
|
+
try {
|
|
1113
|
+
const [ivBase64, encrypted] = data.split(":");
|
|
1114
|
+
if (!ivBase64 || !encrypted) return data;
|
|
1115
|
+
const key = crypto52.createHash("sha256").update(OBFUSCATION_KEY).digest();
|
|
1116
|
+
const iv = Buffer.from(ivBase64, "base64");
|
|
1117
|
+
const decipher = crypto52.createDecipheriv("aes-256-cbc", key, iv);
|
|
1118
|
+
let decrypted = decipher.update(encrypted, "base64", "utf8");
|
|
1119
|
+
decrypted += decipher.final("utf8");
|
|
1120
|
+
return decrypted;
|
|
1121
|
+
} catch {
|
|
1122
|
+
return data;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
async function ensureConfigDir() {
|
|
1126
|
+
await fs102.mkdir(CONFIG_DIR, { recursive: true });
|
|
1127
|
+
}
|
|
1128
|
+
async function saveCredentials(credentials) {
|
|
1129
|
+
await ensureConfigDir();
|
|
1130
|
+
const toSave = {
|
|
1131
|
+
...credentials,
|
|
1132
|
+
authToken: credentials.authToken ? obfuscate(credentials.authToken) : void 0,
|
|
1133
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1134
|
+
};
|
|
1135
|
+
await fs102.writeFile(
|
|
1136
|
+
CREDENTIALS_PATH,
|
|
1137
|
+
JSON.stringify(toSave, null, 2),
|
|
1138
|
+
{ mode: 384 }
|
|
1139
|
+
// Owner read/write only
|
|
1140
|
+
);
|
|
1141
|
+
}
|
|
1142
|
+
async function loadCredentials() {
|
|
1143
|
+
try {
|
|
1144
|
+
const content = await fs102.readFile(CREDENTIALS_PATH, "utf-8");
|
|
1145
|
+
const stored = JSON.parse(content);
|
|
1146
|
+
if (stored.expiresAt && new Date(stored.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
1147
|
+
return {
|
|
1148
|
+
valid: false,
|
|
1149
|
+
error: "Token has expired. Please run `vibecheck login` to re-authenticate."
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
const credentials = {
|
|
1153
|
+
...stored,
|
|
1154
|
+
authToken: stored.authToken ? deobfuscate(stored.authToken) : void 0
|
|
1155
|
+
};
|
|
1156
|
+
return {
|
|
1157
|
+
valid: Boolean(credentials.authToken),
|
|
1158
|
+
credentials
|
|
1159
|
+
};
|
|
1160
|
+
} catch (error) {
|
|
1161
|
+
if (error.code === "ENOENT") {
|
|
1162
|
+
return {
|
|
1163
|
+
valid: false,
|
|
1164
|
+
error: "Not logged in. Run `vibecheck login` to authenticate."
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
return {
|
|
1168
|
+
valid: false,
|
|
1169
|
+
error: `Failed to load credentials: ${error instanceof Error ? error.message : String(error)}`
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
}
|
|
1173
|
+
async function clearCredentials() {
|
|
1174
|
+
try {
|
|
1175
|
+
await fs102.unlink(CREDENTIALS_PATH);
|
|
1176
|
+
} catch {
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
async function hasCredentials() {
|
|
1180
|
+
try {
|
|
1181
|
+
await fs102.access(CREDENTIALS_PATH);
|
|
1182
|
+
return true;
|
|
1183
|
+
} catch {
|
|
1184
|
+
return false;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
async function getAuthToken() {
|
|
1188
|
+
if (process.env.VIBECHECK_AUTH_TOKEN) {
|
|
1189
|
+
return process.env.VIBECHECK_AUTH_TOKEN;
|
|
1190
|
+
}
|
|
1191
|
+
const result = await loadCredentials();
|
|
1192
|
+
return result.credentials?.authToken;
|
|
1193
|
+
}
|
|
1194
|
+
async function getApiUrl() {
|
|
1195
|
+
if (process.env.VIBECHECK_API_URL) {
|
|
1196
|
+
return process.env.VIBECHECK_API_URL;
|
|
1197
|
+
}
|
|
1198
|
+
if (process.env.API_URL) {
|
|
1199
|
+
return process.env.API_URL;
|
|
1200
|
+
}
|
|
1201
|
+
const result = await loadCredentials();
|
|
1202
|
+
return result.credentials?.apiUrl ?? DEFAULT_API_URL;
|
|
1203
|
+
}
|
|
1204
|
+
async function getWebUrl() {
|
|
1205
|
+
if (process.env.VIBECHECK_WEB_URL) {
|
|
1206
|
+
return process.env.VIBECHECK_WEB_URL;
|
|
1207
|
+
}
|
|
1208
|
+
if (process.env.WEB_URL) {
|
|
1209
|
+
return process.env.WEB_URL;
|
|
1210
|
+
}
|
|
1211
|
+
const result = await loadCredentials();
|
|
1212
|
+
return result.credentials?.webUrl ?? DEFAULT_WEB_URL;
|
|
1213
|
+
}
|
|
1214
|
+
async function getCurrentUser() {
|
|
1215
|
+
const result = await loadCredentials();
|
|
1216
|
+
if (!result.valid || !result.credentials) {
|
|
1217
|
+
return null;
|
|
1218
|
+
}
|
|
1219
|
+
return {
|
|
1220
|
+
userId: result.credentials.userId,
|
|
1221
|
+
email: result.credentials.email,
|
|
1222
|
+
name: result.credentials.name,
|
|
1223
|
+
tier: result.credentials.tier
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
async function isLoggedIn() {
|
|
1227
|
+
const token = await getAuthToken();
|
|
1228
|
+
return Boolean(token);
|
|
1229
|
+
}
|
|
1230
|
+
async function requestMagicLink(email, options = {}) {
|
|
1231
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
1232
|
+
try {
|
|
1233
|
+
const response = await fetch(`${apiUrl}/api/v1/auth/magic-link/request`, {
|
|
1234
|
+
method: "POST",
|
|
1235
|
+
headers: {
|
|
1236
|
+
"Content-Type": "application/json"
|
|
1237
|
+
},
|
|
1238
|
+
body: JSON.stringify({ email })
|
|
1239
|
+
});
|
|
1240
|
+
const data = await response.json();
|
|
1241
|
+
if (!response.ok || !data.success) {
|
|
1242
|
+
return {
|
|
1243
|
+
success: false,
|
|
1244
|
+
error: data.error?.message ?? "Failed to send magic link"
|
|
1245
|
+
};
|
|
1246
|
+
}
|
|
1247
|
+
return { success: true };
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1250
|
+
if (message.includes("ECONNREFUSED") || message.includes("fetch failed")) {
|
|
1251
|
+
return { success: false, error: `Cannot connect to API at ${apiUrl}` };
|
|
1252
|
+
}
|
|
1253
|
+
return { success: false, error: message };
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
async function verifyMagicLink(token, options = {}) {
|
|
1257
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
1258
|
+
const webUrl = options.webUrl ?? DEFAULT_WEB_URL;
|
|
1259
|
+
try {
|
|
1260
|
+
const response = await fetch(`${apiUrl}/api/v1/auth/magic-link/verify`, {
|
|
1261
|
+
method: "POST",
|
|
1262
|
+
headers: {
|
|
1263
|
+
"Content-Type": "application/json"
|
|
1264
|
+
},
|
|
1265
|
+
body: JSON.stringify({ token })
|
|
1266
|
+
});
|
|
1267
|
+
const data = await response.json();
|
|
1268
|
+
if (!response.ok || !data.success) {
|
|
1269
|
+
return {
|
|
1270
|
+
success: false,
|
|
1271
|
+
error: data.error?.message ?? "Invalid or expired magic link"
|
|
1272
|
+
};
|
|
1273
|
+
}
|
|
1274
|
+
const { accessToken, expiresIn, user } = data.data;
|
|
1275
|
+
const credentials = {
|
|
1276
|
+
authToken: accessToken,
|
|
1277
|
+
apiUrl,
|
|
1278
|
+
webUrl,
|
|
1279
|
+
userId: user.id,
|
|
1280
|
+
email: user.email,
|
|
1281
|
+
name: user.name,
|
|
1282
|
+
tier: user.tier,
|
|
1283
|
+
expiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
1284
|
+
};
|
|
1285
|
+
await saveCredentials(credentials);
|
|
1286
|
+
return { success: true, user: credentials };
|
|
1287
|
+
} catch (error) {
|
|
1288
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1289
|
+
if (message.includes("ECONNREFUSED") || message.includes("fetch failed")) {
|
|
1290
|
+
return { success: false, error: `Cannot connect to API at ${apiUrl}` };
|
|
1291
|
+
}
|
|
1292
|
+
return { success: false, error: message };
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
async function loginWithEmailPassword(email, password2, options = {}) {
|
|
1296
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
1297
|
+
const webUrl = options.webUrl ?? DEFAULT_WEB_URL;
|
|
1298
|
+
try {
|
|
1299
|
+
const response = await fetch(`${apiUrl}/api/v1/auth/login`, {
|
|
1300
|
+
method: "POST",
|
|
1301
|
+
headers: {
|
|
1302
|
+
"Content-Type": "application/json"
|
|
1303
|
+
},
|
|
1304
|
+
body: JSON.stringify({ email, password: password2 })
|
|
1305
|
+
});
|
|
1306
|
+
const data = await response.json();
|
|
1307
|
+
if (!response.ok || !data.success) {
|
|
1308
|
+
return {
|
|
1309
|
+
success: false,
|
|
1310
|
+
error: data.error?.message ?? "Invalid email or password"
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
const { accessToken, expiresIn, user } = data.data;
|
|
1314
|
+
const credentials = {
|
|
1315
|
+
authToken: accessToken,
|
|
1316
|
+
apiUrl,
|
|
1317
|
+
webUrl,
|
|
1318
|
+
userId: user.id,
|
|
1319
|
+
email: user.email,
|
|
1320
|
+
name: user.name,
|
|
1321
|
+
tier: user.tier,
|
|
1322
|
+
expiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
1323
|
+
};
|
|
1324
|
+
await saveCredentials(credentials);
|
|
1325
|
+
return { success: true, user: credentials };
|
|
1326
|
+
} catch (error) {
|
|
1327
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1328
|
+
if (message.includes("ECONNREFUSED") || message.includes("fetch failed")) {
|
|
1329
|
+
return { success: false, error: `Cannot connect to API at ${apiUrl}` };
|
|
1330
|
+
}
|
|
1331
|
+
return { success: false, error: message };
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
async function loginWithApiKey(apiKey, options = {}) {
|
|
1335
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
1336
|
+
const webUrl = options.webUrl ?? DEFAULT_WEB_URL;
|
|
1337
|
+
try {
|
|
1338
|
+
const response = await fetch(`${apiUrl}/api/v1/auth/me`, {
|
|
1339
|
+
headers: {
|
|
1340
|
+
"Authorization": `Bearer ${apiKey}`
|
|
1341
|
+
}
|
|
1342
|
+
});
|
|
1343
|
+
if (!response.ok) {
|
|
1344
|
+
if (response.status === 401) {
|
|
1345
|
+
return { success: false, error: "Invalid API key. Please check and try again." };
|
|
1346
|
+
}
|
|
1347
|
+
return { success: false, error: `Authentication failed: ${response.status}` };
|
|
1348
|
+
}
|
|
1349
|
+
const userData = await response.json();
|
|
1350
|
+
const credentials = {
|
|
1351
|
+
authToken: apiKey,
|
|
1352
|
+
apiUrl,
|
|
1353
|
+
webUrl,
|
|
1354
|
+
userId: userData.id,
|
|
1355
|
+
email: userData.email,
|
|
1356
|
+
name: userData.name,
|
|
1357
|
+
tier: userData.tier,
|
|
1358
|
+
// API keys don't expire unless revoked
|
|
1359
|
+
expiresAt: void 0
|
|
1360
|
+
};
|
|
1361
|
+
await saveCredentials(credentials);
|
|
1362
|
+
return { success: true, user: credentials };
|
|
1363
|
+
} catch (error) {
|
|
1364
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1365
|
+
if (message.includes("ECONNREFUSED") || message.includes("fetch failed")) {
|
|
1366
|
+
return { success: false, error: `Cannot connect to API at ${apiUrl}` };
|
|
1367
|
+
}
|
|
1368
|
+
return { success: false, error: message };
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
async function loginWithOAuth(provider, options = {}) {
|
|
1372
|
+
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
1373
|
+
const webUrl = options.webUrl ?? DEFAULT_WEB_URL;
|
|
1374
|
+
const http2 = await import('http');
|
|
1375
|
+
const { URL: URL2 } = await import('url');
|
|
1376
|
+
return new Promise((resolve6) => {
|
|
1377
|
+
const server = http2.createServer(async (req, res) => {
|
|
1378
|
+
const url = new URL2(req.url ?? "/", `http://localhost`);
|
|
1379
|
+
if (url.pathname === "/callback") {
|
|
1380
|
+
const code = url.searchParams.get("code");
|
|
1381
|
+
const error = url.searchParams.get("error");
|
|
1382
|
+
if (error) {
|
|
1383
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1384
|
+
res.end(`
|
|
1385
|
+
<html>
|
|
1386
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
1387
|
+
<h1>\u274C Authentication Failed</h1>
|
|
1388
|
+
<p>Error: ${error}</p>
|
|
1389
|
+
<p>You can close this window and try again.</p>
|
|
1390
|
+
</body>
|
|
1391
|
+
</html>
|
|
1392
|
+
`);
|
|
1393
|
+
server.close();
|
|
1394
|
+
resolve6({ success: false, error: `OAuth error: ${error}` });
|
|
1395
|
+
return;
|
|
1396
|
+
}
|
|
1397
|
+
if (!code) {
|
|
1398
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1399
|
+
res.end(`
|
|
1400
|
+
<html>
|
|
1401
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
1402
|
+
<h1>\u274C Missing Authorization Code</h1>
|
|
1403
|
+
<p>You can close this window and try again.</p>
|
|
1404
|
+
</body>
|
|
1405
|
+
</html>
|
|
1406
|
+
`);
|
|
1407
|
+
server.close();
|
|
1408
|
+
resolve6({ success: false, error: "Missing authorization code" });
|
|
1409
|
+
return;
|
|
1410
|
+
}
|
|
1411
|
+
try {
|
|
1412
|
+
const exchangeResponse = await fetch(`${apiUrl}/api/v1/auth/${provider}/exchange`, {
|
|
1413
|
+
method: "POST",
|
|
1414
|
+
headers: { "Content-Type": "application/json" },
|
|
1415
|
+
body: JSON.stringify({ code, redirect_uri: "http://localhost:9876/callback" })
|
|
1416
|
+
});
|
|
1417
|
+
const data = await exchangeResponse.json();
|
|
1418
|
+
if (!exchangeResponse.ok || !data.success) {
|
|
1419
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1420
|
+
res.end(`
|
|
1421
|
+
<html>
|
|
1422
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
1423
|
+
<h1>\u274C Authentication Failed</h1>
|
|
1424
|
+
<p>${data.error?.message ?? "Unknown error"}</p>
|
|
1425
|
+
<p>You can close this window and try again.</p>
|
|
1426
|
+
</body>
|
|
1427
|
+
</html>
|
|
1428
|
+
`);
|
|
1429
|
+
server.close();
|
|
1430
|
+
resolve6({ success: false, error: data.error?.message ?? "Token exchange failed" });
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
const { accessToken, expiresIn, user } = data.data;
|
|
1434
|
+
const credentials = {
|
|
1435
|
+
authToken: accessToken,
|
|
1436
|
+
apiUrl,
|
|
1437
|
+
webUrl,
|
|
1438
|
+
userId: user.id,
|
|
1439
|
+
email: user.email,
|
|
1440
|
+
name: user.name,
|
|
1441
|
+
tier: user.tier,
|
|
1442
|
+
expiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
1443
|
+
};
|
|
1444
|
+
await saveCredentials(credentials);
|
|
1445
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1446
|
+
res.end(`
|
|
1447
|
+
<html>
|
|
1448
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; min-height: 100vh; margin: 0;">
|
|
1449
|
+
<h1>\u2705 Authentication Successful!</h1>
|
|
1450
|
+
<p>Welcome, ${user.name ?? user.email}!</p>
|
|
1451
|
+
<p>You can close this window and return to the CLI.</p>
|
|
1452
|
+
</body>
|
|
1453
|
+
</html>
|
|
1454
|
+
`);
|
|
1455
|
+
server.close();
|
|
1456
|
+
resolve6({ success: true, user: credentials });
|
|
1457
|
+
} catch (err) {
|
|
1458
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1459
|
+
res.end(`
|
|
1460
|
+
<html>
|
|
1461
|
+
<body style="font-family: system-ui; padding: 40px; text-align: center;">
|
|
1462
|
+
<h1>\u274C Authentication Failed</h1>
|
|
1463
|
+
<p>${err instanceof Error ? err.message : "Unknown error"}</p>
|
|
1464
|
+
<p>You can close this window and try again.</p>
|
|
1465
|
+
</body>
|
|
1466
|
+
</html>
|
|
1467
|
+
`);
|
|
1468
|
+
server.close();
|
|
1469
|
+
resolve6({ success: false, error: err instanceof Error ? err.message : "Unknown error" });
|
|
1470
|
+
}
|
|
1471
|
+
} else {
|
|
1472
|
+
res.writeHead(404);
|
|
1473
|
+
res.end("Not found");
|
|
1474
|
+
}
|
|
1475
|
+
});
|
|
1476
|
+
server.listen(9876, "127.0.0.1", async () => {
|
|
1477
|
+
const redirectUri = "http://localhost:9876/callback";
|
|
1478
|
+
let authUrl;
|
|
1479
|
+
if (provider === "github") {
|
|
1480
|
+
const params = new URLSearchParams({
|
|
1481
|
+
client_id: process.env.GITHUB_CLIENT_ID ?? "",
|
|
1482
|
+
redirect_uri: `${apiUrl}/api/v1/auth/github/callback?cli_redirect=${encodeURIComponent(redirectUri)}`,
|
|
1483
|
+
scope: "user:email read:user"
|
|
1484
|
+
});
|
|
1485
|
+
authUrl = `https://github.com/login/oauth/authorize?${params}`;
|
|
1486
|
+
} else {
|
|
1487
|
+
const params = new URLSearchParams({
|
|
1488
|
+
client_id: process.env.GOOGLE_CLIENT_ID ?? "",
|
|
1489
|
+
redirect_uri: `${apiUrl}/api/v1/auth/google/callback?cli_redirect=${encodeURIComponent(redirectUri)}`,
|
|
1490
|
+
response_type: "code",
|
|
1491
|
+
scope: "openid email profile",
|
|
1492
|
+
access_type: "offline",
|
|
1493
|
+
prompt: "consent"
|
|
1494
|
+
});
|
|
1495
|
+
authUrl = `https://accounts.google.com/o/oauth2/v2/auth?${params}`;
|
|
1496
|
+
}
|
|
1497
|
+
const open = (await import('open')).default;
|
|
1498
|
+
await open(authUrl);
|
|
1499
|
+
});
|
|
1500
|
+
setTimeout(() => {
|
|
1501
|
+
server.close();
|
|
1502
|
+
resolve6({ success: false, error: "Authentication timed out. Please try again." });
|
|
1503
|
+
}, 5 * 60 * 1e3);
|
|
1504
|
+
});
|
|
1505
|
+
}
|
|
1506
|
+
async function loginWithOAuthToken(accessToken, refreshToken, expiresIn, user, options = {}) {
|
|
1507
|
+
const credentials = {
|
|
1508
|
+
authToken: accessToken,
|
|
1509
|
+
apiUrl: options.apiUrl ?? DEFAULT_API_URL,
|
|
1510
|
+
webUrl: options.webUrl ?? DEFAULT_WEB_URL,
|
|
1511
|
+
userId: user.id,
|
|
1512
|
+
email: user.email,
|
|
1513
|
+
name: user.name,
|
|
1514
|
+
tier: user.tier,
|
|
1515
|
+
expiresAt: new Date(Date.now() + expiresIn * 1e3).toISOString()
|
|
1516
|
+
};
|
|
1517
|
+
await saveCredentials(credentials);
|
|
1518
|
+
}
|
|
1519
|
+
function getCredentialsPath() {
|
|
1520
|
+
return CREDENTIALS_PATH;
|
|
1521
|
+
}
|
|
1522
|
+
var CONFIG_DIR, CREDENTIALS_FILE, CREDENTIALS_PATH, OBFUSCATION_KEY, DEFAULT_API_URL, DEFAULT_WEB_URL;
|
|
1523
|
+
var init_credentials = __esm({
|
|
1524
|
+
"src/lib/credentials.ts"() {
|
|
1525
|
+
CONFIG_DIR = path18.join(os5.homedir(), ".config", "vibecheck");
|
|
1526
|
+
CREDENTIALS_FILE = "credentials.json";
|
|
1527
|
+
CREDENTIALS_PATH = path18.join(CONFIG_DIR, CREDENTIALS_FILE);
|
|
1528
|
+
OBFUSCATION_KEY = "vibecheck-cli-v1";
|
|
1529
|
+
DEFAULT_API_URL = "https://api.vibecheckai.dev";
|
|
1530
|
+
DEFAULT_WEB_URL = "https://app.vibecheckai.dev";
|
|
1531
|
+
}
|
|
1532
|
+
});
|
|
1533
|
+
|
|
1083
1534
|
// ../../packages/shared-types/dist/index.js
|
|
1084
1535
|
function tierMeetsRequirement(userTier, requiredTier) {
|
|
1085
1536
|
const userIndex = TIER_ORDER.indexOf(userTier);
|
|
@@ -70142,8 +70593,8 @@ async function runInitWizard() {
|
|
|
70142
70593
|
configContent: generateConfigTemplate("standard")
|
|
70143
70594
|
};
|
|
70144
70595
|
}
|
|
70145
|
-
|
|
70146
|
-
const template = await
|
|
70596
|
+
p4.intro(chalk8.cyan("vibecheck init"));
|
|
70597
|
+
const template = await p4.select({
|
|
70147
70598
|
message: "Choose a configuration template:",
|
|
70148
70599
|
options: [
|
|
70149
70600
|
{
|
|
@@ -70164,11 +70615,11 @@ async function runInitWizard() {
|
|
|
70164
70615
|
],
|
|
70165
70616
|
initialValue: "standard"
|
|
70166
70617
|
});
|
|
70167
|
-
if (
|
|
70168
|
-
|
|
70618
|
+
if (p4.isCancel(template)) {
|
|
70619
|
+
p4.cancel("Setup cancelled.");
|
|
70169
70620
|
return null;
|
|
70170
70621
|
}
|
|
70171
|
-
const rules = await
|
|
70622
|
+
const rules = await p4.multiselect({
|
|
70172
70623
|
message: "Select scanners to enable:",
|
|
70173
70624
|
options: [
|
|
70174
70625
|
{ value: "routes", label: "Routes", hint: "API routes and endpoints" },
|
|
@@ -70180,20 +70631,20 @@ async function runInitWizard() {
|
|
|
70180
70631
|
initialValues: template === "minimal" ? ["routes", "env"] : ["routes", "env", "auth", "contracts"],
|
|
70181
70632
|
required: true
|
|
70182
70633
|
});
|
|
70183
|
-
if (
|
|
70184
|
-
|
|
70634
|
+
if (p4.isCancel(rules)) {
|
|
70635
|
+
p4.cancel("Setup cancelled.");
|
|
70185
70636
|
return null;
|
|
70186
70637
|
}
|
|
70187
|
-
const strict = await
|
|
70638
|
+
const strict = await p4.confirm({
|
|
70188
70639
|
message: "Enable strict mode?",
|
|
70189
70640
|
initialValue: template === "strict"
|
|
70190
70641
|
});
|
|
70191
|
-
if (
|
|
70192
|
-
|
|
70642
|
+
if (p4.isCancel(strict)) {
|
|
70643
|
+
p4.cancel("Setup cancelled.");
|
|
70193
70644
|
return null;
|
|
70194
70645
|
}
|
|
70195
70646
|
const configContent = generateConfigTemplate(template);
|
|
70196
|
-
|
|
70647
|
+
p4.outro(chalk8.green("Configuration ready!"));
|
|
70197
70648
|
return {
|
|
70198
70649
|
template,
|
|
70199
70650
|
rules,
|
|
@@ -70205,11 +70656,11 @@ async function confirmOverwrite(path28) {
|
|
|
70205
70656
|
if (!shouldPrompt()) {
|
|
70206
70657
|
return false;
|
|
70207
70658
|
}
|
|
70208
|
-
const confirmed = await
|
|
70659
|
+
const confirmed = await p4.confirm({
|
|
70209
70660
|
message: `Config file already exists at ${chalk8.cyan(path28)}. Overwrite?`,
|
|
70210
70661
|
initialValue: false
|
|
70211
70662
|
});
|
|
70212
|
-
if (
|
|
70663
|
+
if (p4.isCancel(confirmed)) {
|
|
70213
70664
|
return false;
|
|
70214
70665
|
}
|
|
70215
70666
|
return confirmed;
|
|
@@ -70224,13 +70675,13 @@ async function runConfigWizard(currentConfig) {
|
|
|
70224
70675
|
if (!shouldPrompt()) {
|
|
70225
70676
|
return null;
|
|
70226
70677
|
}
|
|
70227
|
-
|
|
70228
|
-
const section = await
|
|
70678
|
+
p4.intro(chalk8.cyan("vibecheck config"));
|
|
70679
|
+
const section = await p4.select({
|
|
70229
70680
|
message: "What would you like to configure?",
|
|
70230
70681
|
options: [...CONFIG_SECTIONS]
|
|
70231
70682
|
});
|
|
70232
|
-
if (
|
|
70233
|
-
|
|
70683
|
+
if (p4.isCancel(section)) {
|
|
70684
|
+
p4.cancel("Configuration cancelled.");
|
|
70234
70685
|
return null;
|
|
70235
70686
|
}
|
|
70236
70687
|
switch (section) {
|
|
@@ -70255,37 +70706,37 @@ var SCANNER_OPTIONS = [
|
|
|
70255
70706
|
{ value: "ui", label: "UI Graph", hint: "Component relationships" }
|
|
70256
70707
|
];
|
|
70257
70708
|
async function configureRules(currentConfig) {
|
|
70258
|
-
const rules = await
|
|
70709
|
+
const rules = await p4.multiselect({
|
|
70259
70710
|
message: "Select scanners to enable:",
|
|
70260
70711
|
options: [...SCANNER_OPTIONS],
|
|
70261
70712
|
initialValues: currentConfig.rules,
|
|
70262
70713
|
required: true
|
|
70263
70714
|
});
|
|
70264
|
-
if (
|
|
70265
|
-
|
|
70715
|
+
if (p4.isCancel(rules)) {
|
|
70716
|
+
p4.cancel("Scanner configuration cancelled.");
|
|
70266
70717
|
return null;
|
|
70267
70718
|
}
|
|
70268
|
-
|
|
70719
|
+
p4.outro(chalk8.green("Rules updated!"));
|
|
70269
70720
|
return { rules };
|
|
70270
70721
|
}
|
|
70271
70722
|
async function configureValidation(currentConfig) {
|
|
70272
|
-
const strict = await
|
|
70723
|
+
const strict = await p4.confirm({
|
|
70273
70724
|
message: "Enable strict mode?",
|
|
70274
70725
|
initialValue: currentConfig.strict
|
|
70275
70726
|
});
|
|
70276
|
-
if (
|
|
70277
|
-
|
|
70727
|
+
if (p4.isCancel(strict)) {
|
|
70728
|
+
p4.cancel("Validation configuration cancelled.");
|
|
70278
70729
|
return null;
|
|
70279
70730
|
}
|
|
70280
|
-
const failFast = await
|
|
70731
|
+
const failFast = await p4.confirm({
|
|
70281
70732
|
message: "Stop on first error (fail-fast)?",
|
|
70282
70733
|
initialValue: currentConfig.validation.failFast
|
|
70283
70734
|
});
|
|
70284
|
-
if (
|
|
70285
|
-
|
|
70735
|
+
if (p4.isCancel(failFast)) {
|
|
70736
|
+
p4.cancel("Validation configuration cancelled.");
|
|
70286
70737
|
return null;
|
|
70287
70738
|
}
|
|
70288
|
-
const maxErrorsStr = await
|
|
70739
|
+
const maxErrorsStr = await p4.text({
|
|
70289
70740
|
message: "Maximum errors to report:",
|
|
70290
70741
|
initialValue: String(currentConfig.validation.maxErrors),
|
|
70291
70742
|
validate: (value) => {
|
|
@@ -70299,11 +70750,11 @@ async function configureValidation(currentConfig) {
|
|
|
70299
70750
|
return void 0;
|
|
70300
70751
|
}
|
|
70301
70752
|
});
|
|
70302
|
-
if (
|
|
70303
|
-
|
|
70753
|
+
if (p4.isCancel(maxErrorsStr)) {
|
|
70754
|
+
p4.cancel("Validation configuration cancelled.");
|
|
70304
70755
|
return null;
|
|
70305
70756
|
}
|
|
70306
|
-
|
|
70757
|
+
p4.outro(chalk8.green("Validation settings updated!"));
|
|
70307
70758
|
return {
|
|
70308
70759
|
strict,
|
|
70309
70760
|
validation: {
|
|
@@ -70314,7 +70765,7 @@ async function configureValidation(currentConfig) {
|
|
|
70314
70765
|
};
|
|
70315
70766
|
}
|
|
70316
70767
|
async function configureWatch(currentConfig) {
|
|
70317
|
-
const includeStr = await
|
|
70768
|
+
const includeStr = await p4.text({
|
|
70318
70769
|
message: "Include patterns (comma-separated):",
|
|
70319
70770
|
initialValue: currentConfig.watch.include.join(", "),
|
|
70320
70771
|
placeholder: "src/**/*.ts, src/**/*.tsx",
|
|
@@ -70325,20 +70776,20 @@ async function configureWatch(currentConfig) {
|
|
|
70325
70776
|
return void 0;
|
|
70326
70777
|
}
|
|
70327
70778
|
});
|
|
70328
|
-
if (
|
|
70329
|
-
|
|
70779
|
+
if (p4.isCancel(includeStr)) {
|
|
70780
|
+
p4.cancel("Watch configuration cancelled.");
|
|
70330
70781
|
return null;
|
|
70331
70782
|
}
|
|
70332
|
-
const excludeStr = await
|
|
70783
|
+
const excludeStr = await p4.text({
|
|
70333
70784
|
message: "Exclude patterns (comma-separated):",
|
|
70334
70785
|
initialValue: currentConfig.watch.exclude.join(", "),
|
|
70335
70786
|
placeholder: "node_modules, dist, build"
|
|
70336
70787
|
});
|
|
70337
|
-
if (
|
|
70338
|
-
|
|
70788
|
+
if (p4.isCancel(excludeStr)) {
|
|
70789
|
+
p4.cancel("Watch configuration cancelled.");
|
|
70339
70790
|
return null;
|
|
70340
70791
|
}
|
|
70341
|
-
const debounceStr = await
|
|
70792
|
+
const debounceStr = await p4.text({
|
|
70342
70793
|
message: "Debounce delay (ms):",
|
|
70343
70794
|
initialValue: String(currentConfig.watch.debounce),
|
|
70344
70795
|
validate: (value) => {
|
|
@@ -70355,11 +70806,11 @@ async function configureWatch(currentConfig) {
|
|
|
70355
70806
|
return void 0;
|
|
70356
70807
|
}
|
|
70357
70808
|
});
|
|
70358
|
-
if (
|
|
70359
|
-
|
|
70809
|
+
if (p4.isCancel(debounceStr)) {
|
|
70810
|
+
p4.cancel("Watch configuration cancelled.");
|
|
70360
70811
|
return null;
|
|
70361
70812
|
}
|
|
70362
|
-
|
|
70813
|
+
p4.outro(chalk8.green("Watch settings updated!"));
|
|
70363
70814
|
const includePatterns = includeStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
70364
70815
|
const excludePatterns = excludeStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
70365
70816
|
return {
|
|
@@ -70375,16 +70826,16 @@ var OUTPUT_FORMAT_OPTIONS = [
|
|
|
70375
70826
|
{ value: "json", label: "JSON", hint: "Machine-readable JSON" }
|
|
70376
70827
|
];
|
|
70377
70828
|
async function configureOutput(currentConfig) {
|
|
70378
|
-
const output = await
|
|
70829
|
+
const output = await p4.select({
|
|
70379
70830
|
message: "Default output format:",
|
|
70380
70831
|
options: [...OUTPUT_FORMAT_OPTIONS],
|
|
70381
70832
|
initialValue: currentConfig.output
|
|
70382
70833
|
});
|
|
70383
|
-
if (
|
|
70384
|
-
|
|
70834
|
+
if (p4.isCancel(output)) {
|
|
70835
|
+
p4.cancel("Output configuration cancelled.");
|
|
70385
70836
|
return null;
|
|
70386
70837
|
}
|
|
70387
|
-
const truthpackPath = await
|
|
70838
|
+
const truthpackPath = await p4.text({
|
|
70388
70839
|
message: "Truthpack storage path:",
|
|
70389
70840
|
initialValue: currentConfig.truthpackPath,
|
|
70390
70841
|
placeholder: ".vibecheck/truthpack",
|
|
@@ -70396,16 +70847,22 @@ async function configureOutput(currentConfig) {
|
|
|
70396
70847
|
return void 0;
|
|
70397
70848
|
}
|
|
70398
70849
|
});
|
|
70399
|
-
if (
|
|
70400
|
-
|
|
70850
|
+
if (p4.isCancel(truthpackPath)) {
|
|
70851
|
+
p4.cancel("Output configuration cancelled.");
|
|
70401
70852
|
return null;
|
|
70402
70853
|
}
|
|
70403
|
-
|
|
70854
|
+
p4.outro(chalk8.green("Output settings updated!"));
|
|
70404
70855
|
return {
|
|
70405
70856
|
output,
|
|
70406
70857
|
truthpackPath: truthpackPath.trim()
|
|
70407
70858
|
};
|
|
70408
70859
|
}
|
|
70860
|
+
|
|
70861
|
+
// src/lib/version.ts
|
|
70862
|
+
var CLI_VERSION = "1.0.8" ;
|
|
70863
|
+
var CLI_NAME = "vibecheck-ai" ;
|
|
70864
|
+
|
|
70865
|
+
// src/ui/command-header.tsx
|
|
70409
70866
|
var VIBECHECK_BANNER = [
|
|
70410
70867
|
"\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
70411
70868
|
"\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2550\u255D\u2588\u2588\u2551 \u2588\u2588\u2554\u255D",
|
|
@@ -70561,7 +71018,7 @@ function getCommandAscii(command) {
|
|
|
70561
71018
|
return COMMAND_ASCII[key] || [`[ ${key} ]`];
|
|
70562
71019
|
}
|
|
70563
71020
|
function renderCommandHeader(options) {
|
|
70564
|
-
const { command, version =
|
|
71021
|
+
const { command, version = CLI_VERSION, target, sessionId, elapsedTime = 0 } = options;
|
|
70565
71022
|
const session = sessionId || `${Math.random().toString(36).substring(2, 4).toUpperCase()}-${Math.floor(Math.random() * 1e3)}`;
|
|
70566
71023
|
const commandAscii = getCommandAscii(command);
|
|
70567
71024
|
const integrityStatus = options.vitals && options.vitals.length > 0 ? options.vitals[0].status.toUpperCase() : "READY";
|
|
@@ -71120,7 +71577,7 @@ function getStatusIndicator() {
|
|
|
71120
71577
|
}
|
|
71121
71578
|
async function showInteractiveMenu() {
|
|
71122
71579
|
await printWelcome();
|
|
71123
|
-
const selection = await
|
|
71580
|
+
const selection = await p4.select({
|
|
71124
71581
|
message: chalk8.cyan.bold("What would you like to do?"),
|
|
71125
71582
|
options: MAIN_MENU_ITEMS.filter((item) => !item.value.startsWith("divider")).map((item) => ({
|
|
71126
71583
|
value: item.value,
|
|
@@ -71128,7 +71585,7 @@ async function showInteractiveMenu() {
|
|
|
71128
71585
|
hint: chalk8.dim(item.hint)
|
|
71129
71586
|
}))
|
|
71130
71587
|
});
|
|
71131
|
-
if (
|
|
71588
|
+
if (p4.isCancel(selection)) {
|
|
71132
71589
|
console.log("");
|
|
71133
71590
|
console.log(chalk8.dim(" \u{1F44B} See you next time, Vibecoder!"));
|
|
71134
71591
|
console.log("");
|
|
@@ -71146,152 +71603,14 @@ function printError(message) {
|
|
|
71146
71603
|
console.log(chalk8.red.bold(" \u2717 ") + chalk8.white(message));
|
|
71147
71604
|
console.log("");
|
|
71148
71605
|
}
|
|
71149
|
-
|
|
71150
|
-
|
|
71151
|
-
|
|
71152
|
-
|
|
71153
|
-
var DEFAULT_API_URL = "https://api.vibecheckai.dev";
|
|
71154
|
-
var DEFAULT_WEB_URL = "https://app.vibecheckai.dev";
|
|
71155
|
-
function obfuscate(data) {
|
|
71156
|
-
const key = crypto52.createHash("sha256").update(OBFUSCATION_KEY).digest();
|
|
71157
|
-
const iv = crypto52.randomBytes(16);
|
|
71158
|
-
const cipher = crypto52.createCipheriv("aes-256-cbc", key, iv);
|
|
71159
|
-
let encrypted = cipher.update(data, "utf8", "base64");
|
|
71160
|
-
encrypted += cipher.final("base64");
|
|
71161
|
-
return iv.toString("base64") + ":" + encrypted;
|
|
71162
|
-
}
|
|
71163
|
-
function deobfuscate(data) {
|
|
71164
|
-
try {
|
|
71165
|
-
const [ivBase64, encrypted] = data.split(":");
|
|
71166
|
-
if (!ivBase64 || !encrypted) return data;
|
|
71167
|
-
const key = crypto52.createHash("sha256").update(OBFUSCATION_KEY).digest();
|
|
71168
|
-
const iv = Buffer.from(ivBase64, "base64");
|
|
71169
|
-
const decipher = crypto52.createDecipheriv("aes-256-cbc", key, iv);
|
|
71170
|
-
let decrypted = decipher.update(encrypted, "base64", "utf8");
|
|
71171
|
-
decrypted += decipher.final("utf8");
|
|
71172
|
-
return decrypted;
|
|
71173
|
-
} catch {
|
|
71174
|
-
return data;
|
|
71175
|
-
}
|
|
71176
|
-
}
|
|
71177
|
-
async function ensureConfigDir() {
|
|
71178
|
-
await fs102.mkdir(CONFIG_DIR, { recursive: true });
|
|
71179
|
-
}
|
|
71180
|
-
async function saveCredentials(credentials) {
|
|
71181
|
-
await ensureConfigDir();
|
|
71182
|
-
const toSave = {
|
|
71183
|
-
...credentials,
|
|
71184
|
-
authToken: credentials.authToken ? obfuscate(credentials.authToken) : void 0,
|
|
71185
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
71186
|
-
};
|
|
71187
|
-
await fs102.writeFile(
|
|
71188
|
-
CREDENTIALS_PATH,
|
|
71189
|
-
JSON.stringify(toSave, null, 2),
|
|
71190
|
-
{ mode: 384 }
|
|
71191
|
-
// Owner read/write only
|
|
71192
|
-
);
|
|
71193
|
-
}
|
|
71194
|
-
async function loadCredentials() {
|
|
71195
|
-
try {
|
|
71196
|
-
const content = await fs102.readFile(CREDENTIALS_PATH, "utf-8");
|
|
71197
|
-
const stored = JSON.parse(content);
|
|
71198
|
-
if (stored.expiresAt && new Date(stored.expiresAt) < /* @__PURE__ */ new Date()) {
|
|
71199
|
-
return {
|
|
71200
|
-
valid: false,
|
|
71201
|
-
error: "Token has expired. Please run `vibecheck login` to re-authenticate."
|
|
71202
|
-
};
|
|
71203
|
-
}
|
|
71204
|
-
const credentials = {
|
|
71205
|
-
...stored,
|
|
71206
|
-
authToken: stored.authToken ? deobfuscate(stored.authToken) : void 0
|
|
71207
|
-
};
|
|
71208
|
-
return {
|
|
71209
|
-
valid: Boolean(credentials.authToken),
|
|
71210
|
-
credentials
|
|
71211
|
-
};
|
|
71212
|
-
} catch (error) {
|
|
71213
|
-
if (error.code === "ENOENT") {
|
|
71214
|
-
return {
|
|
71215
|
-
valid: false,
|
|
71216
|
-
error: "Not logged in. Run `vibecheck login` to authenticate."
|
|
71217
|
-
};
|
|
71218
|
-
}
|
|
71219
|
-
return {
|
|
71220
|
-
valid: false,
|
|
71221
|
-
error: `Failed to load credentials: ${error instanceof Error ? error.message : String(error)}`
|
|
71222
|
-
};
|
|
71223
|
-
}
|
|
71224
|
-
}
|
|
71225
|
-
async function getAuthToken() {
|
|
71226
|
-
if (process.env.VIBECHECK_AUTH_TOKEN) {
|
|
71227
|
-
return process.env.VIBECHECK_AUTH_TOKEN;
|
|
71228
|
-
}
|
|
71229
|
-
const result = await loadCredentials();
|
|
71230
|
-
return result.credentials?.authToken;
|
|
71231
|
-
}
|
|
71232
|
-
async function getApiUrl() {
|
|
71233
|
-
if (process.env.VIBECHECK_API_URL) {
|
|
71234
|
-
return process.env.VIBECHECK_API_URL;
|
|
71235
|
-
}
|
|
71236
|
-
if (process.env.API_URL) {
|
|
71237
|
-
return process.env.API_URL;
|
|
71238
|
-
}
|
|
71239
|
-
const result = await loadCredentials();
|
|
71240
|
-
return result.credentials?.apiUrl ?? DEFAULT_API_URL;
|
|
71241
|
-
}
|
|
71242
|
-
async function getWebUrl() {
|
|
71243
|
-
if (process.env.VIBECHECK_WEB_URL) {
|
|
71244
|
-
return process.env.VIBECHECK_WEB_URL;
|
|
71245
|
-
}
|
|
71246
|
-
if (process.env.WEB_URL) {
|
|
71247
|
-
return process.env.WEB_URL;
|
|
71248
|
-
}
|
|
71249
|
-
const result = await loadCredentials();
|
|
71250
|
-
return result.credentials?.webUrl ?? DEFAULT_WEB_URL;
|
|
71251
|
-
}
|
|
71252
|
-
async function isLoggedIn() {
|
|
71253
|
-
const token = await getAuthToken();
|
|
71254
|
-
return Boolean(token);
|
|
71255
|
-
}
|
|
71256
|
-
async function loginWithApiKey(apiKey, options = {}) {
|
|
71257
|
-
const apiUrl = options.apiUrl ?? DEFAULT_API_URL;
|
|
71258
|
-
const webUrl = options.webUrl ?? DEFAULT_WEB_URL;
|
|
71259
|
-
try {
|
|
71260
|
-
const response = await fetch(`${apiUrl}/api/v1/auth/me`, {
|
|
71261
|
-
headers: {
|
|
71262
|
-
"Authorization": `Bearer ${apiKey}`
|
|
71263
|
-
}
|
|
71264
|
-
});
|
|
71265
|
-
if (!response.ok) {
|
|
71266
|
-
if (response.status === 401) {
|
|
71267
|
-
return { success: false, error: "Invalid API key. Please check and try again." };
|
|
71268
|
-
}
|
|
71269
|
-
return { success: false, error: `Authentication failed: ${response.status}` };
|
|
71270
|
-
}
|
|
71271
|
-
const userData = await response.json();
|
|
71272
|
-
const credentials = {
|
|
71273
|
-
authToken: apiKey,
|
|
71274
|
-
apiUrl,
|
|
71275
|
-
webUrl,
|
|
71276
|
-
userId: userData.id,
|
|
71277
|
-
email: userData.email,
|
|
71278
|
-
name: userData.name,
|
|
71279
|
-
tier: userData.tier,
|
|
71280
|
-
// API keys don't expire unless revoked
|
|
71281
|
-
expiresAt: void 0
|
|
71282
|
-
};
|
|
71283
|
-
await saveCredentials(credentials);
|
|
71284
|
-
return { success: true, user: credentials };
|
|
71285
|
-
} catch (error) {
|
|
71286
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
71287
|
-
if (message.includes("ECONNREFUSED") || message.includes("fetch failed")) {
|
|
71288
|
-
return { success: false, error: `Cannot connect to API at ${apiUrl}` };
|
|
71289
|
-
}
|
|
71290
|
-
return { success: false, error: message };
|
|
71291
|
-
}
|
|
71606
|
+
function printWarning(message) {
|
|
71607
|
+
console.log("");
|
|
71608
|
+
console.log(chalk8.yellow.bold(" \u26A0 ") + chalk8.white(message));
|
|
71609
|
+
console.log("");
|
|
71292
71610
|
}
|
|
71293
71611
|
|
|
71294
71612
|
// src/ui/login.ts
|
|
71613
|
+
init_credentials();
|
|
71295
71614
|
var loginGradient = gradient(["#667eea", "#764ba2", "#f093fb"]);
|
|
71296
71615
|
var successGradient = gradient(["#00ff88", "#00d9ff", "#a855f7"]);
|
|
71297
71616
|
gradient(["#ff416c", "#ff4b2b"]);
|
|
@@ -71359,7 +71678,7 @@ async function showLoginScreen() {
|
|
|
71359
71678
|
console.log("");
|
|
71360
71679
|
console.log(chalk8.dim(" \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550"));
|
|
71361
71680
|
console.log("");
|
|
71362
|
-
const authMethod = await
|
|
71681
|
+
const authMethod = await p4.select({
|
|
71363
71682
|
message: chalk8.cyan("How would you like to authenticate?"),
|
|
71364
71683
|
options: [
|
|
71365
71684
|
{
|
|
@@ -71399,7 +71718,7 @@ async function showLoginScreen() {
|
|
|
71399
71718
|
}
|
|
71400
71719
|
]
|
|
71401
71720
|
});
|
|
71402
|
-
if (
|
|
71721
|
+
if (p4.isCancel(authMethod) || authMethod === "cancel") {
|
|
71403
71722
|
return null;
|
|
71404
71723
|
}
|
|
71405
71724
|
switch (authMethod) {
|
|
@@ -71426,7 +71745,7 @@ async function apiKeyLogin() {
|
|
|
71426
71745
|
console.log(" " + chalk8.cyan("Get your API key from:"));
|
|
71427
71746
|
console.log(" " + chalk8.white("https://app.vibecheckai.dev/api-keys"));
|
|
71428
71747
|
console.log("");
|
|
71429
|
-
const apiKey = await
|
|
71748
|
+
const apiKey = await p4.password({
|
|
71430
71749
|
message: chalk8.cyan("\u{1F511} Paste your API key"),
|
|
71431
71750
|
validate: (value) => {
|
|
71432
71751
|
if (!value) return "API key is required";
|
|
@@ -71434,18 +71753,18 @@ async function apiKeyLogin() {
|
|
|
71434
71753
|
return void 0;
|
|
71435
71754
|
}
|
|
71436
71755
|
});
|
|
71437
|
-
if (
|
|
71438
|
-
const s =
|
|
71756
|
+
if (p4.isCancel(apiKey)) return null;
|
|
71757
|
+
const s = p4.spinner();
|
|
71439
71758
|
s.start(chalk8.cyan("Validating API key..."));
|
|
71440
71759
|
const result = await loginWithApiKey(apiKey);
|
|
71441
71760
|
if (!result.success) {
|
|
71442
71761
|
s.stop(chalk8.red(`\u2717 ${result.error}`));
|
|
71443
71762
|
console.log("");
|
|
71444
|
-
const retry = await
|
|
71763
|
+
const retry = await p4.confirm({
|
|
71445
71764
|
message: chalk8.cyan("Would you like to try again?"),
|
|
71446
71765
|
initialValue: true
|
|
71447
71766
|
});
|
|
71448
|
-
if (
|
|
71767
|
+
if (p4.isCancel(retry) || !retry) return null;
|
|
71449
71768
|
return apiKeyLogin();
|
|
71450
71769
|
}
|
|
71451
71770
|
s.stop(chalk8.green("\u2713 API key validated!"));
|
|
@@ -71462,7 +71781,7 @@ async function emailLogin() {
|
|
|
71462
71781
|
console.log("");
|
|
71463
71782
|
console.log(chalk8.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
71464
71783
|
console.log("");
|
|
71465
|
-
const email = await
|
|
71784
|
+
const email = await p4.text({
|
|
71466
71785
|
message: chalk8.cyan("\u{1F4E7} Email address"),
|
|
71467
71786
|
placeholder: "you@example.com",
|
|
71468
71787
|
validate: (value) => {
|
|
@@ -71471,8 +71790,8 @@ async function emailLogin() {
|
|
|
71471
71790
|
return void 0;
|
|
71472
71791
|
}
|
|
71473
71792
|
});
|
|
71474
|
-
if (
|
|
71475
|
-
const password2 = await
|
|
71793
|
+
if (p4.isCancel(email)) return null;
|
|
71794
|
+
const password2 = await p4.password({
|
|
71476
71795
|
message: chalk8.cyan("\u{1F511} Password"),
|
|
71477
71796
|
validate: (value) => {
|
|
71478
71797
|
if (!value) return "Password is required";
|
|
@@ -71480,49 +71799,103 @@ async function emailLogin() {
|
|
|
71480
71799
|
return void 0;
|
|
71481
71800
|
}
|
|
71482
71801
|
});
|
|
71483
|
-
if (
|
|
71802
|
+
if (p4.isCancel(password2)) return null;
|
|
71803
|
+
const s = p4.spinner();
|
|
71804
|
+
s.start(chalk8.cyan("Authenticating..."));
|
|
71805
|
+
const result = await loginWithEmailPassword(email, password2);
|
|
71806
|
+
if (!result.success) {
|
|
71807
|
+
s.stop(chalk8.red(`\u2717 ${result.error}`));
|
|
71808
|
+
console.log("");
|
|
71809
|
+
const retry = await p4.confirm({
|
|
71810
|
+
message: chalk8.cyan("Would you like to try again?"),
|
|
71811
|
+
initialValue: true
|
|
71812
|
+
});
|
|
71813
|
+
if (p4.isCancel(retry) || !retry) return null;
|
|
71814
|
+
return emailLogin();
|
|
71815
|
+
}
|
|
71816
|
+
s.stop(chalk8.green("\u2713 Authentication successful!"));
|
|
71484
71817
|
console.log("");
|
|
71485
71818
|
await showSecurityAnimation();
|
|
71819
|
+
const user = result.user;
|
|
71486
71820
|
return showLoginSuccess({
|
|
71487
|
-
email,
|
|
71488
|
-
name: email.split("@")[0],
|
|
71489
|
-
tier: "
|
|
71821
|
+
email: user.email ?? email,
|
|
71822
|
+
name: user.name ?? email.split("@")[0],
|
|
71823
|
+
tier: user.tier ?? "free"
|
|
71490
71824
|
});
|
|
71491
71825
|
}
|
|
71492
71826
|
async function oauthLogin(provider) {
|
|
71493
71827
|
console.log("");
|
|
71494
71828
|
console.log(chalk8.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
71495
71829
|
console.log("");
|
|
71496
|
-
|
|
71497
|
-
|
|
71498
|
-
|
|
71830
|
+
console.log(chalk8.cyan(` ${provider} OAuth for CLI:`));
|
|
71831
|
+
console.log("");
|
|
71832
|
+
console.log(chalk8.white(" 1. We'll open your browser to sign in with " + provider));
|
|
71833
|
+
console.log(chalk8.white(" 2. After signing in, go to Settings \u2192 API Keys"));
|
|
71834
|
+
console.log(chalk8.white(" 3. Create an API key and paste it here"));
|
|
71835
|
+
console.log("");
|
|
71836
|
+
const proceed = await p4.confirm({
|
|
71837
|
+
message: chalk8.cyan(`Open browser to sign in with ${provider}?`),
|
|
71838
|
+
initialValue: true
|
|
71839
|
+
});
|
|
71840
|
+
if (p4.isCancel(proceed) || !proceed) return null;
|
|
71841
|
+
const s = p4.spinner();
|
|
71842
|
+
s.start(chalk8.cyan(`Opening ${provider} login page...`));
|
|
71843
|
+
try {
|
|
71844
|
+
const open = (await import('open')).default;
|
|
71845
|
+
const webUrl = process.env.VIBECHECK_WEB_URL ?? "https://app.vibecheckai.dev";
|
|
71846
|
+
const providerLower = provider.toLowerCase();
|
|
71847
|
+
await open(`${webUrl}/auth/login?provider=${providerLower}`);
|
|
71848
|
+
s.stop(chalk8.green(`\u2713 Browser opened!`));
|
|
71849
|
+
} catch {
|
|
71850
|
+
s.stop(chalk8.yellow("\u26A0 Could not open browser automatically"));
|
|
71851
|
+
console.log("");
|
|
71852
|
+
console.log(chalk8.white(" Please visit: https://app.vibecheckai.dev/auth/login"));
|
|
71853
|
+
}
|
|
71854
|
+
console.log("");
|
|
71855
|
+
console.log(chalk8.dim(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
|
|
71856
|
+
console.log(chalk8.dim(" \u2502") + chalk8.white(" Complete sign-in in your browser, then: ") + chalk8.dim("\u2502"));
|
|
71857
|
+
console.log(chalk8.dim(" \u2502") + chalk8.white(" 1. Go to Settings \u2192 API Keys ") + chalk8.dim("\u2502"));
|
|
71858
|
+
console.log(chalk8.dim(" \u2502") + chalk8.white(" 2. Create a new API key ") + chalk8.dim("\u2502"));
|
|
71859
|
+
console.log(chalk8.dim(" \u2502") + chalk8.white(" 3. Copy and paste it below ") + chalk8.dim("\u2502"));
|
|
71860
|
+
console.log(chalk8.dim(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
|
|
71861
|
+
console.log("");
|
|
71862
|
+
const apiKey = await p4.password({
|
|
71863
|
+
message: chalk8.cyan("\u{1F511} Paste your API key"),
|
|
71499
71864
|
validate: (value) => {
|
|
71500
|
-
if (!value) return "
|
|
71501
|
-
if (
|
|
71865
|
+
if (!value) return "API key is required";
|
|
71866
|
+
if (value.length < 20) return "Invalid API key format";
|
|
71502
71867
|
return void 0;
|
|
71503
71868
|
}
|
|
71504
71869
|
});
|
|
71505
|
-
if (
|
|
71506
|
-
const
|
|
71507
|
-
|
|
71508
|
-
|
|
71509
|
-
|
|
71510
|
-
|
|
71511
|
-
|
|
71870
|
+
if (p4.isCancel(apiKey)) return null;
|
|
71871
|
+
const s2 = p4.spinner();
|
|
71872
|
+
s2.start(chalk8.cyan("Validating API key..."));
|
|
71873
|
+
const result = await loginWithApiKey(apiKey);
|
|
71874
|
+
if (!result.success) {
|
|
71875
|
+
s2.stop(chalk8.red(`\u2717 ${result.error}`));
|
|
71876
|
+
console.log("");
|
|
71877
|
+
const retry = await p4.confirm({
|
|
71878
|
+
message: chalk8.cyan("Would you like to try again?"),
|
|
71879
|
+
initialValue: true
|
|
71880
|
+
});
|
|
71881
|
+
if (p4.isCancel(retry) || !retry) return null;
|
|
71882
|
+
return oauthLogin(provider);
|
|
71883
|
+
}
|
|
71884
|
+
s2.stop(chalk8.green("\u2713 API key validated!"));
|
|
71512
71885
|
console.log("");
|
|
71513
71886
|
await showSecurityAnimation();
|
|
71514
|
-
const
|
|
71887
|
+
const user = result.user;
|
|
71515
71888
|
return showLoginSuccess({
|
|
71516
|
-
email,
|
|
71517
|
-
name:
|
|
71518
|
-
tier: "
|
|
71889
|
+
email: user.email ?? "user@vibecheck.dev",
|
|
71890
|
+
name: user.name ?? user.email?.split("@")[0] ?? "User",
|
|
71891
|
+
tier: user.tier ?? "free"
|
|
71519
71892
|
});
|
|
71520
71893
|
}
|
|
71521
71894
|
async function magicLinkLogin() {
|
|
71522
71895
|
console.log("");
|
|
71523
71896
|
console.log(chalk8.dim(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
71524
71897
|
console.log("");
|
|
71525
|
-
const email = await
|
|
71898
|
+
const email = await p4.text({
|
|
71526
71899
|
message: chalk8.cyan("\u{1F4E7} Enter your email for a magic link"),
|
|
71527
71900
|
placeholder: "you@example.com",
|
|
71528
71901
|
validate: (value) => {
|
|
@@ -71531,27 +71904,60 @@ async function magicLinkLogin() {
|
|
|
71531
71904
|
return void 0;
|
|
71532
71905
|
}
|
|
71533
71906
|
});
|
|
71534
|
-
if (
|
|
71535
|
-
const s =
|
|
71907
|
+
if (p4.isCancel(email)) return null;
|
|
71908
|
+
const s = p4.spinner();
|
|
71536
71909
|
s.start(chalk8.cyan("Sending magic link..."));
|
|
71537
|
-
|
|
71910
|
+
const result = await requestMagicLink(email);
|
|
71911
|
+
if (!result.success) {
|
|
71912
|
+
s.stop(chalk8.red(`\u2717 ${result.error}`));
|
|
71913
|
+
console.log("");
|
|
71914
|
+
const retry = await p4.confirm({
|
|
71915
|
+
message: chalk8.cyan("Would you like to try again?"),
|
|
71916
|
+
initialValue: true
|
|
71917
|
+
});
|
|
71918
|
+
if (p4.isCancel(retry) || !retry) return null;
|
|
71919
|
+
return magicLinkLogin();
|
|
71920
|
+
}
|
|
71538
71921
|
s.stop(chalk8.green("\u2713 Magic link sent!"));
|
|
71539
71922
|
console.log("");
|
|
71540
71923
|
console.log(chalk8.dim(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
|
|
71541
71924
|
console.log(chalk8.dim(" \u2502") + chalk8.white(" \u{1F4EC} Check your inbox! ") + chalk8.dim("\u2502"));
|
|
71542
71925
|
console.log(chalk8.dim(" \u2502") + chalk8.dim(` We sent a login link to ${email}`) + " ".repeat(Math.max(0, 26 - email.length)) + chalk8.dim("\u2502"));
|
|
71543
|
-
console.log(chalk8.dim(" \u2502") + chalk8.
|
|
71926
|
+
console.log(chalk8.dim(" \u2502") + chalk8.white(" ") + chalk8.dim("\u2502"));
|
|
71927
|
+
console.log(chalk8.dim(" \u2502") + chalk8.white(" Click the link in your email, then paste the ") + chalk8.dim("\u2502"));
|
|
71928
|
+
console.log(chalk8.dim(" \u2502") + chalk8.white(" verification code below. ") + chalk8.dim("\u2502"));
|
|
71544
71929
|
console.log(chalk8.dim(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
|
|
71545
71930
|
console.log("");
|
|
71546
|
-
const
|
|
71547
|
-
|
|
71548
|
-
|
|
71549
|
-
|
|
71931
|
+
const token = await p4.text({
|
|
71932
|
+
message: chalk8.cyan("\u{1F511} Paste the verification code from the email"),
|
|
71933
|
+
placeholder: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
71934
|
+
validate: (value) => {
|
|
71935
|
+
if (!value) return "Verification code is required";
|
|
71936
|
+
if (value.length < 20) return "Invalid verification code";
|
|
71937
|
+
return void 0;
|
|
71938
|
+
}
|
|
71939
|
+
});
|
|
71940
|
+
if (p4.isCancel(token)) return null;
|
|
71941
|
+
const s2 = p4.spinner();
|
|
71942
|
+
s2.start(chalk8.cyan("Verifying..."));
|
|
71943
|
+
const verifyResult = await verifyMagicLink(token);
|
|
71944
|
+
if (!verifyResult.success) {
|
|
71945
|
+
s2.stop(chalk8.red(`\u2717 ${verifyResult.error}`));
|
|
71946
|
+
console.log("");
|
|
71947
|
+
const retry = await p4.confirm({
|
|
71948
|
+
message: chalk8.cyan("Would you like to try again?"),
|
|
71949
|
+
initialValue: true
|
|
71950
|
+
});
|
|
71951
|
+
if (p4.isCancel(retry) || !retry) return null;
|
|
71952
|
+
return magicLinkLogin();
|
|
71953
|
+
}
|
|
71954
|
+
s2.stop(chalk8.green("\u2713 Verification successful!"));
|
|
71550
71955
|
await showSecurityAnimation();
|
|
71956
|
+
const user = verifyResult.user;
|
|
71551
71957
|
return showLoginSuccess({
|
|
71552
|
-
email,
|
|
71553
|
-
name: email.split("@")[0],
|
|
71554
|
-
tier: "free"
|
|
71958
|
+
email: user.email ?? email,
|
|
71959
|
+
name: user.name ?? email.split("@")[0],
|
|
71960
|
+
tier: user.tier ?? "free"
|
|
71555
71961
|
});
|
|
71556
71962
|
}
|
|
71557
71963
|
async function signupFlow() {
|
|
@@ -71571,7 +71977,7 @@ async function signupFlow() {
|
|
|
71571
71977
|
console.log(" " + chalk8.white.bold("Create Your VibeCheck Account"));
|
|
71572
71978
|
console.log(" " + chalk8.dim("Join thousands of developers preventing AI hallucinations"));
|
|
71573
71979
|
console.log("");
|
|
71574
|
-
const name = await
|
|
71980
|
+
const name = await p4.text({
|
|
71575
71981
|
message: chalk8.cyan("\u{1F464} What should we call you?"),
|
|
71576
71982
|
placeholder: "Your name",
|
|
71577
71983
|
validate: (value) => {
|
|
@@ -71579,8 +71985,8 @@ async function signupFlow() {
|
|
|
71579
71985
|
return void 0;
|
|
71580
71986
|
}
|
|
71581
71987
|
});
|
|
71582
|
-
if (
|
|
71583
|
-
const email = await
|
|
71988
|
+
if (p4.isCancel(name)) return null;
|
|
71989
|
+
const email = await p4.text({
|
|
71584
71990
|
message: chalk8.cyan("\u{1F4E7} Email address"),
|
|
71585
71991
|
placeholder: "you@example.com",
|
|
71586
71992
|
validate: (value) => {
|
|
@@ -71589,8 +71995,8 @@ async function signupFlow() {
|
|
|
71589
71995
|
return void 0;
|
|
71590
71996
|
}
|
|
71591
71997
|
});
|
|
71592
|
-
if (
|
|
71593
|
-
const password2 = await
|
|
71998
|
+
if (p4.isCancel(email)) return null;
|
|
71999
|
+
const password2 = await p4.password({
|
|
71594
72000
|
message: chalk8.cyan("\u{1F511} Create a password"),
|
|
71595
72001
|
validate: (value) => {
|
|
71596
72002
|
if (!value) return "Password is required";
|
|
@@ -71600,20 +72006,20 @@ async function signupFlow() {
|
|
|
71600
72006
|
return void 0;
|
|
71601
72007
|
}
|
|
71602
72008
|
});
|
|
71603
|
-
if (
|
|
72009
|
+
if (p4.isCancel(password2)) return null;
|
|
71604
72010
|
const strength = getPasswordStrength(password2);
|
|
71605
72011
|
console.log("");
|
|
71606
72012
|
console.log(" " + chalk8.dim("Password Strength:") + " " + strength.indicator);
|
|
71607
|
-
const confirmPassword = await
|
|
72013
|
+
const confirmPassword = await p4.password({
|
|
71608
72014
|
message: chalk8.cyan("\u{1F511} Confirm password"),
|
|
71609
72015
|
validate: (value) => {
|
|
71610
72016
|
if (value !== password2) return "Passwords do not match";
|
|
71611
72017
|
return void 0;
|
|
71612
72018
|
}
|
|
71613
72019
|
});
|
|
71614
|
-
if (
|
|
72020
|
+
if (p4.isCancel(confirmPassword)) return null;
|
|
71615
72021
|
console.log("");
|
|
71616
|
-
const tier = await
|
|
72022
|
+
const tier = await p4.select({
|
|
71617
72023
|
message: chalk8.cyan("Choose your plan"),
|
|
71618
72024
|
options: [
|
|
71619
72025
|
{
|
|
@@ -71633,12 +72039,12 @@ async function signupFlow() {
|
|
|
71633
72039
|
}
|
|
71634
72040
|
]
|
|
71635
72041
|
});
|
|
71636
|
-
if (
|
|
71637
|
-
const terms = await
|
|
72042
|
+
if (p4.isCancel(tier)) return null;
|
|
72043
|
+
const terms = await p4.confirm({
|
|
71638
72044
|
message: chalk8.cyan("I agree to the Terms of Service and Privacy Policy"),
|
|
71639
72045
|
initialValue: false
|
|
71640
72046
|
});
|
|
71641
|
-
if (
|
|
72047
|
+
if (p4.isCancel(terms) || !terms) {
|
|
71642
72048
|
console.log("");
|
|
71643
72049
|
console.log(chalk8.yellow(" \u26A0 You must accept the terms to create an account."));
|
|
71644
72050
|
return null;
|
|
@@ -71741,6 +72147,7 @@ init_dist();
|
|
|
71741
72147
|
|
|
71742
72148
|
// src/lib/entitlements.ts
|
|
71743
72149
|
init_dist();
|
|
72150
|
+
init_credentials();
|
|
71744
72151
|
var currentSession = {
|
|
71745
72152
|
tier: "free",
|
|
71746
72153
|
authenticated: false
|
|
@@ -72954,6 +73361,7 @@ async function integrateWithCI(options) {
|
|
|
72954
73361
|
|
|
72955
73362
|
// src/lib/reality-uploader.ts
|
|
72956
73363
|
init_dist2();
|
|
73364
|
+
init_credentials();
|
|
72957
73365
|
async function uploadVideoArtifacts(artifactsDir, projectId, checkId) {
|
|
72958
73366
|
try {
|
|
72959
73367
|
const storageService = createVideoStorageService();
|
|
@@ -73080,6 +73488,9 @@ async function isApiUploadConfigured() {
|
|
|
73080
73488
|
return await isLoggedIn();
|
|
73081
73489
|
}
|
|
73082
73490
|
|
|
73491
|
+
// src/lib/index.ts
|
|
73492
|
+
init_credentials();
|
|
73493
|
+
|
|
73083
73494
|
// src/commands/scan.ts
|
|
73084
73495
|
init_truthpack();
|
|
73085
73496
|
init_utils();
|
|
@@ -73354,7 +73765,6 @@ async function scanCommand(options) {
|
|
|
73354
73765
|
if (env.isInteractive && !options.json && !options.quiet) {
|
|
73355
73766
|
renderCommandHeader({
|
|
73356
73767
|
command: "scan",
|
|
73357
|
-
version: "1.0.0",
|
|
73358
73768
|
target: process.cwd(),
|
|
73359
73769
|
elapsedTime: results.duration,
|
|
73360
73770
|
vitals: [
|
|
@@ -73421,7 +73831,6 @@ async function scanCommand(options) {
|
|
|
73421
73831
|
if (options.json) {
|
|
73422
73832
|
const commandResult = {
|
|
73423
73833
|
commandName: "scan",
|
|
73424
|
-
version: "1.0.0",
|
|
73425
73834
|
repoRoot: process.cwd(),
|
|
73426
73835
|
startedAt: new Date(startTime).toISOString(),
|
|
73427
73836
|
durationMs: results.duration,
|
|
@@ -73605,7 +74014,6 @@ async function validateCommand(files, options) {
|
|
|
73605
74014
|
if (env.isInteractive && !options.json && !options.quiet) {
|
|
73606
74015
|
renderCommandHeader({
|
|
73607
74016
|
command: "validate",
|
|
73608
|
-
version: "1.0.0",
|
|
73609
74017
|
target: process.cwd(),
|
|
73610
74018
|
elapsedTime: 0
|
|
73611
74019
|
});
|
|
@@ -73718,7 +74126,6 @@ async function validateCommand(files, options) {
|
|
|
73718
74126
|
const warningScore = calculateHealthScore({ ...createEmptySeverityCounts(), medium: summary.warnings });
|
|
73719
74127
|
renderCommandHeader({
|
|
73720
74128
|
command: "validate",
|
|
73721
|
-
version: "1.0.0",
|
|
73722
74129
|
target: process.cwd(),
|
|
73723
74130
|
elapsedTime: summary.duration,
|
|
73724
74131
|
vitals: [
|
|
@@ -73752,7 +74159,6 @@ async function validateCommand(files, options) {
|
|
|
73752
74159
|
};
|
|
73753
74160
|
const commandResult = {
|
|
73754
74161
|
commandName: "validate",
|
|
73755
|
-
version: "1.0.0",
|
|
73756
74162
|
repoRoot: process.cwd(),
|
|
73757
74163
|
startedAt: new Date(startTime).toISOString(),
|
|
73758
74164
|
durationMs: summary.duration,
|
|
@@ -73863,7 +74269,6 @@ async function checkCommand(options) {
|
|
|
73863
74269
|
if (env.isInteractive && !options.json && !options.quiet) {
|
|
73864
74270
|
renderCommandHeader({
|
|
73865
74271
|
command: "check",
|
|
73866
|
-
version: "1.0.0",
|
|
73867
74272
|
target: process.cwd(),
|
|
73868
74273
|
elapsedTime: 0
|
|
73869
74274
|
});
|
|
@@ -73997,7 +74402,6 @@ async function checkCommand(options) {
|
|
|
73997
74402
|
const driftScore = calculateHealthScore({ ...createEmptySeverityCounts(), medium: driftCount });
|
|
73998
74403
|
renderCommandHeader({
|
|
73999
74404
|
command: "check",
|
|
74000
|
-
version: "1.0.0",
|
|
74001
74405
|
target: process.cwd(),
|
|
74002
74406
|
elapsedTime: result.duration,
|
|
74003
74407
|
vitals: [
|
|
@@ -74049,7 +74453,6 @@ async function checkCommand(options) {
|
|
|
74049
74453
|
};
|
|
74050
74454
|
const commandResult = {
|
|
74051
74455
|
commandName: "check",
|
|
74052
|
-
version: "1.0.0",
|
|
74053
74456
|
repoRoot: process.cwd(),
|
|
74054
74457
|
startedAt: new Date(startTime).toISOString(),
|
|
74055
74458
|
durationMs: result.duration,
|
|
@@ -74210,20 +74613,38 @@ function renderScoreBar2(score, maxScore = 20, width = 10) {
|
|
|
74210
74613
|
function renderShipScoreBox(score, options = {}) {
|
|
74211
74614
|
const env2 = getEnvironment();
|
|
74212
74615
|
const width = options.width ?? Math.min(60, getSafeTerminalWidth(80));
|
|
74213
|
-
const
|
|
74214
|
-
const
|
|
74616
|
+
const verdict = score?.verdict ?? "WARN";
|
|
74617
|
+
const verdictColor = VERDICT_COLORS3[verdict] ?? chalk8.yellow;
|
|
74618
|
+
const verdictIcon = env2.terminal.unicode ? VERDICT_ICONS2[verdict] ?? "" : "";
|
|
74215
74619
|
const lines = [];
|
|
74216
|
-
const
|
|
74217
|
-
const
|
|
74620
|
+
const totalScore = score?.total ?? 0;
|
|
74621
|
+
const scoreText = `Ship Score: ${totalScore}/100`;
|
|
74622
|
+
const verdictText = `${verdictIcon} ${verdict}`;
|
|
74218
74623
|
lines.push(` ${chalk8.bold(scoreText)} ${verdictColor(verdictText)}`);
|
|
74219
74624
|
lines.push("");
|
|
74220
|
-
const dimensions = score
|
|
74625
|
+
const dimensions = score?.dimensions ?? {};
|
|
74221
74626
|
const dimEntries = Object.entries(dimensions);
|
|
74222
|
-
|
|
74223
|
-
const
|
|
74224
|
-
|
|
74225
|
-
|
|
74226
|
-
|
|
74627
|
+
if (dimEntries.length === 0) {
|
|
74628
|
+
const defaultDimensions = [
|
|
74629
|
+
["ghostRisk", 0],
|
|
74630
|
+
["authCoverage", 0],
|
|
74631
|
+
["envIntegrity", 0],
|
|
74632
|
+
["runtimeProof", 0],
|
|
74633
|
+
["contractsAlignment", 0]
|
|
74634
|
+
];
|
|
74635
|
+
for (const [key, value] of defaultDimensions) {
|
|
74636
|
+
const label = (DIMENSION_LABELS[key] ?? key).padEnd(16);
|
|
74637
|
+
const scoreStr = `${value}/20`.padStart(5);
|
|
74638
|
+
const bar = renderScoreBar2(value, 20, 10);
|
|
74639
|
+
lines.push(` ${chalk8.dim(label)} ${scoreStr} ${bar}`);
|
|
74640
|
+
}
|
|
74641
|
+
} else {
|
|
74642
|
+
for (const [key, value] of dimEntries) {
|
|
74643
|
+
const label = (DIMENSION_LABELS[key] ?? key).padEnd(16);
|
|
74644
|
+
const scoreStr = `${value ?? 0}/20`.padStart(5);
|
|
74645
|
+
const bar = renderScoreBar2(value ?? 0, 20, 10);
|
|
74646
|
+
lines.push(` ${chalk8.dim(label)} ${scoreStr} ${bar}`);
|
|
74647
|
+
}
|
|
74227
74648
|
}
|
|
74228
74649
|
const boxContent = lines.join("\n");
|
|
74229
74650
|
if (env2.terminal.unicode) {
|
|
@@ -74333,7 +74754,7 @@ async function initCommand(options) {
|
|
|
74333
74754
|
if (env.isInteractive && !options.json) {
|
|
74334
74755
|
renderCommandHeader({
|
|
74335
74756
|
command: "init",
|
|
74336
|
-
version:
|
|
74757
|
+
version: CLI_VERSION,
|
|
74337
74758
|
target: process.cwd(),
|
|
74338
74759
|
elapsedTime: 0
|
|
74339
74760
|
});
|
|
@@ -74528,7 +74949,7 @@ async function initCommand(options) {
|
|
|
74528
74949
|
if (env.isInteractive && !options.json) {
|
|
74529
74950
|
renderCommandHeader({
|
|
74530
74951
|
command: "init",
|
|
74531
|
-
version:
|
|
74952
|
+
version: CLI_VERSION,
|
|
74532
74953
|
target: process.cwd(),
|
|
74533
74954
|
elapsedTime: duration,
|
|
74534
74955
|
vitals: [
|
|
@@ -74793,8 +75214,9 @@ async function configCommand(options) {
|
|
|
74793
75214
|
}
|
|
74794
75215
|
const changes = await runConfigWizard(config);
|
|
74795
75216
|
if (changes) {
|
|
74796
|
-
logger2.info("Configuration changes
|
|
74797
|
-
logger2.dim("
|
|
75217
|
+
logger2.info("Configuration changes detected.");
|
|
75218
|
+
logger2.dim("Use --set to apply changes: vibecheck config --set key=value");
|
|
75219
|
+
logger2.dim("Example: vibecheck config --set strict=true");
|
|
74798
75220
|
}
|
|
74799
75221
|
}
|
|
74800
75222
|
} catch (error) {
|
|
@@ -74984,7 +75406,6 @@ async function watchCommand(options) {
|
|
|
74984
75406
|
if (env.isInteractive && !options.json && !options.quiet) {
|
|
74985
75407
|
renderCommandHeader({
|
|
74986
75408
|
command: "watch",
|
|
74987
|
-
version: "1.0.0",
|
|
74988
75409
|
target: process.cwd(),
|
|
74989
75410
|
elapsedTime: 0,
|
|
74990
75411
|
vitals: [
|
|
@@ -75499,7 +75920,7 @@ ${fix.description}`));
|
|
|
75499
75920
|
console.log(chalk8.yellow("Diff:"));
|
|
75500
75921
|
console.log(patchGenerator.formatAsUnifiedDiff(fix.patch));
|
|
75501
75922
|
console.log();
|
|
75502
|
-
const action = await
|
|
75923
|
+
const action = await p4.select({
|
|
75503
75924
|
message: "What would you like to do?",
|
|
75504
75925
|
options: [
|
|
75505
75926
|
{ value: "approve", label: "Approve and apply this fix" },
|
|
@@ -75507,7 +75928,7 @@ ${fix.description}`));
|
|
|
75507
75928
|
{ value: "skip", label: "Skip for now" }
|
|
75508
75929
|
]
|
|
75509
75930
|
});
|
|
75510
|
-
if (
|
|
75931
|
+
if (p4.isCancel(action)) {
|
|
75511
75932
|
logger2.warn("Review cancelled");
|
|
75512
75933
|
break;
|
|
75513
75934
|
}
|
|
@@ -75576,10 +75997,10 @@ async function handleRollback(transactionId, logger2, options) {
|
|
|
75576
75997
|
return;
|
|
75577
75998
|
}
|
|
75578
75999
|
if (env.isInteractive && !options.json && !options.quiet) {
|
|
75579
|
-
const confirm6 = await
|
|
76000
|
+
const confirm6 = await p4.confirm({
|
|
75580
76001
|
message: `Rollback ${transaction.fixes.length} file(s)?`
|
|
75581
76002
|
});
|
|
75582
|
-
if (
|
|
76003
|
+
if (p4.isCancel(confirm6) || !confirm6) {
|
|
75583
76004
|
logger2.warn("Rollback cancelled");
|
|
75584
76005
|
return;
|
|
75585
76006
|
}
|
|
@@ -75723,7 +76144,6 @@ async function shipCommand(options) {
|
|
|
75723
76144
|
if (env.isInteractive && !options.json && !options.quiet) {
|
|
75724
76145
|
renderCommandHeader({
|
|
75725
76146
|
command: "ship",
|
|
75726
|
-
version: "1.0.0",
|
|
75727
76147
|
target: projectRoot,
|
|
75728
76148
|
elapsedTime: 0,
|
|
75729
76149
|
vitals: [
|
|
@@ -76830,7 +77250,6 @@ function printResults2(result, logger2, isForced) {
|
|
|
76830
77250
|
} : void 0;
|
|
76831
77251
|
renderCommandHeader({
|
|
76832
77252
|
command: result.ready || isForced ? "ship" : "no ship",
|
|
76833
|
-
version: "1.0.0",
|
|
76834
77253
|
target: process.cwd(),
|
|
76835
77254
|
elapsedTime: result.duration,
|
|
76836
77255
|
vitals,
|
|
@@ -76995,7 +77414,6 @@ async function doctorCommand(options) {
|
|
|
76995
77414
|
if (!options.json) {
|
|
76996
77415
|
renderCommandHeader({
|
|
76997
77416
|
command: "doctor",
|
|
76998
|
-
version: "1.0.0",
|
|
76999
77417
|
target: process.cwd(),
|
|
77000
77418
|
elapsedTime: 0
|
|
77001
77419
|
});
|
|
@@ -77061,7 +77479,6 @@ async function doctorCommand(options) {
|
|
|
77061
77479
|
} : void 0;
|
|
77062
77480
|
renderCommandHeader({
|
|
77063
77481
|
command: "doctor",
|
|
77064
|
-
version: "1.0.0",
|
|
77065
77482
|
target: process.cwd(),
|
|
77066
77483
|
elapsedTime: duration,
|
|
77067
77484
|
vitals: [
|
|
@@ -77144,7 +77561,6 @@ async function doctorCommand(options) {
|
|
|
77144
77561
|
} : void 0;
|
|
77145
77562
|
renderCommandHeader({
|
|
77146
77563
|
command: "doctor",
|
|
77147
|
-
version: "1.0.0",
|
|
77148
77564
|
target: process.cwd(),
|
|
77149
77565
|
elapsedTime: duration,
|
|
77150
77566
|
vitals: [
|
|
@@ -77248,7 +77664,6 @@ async function reportCommand(options) {
|
|
|
77248
77664
|
if (env.isInteractive && !options.json && !options.quiet) {
|
|
77249
77665
|
renderCommandHeader({
|
|
77250
77666
|
command: "report",
|
|
77251
|
-
version: "1.0.0",
|
|
77252
77667
|
target: projectRoot,
|
|
77253
77668
|
elapsedTime: 0,
|
|
77254
77669
|
vitals: [
|
|
@@ -77284,8 +77699,7 @@ async function reportCommand(options) {
|
|
|
77284
77699
|
title: "Transforming data for report",
|
|
77285
77700
|
task: async (ctx2) => {
|
|
77286
77701
|
ctx2.reportData = transformToEnterpriseData(ctx2.scanInput, {
|
|
77287
|
-
reportType
|
|
77288
|
-
version: "1.0.0"
|
|
77702
|
+
reportType
|
|
77289
77703
|
});
|
|
77290
77704
|
}
|
|
77291
77705
|
},
|
|
@@ -77370,8 +77784,7 @@ async function reportCommand(options) {
|
|
|
77370
77784
|
scanInput = await collectScanData(projectRoot, config);
|
|
77371
77785
|
logger2.step("Transforming data...");
|
|
77372
77786
|
const reportData = transformToEnterpriseData(scanInput, {
|
|
77373
|
-
reportType
|
|
77374
|
-
version: "1.0.0"
|
|
77787
|
+
reportType
|
|
77375
77788
|
});
|
|
77376
77789
|
logger2.step(`Generating ${format.toUpperCase()} report...`);
|
|
77377
77790
|
const reportConfig = {
|
|
@@ -77458,7 +77871,6 @@ async function reportCommand(options) {
|
|
|
77458
77871
|
if (env.isInteractive && !options.quiet) {
|
|
77459
77872
|
renderCommandHeader({
|
|
77460
77873
|
command: "report",
|
|
77461
|
-
version: "1.0.0",
|
|
77462
77874
|
target: projectRoot,
|
|
77463
77875
|
elapsedTime: result.duration,
|
|
77464
77876
|
vitals: [
|
|
@@ -77614,8 +78026,8 @@ function buildCommandString(options) {
|
|
|
77614
78026
|
init_config();
|
|
77615
78027
|
init_errors();
|
|
77616
78028
|
var pkg = {
|
|
77617
|
-
name:
|
|
77618
|
-
version:
|
|
78029
|
+
name: CLI_NAME,
|
|
78030
|
+
version: CLI_VERSION
|
|
77619
78031
|
};
|
|
77620
78032
|
var isShuttingDown = false;
|
|
77621
78033
|
var cleanupCallbacks = [];
|
|
@@ -77875,6 +78287,13 @@ program2.command("login").description("Login to VibeCheck Cloud").action(async (
|
|
|
77875
78287
|
}
|
|
77876
78288
|
});
|
|
77877
78289
|
program2.command("logout").description("Logout from VibeCheck Cloud").action(async () => {
|
|
78290
|
+
const { clearCredentials: clearCredentials2, hasCredentials: hasCredentials2 } = await Promise.resolve().then(() => (init_credentials(), credentials_exports));
|
|
78291
|
+
const wasLoggedIn = await hasCredentials2();
|
|
78292
|
+
if (!wasLoggedIn) {
|
|
78293
|
+
printWarning("You are not currently logged in.");
|
|
78294
|
+
return;
|
|
78295
|
+
}
|
|
78296
|
+
await clearCredentials2();
|
|
77878
78297
|
printSuccess("Logged out successfully. See you next time!");
|
|
77879
78298
|
});
|
|
77880
78299
|
program2.helpOption("-h, --help", "Display gorgeous help");
|