estatehelm 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +85 -73
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
27
27
|
var import_commander = require("commander");
|
|
28
28
|
|
|
29
29
|
// src/login.ts
|
|
30
|
+
var http = __toESM(require("http"));
|
|
30
31
|
var readline = __toESM(require("readline"));
|
|
31
32
|
var import_open = __toESM(require("open"));
|
|
32
33
|
|
|
@@ -1312,16 +1313,7 @@ var KEYTAR_ACCOUNTS = {
|
|
|
1312
1313
|
DEVICE_CREDENTIALS: "device-credentials"
|
|
1313
1314
|
};
|
|
1314
1315
|
var API_BASE_URL = process.env.ESTATEHELM_API_URL || "https://api.estatehelm.com";
|
|
1315
|
-
var
|
|
1316
|
-
// OAuth authorization URL (opens in browser)
|
|
1317
|
-
authUrl: `${API_BASE_URL}/.ory/self-service/login/browser`,
|
|
1318
|
-
// OAuth token exchange endpoint
|
|
1319
|
-
tokenUrl: `${API_BASE_URL}/api/v2/auth/device-token`,
|
|
1320
|
-
// Device code flow endpoint
|
|
1321
|
-
deviceCodeUrl: `${API_BASE_URL}/api/v2/auth/device-code`,
|
|
1322
|
-
// Client ID for CLI app
|
|
1323
|
-
clientId: "estatehelm-cli"
|
|
1324
|
-
};
|
|
1316
|
+
var APP_URL = process.env.ESTATEHELM_APP_URL || "https://app.estatehelm.com";
|
|
1325
1317
|
var DEFAULT_CONFIG = {
|
|
1326
1318
|
defaultMode: "full"
|
|
1327
1319
|
};
|
|
@@ -1447,6 +1439,76 @@ function prompt(question) {
|
|
|
1447
1439
|
});
|
|
1448
1440
|
});
|
|
1449
1441
|
}
|
|
1442
|
+
async function findAvailablePort(startPort = 19847) {
|
|
1443
|
+
return new Promise((resolve, reject) => {
|
|
1444
|
+
const server = http.createServer();
|
|
1445
|
+
server.listen(startPort, "127.0.0.1", () => {
|
|
1446
|
+
const address = server.address();
|
|
1447
|
+
const port = typeof address === "object" && address ? address.port : startPort;
|
|
1448
|
+
server.close(() => resolve(port));
|
|
1449
|
+
});
|
|
1450
|
+
server.on("error", (err) => {
|
|
1451
|
+
if (err.code === "EADDRINUSE") {
|
|
1452
|
+
resolve(findAvailablePort(startPort + 1));
|
|
1453
|
+
} else {
|
|
1454
|
+
reject(err);
|
|
1455
|
+
}
|
|
1456
|
+
});
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
function waitForCallback(port) {
|
|
1460
|
+
return new Promise((resolve, reject) => {
|
|
1461
|
+
const server = http.createServer((req, res) => {
|
|
1462
|
+
const url = new URL(req.url || "/", `http://127.0.0.1:${port}`);
|
|
1463
|
+
const sessionToken = url.searchParams.get("session_token");
|
|
1464
|
+
if (sessionToken) {
|
|
1465
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1466
|
+
res.end(`
|
|
1467
|
+
<!DOCTYPE html>
|
|
1468
|
+
<html>
|
|
1469
|
+
<head>
|
|
1470
|
+
<title>CLI Authentication Successful</title>
|
|
1471
|
+
<style>
|
|
1472
|
+
body { font-family: system-ui, sans-serif; display: flex; justify-content: center; align-items: center; height: 100vh; margin: 0; background: linear-gradient(115deg, #fff1be 28%, #ee87cb 70%, #b060ff 100%); }
|
|
1473
|
+
.card { background: white; padding: 2rem; border-radius: 1rem; box-shadow: 0 10px 25px rgba(0,0,0,0.1); text-align: center; }
|
|
1474
|
+
h1 { color: #059669; margin: 0 0 0.5rem; }
|
|
1475
|
+
p { color: #6b7280; margin: 0; }
|
|
1476
|
+
</style>
|
|
1477
|
+
</head>
|
|
1478
|
+
<body>
|
|
1479
|
+
<div class="card">
|
|
1480
|
+
<h1>\u2713 Authentication Successful</h1>
|
|
1481
|
+
<p>You can close this window and return to your terminal.</p>
|
|
1482
|
+
</div>
|
|
1483
|
+
</body>
|
|
1484
|
+
</html>
|
|
1485
|
+
`);
|
|
1486
|
+
server.close();
|
|
1487
|
+
resolve(sessionToken);
|
|
1488
|
+
} else {
|
|
1489
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
1490
|
+
res.end(`
|
|
1491
|
+
<!DOCTYPE html>
|
|
1492
|
+
<html>
|
|
1493
|
+
<head><title>Authentication Failed</title></head>
|
|
1494
|
+
<body>
|
|
1495
|
+
<h1>Authentication Failed</h1>
|
|
1496
|
+
<p>No session token received. Please try again.</p>
|
|
1497
|
+
</body>
|
|
1498
|
+
</html>
|
|
1499
|
+
`);
|
|
1500
|
+
}
|
|
1501
|
+
});
|
|
1502
|
+
server.listen(port, "127.0.0.1", () => {
|
|
1503
|
+
console.log(`Callback server listening on http://127.0.0.1:${port}`);
|
|
1504
|
+
});
|
|
1505
|
+
server.on("error", reject);
|
|
1506
|
+
setTimeout(() => {
|
|
1507
|
+
server.close();
|
|
1508
|
+
reject(new Error("Authentication timed out. Please try again."));
|
|
1509
|
+
}, 5 * 60 * 1e3);
|
|
1510
|
+
});
|
|
1511
|
+
}
|
|
1450
1512
|
function createApiClient(token) {
|
|
1451
1513
|
return new ApiClient({
|
|
1452
1514
|
baseUrl: API_BASE_URL,
|
|
@@ -1454,54 +1516,6 @@ function createApiClient(token) {
|
|
|
1454
1516
|
auth: new TokenAuthAdapter(async () => token)
|
|
1455
1517
|
});
|
|
1456
1518
|
}
|
|
1457
|
-
async function startDeviceCodeFlow() {
|
|
1458
|
-
const response = await fetch(`${API_BASE_URL}/api/v2/auth/device-code`, {
|
|
1459
|
-
method: "POST",
|
|
1460
|
-
headers: {
|
|
1461
|
-
"Content-Type": "application/json"
|
|
1462
|
-
},
|
|
1463
|
-
body: JSON.stringify({
|
|
1464
|
-
clientId: OAUTH_CONFIG.clientId,
|
|
1465
|
-
scope: "openid profile email offline_access"
|
|
1466
|
-
})
|
|
1467
|
-
});
|
|
1468
|
-
if (!response.ok) {
|
|
1469
|
-
const error = await response.json().catch(() => ({}));
|
|
1470
|
-
throw new Error(error.message || `Device code request failed: ${response.status}`);
|
|
1471
|
-
}
|
|
1472
|
-
return response.json();
|
|
1473
|
-
}
|
|
1474
|
-
async function pollForToken(deviceCode, interval, expiresIn) {
|
|
1475
|
-
const startTime = Date.now();
|
|
1476
|
-
const expireTime = startTime + expiresIn * 1e3;
|
|
1477
|
-
while (Date.now() < expireTime) {
|
|
1478
|
-
await new Promise((resolve) => setTimeout(resolve, interval * 1e3));
|
|
1479
|
-
const response = await fetch(`${API_BASE_URL}/api/v2/auth/device-token`, {
|
|
1480
|
-
method: "POST",
|
|
1481
|
-
headers: {
|
|
1482
|
-
"Content-Type": "application/json"
|
|
1483
|
-
},
|
|
1484
|
-
body: JSON.stringify({
|
|
1485
|
-
clientId: OAUTH_CONFIG.clientId,
|
|
1486
|
-
deviceCode,
|
|
1487
|
-
grantType: "urn:ietf:params:oauth:grant-type:device_code"
|
|
1488
|
-
})
|
|
1489
|
-
});
|
|
1490
|
-
if (response.ok) {
|
|
1491
|
-
return response.json();
|
|
1492
|
-
}
|
|
1493
|
-
const error = await response.json().catch(() => ({}));
|
|
1494
|
-
if (error.error === "authorization_pending") {
|
|
1495
|
-
continue;
|
|
1496
|
-
}
|
|
1497
|
-
if (error.error === "slow_down") {
|
|
1498
|
-
interval += 5;
|
|
1499
|
-
continue;
|
|
1500
|
-
}
|
|
1501
|
-
throw new Error(error.message || `Token request failed: ${response.status}`);
|
|
1502
|
-
}
|
|
1503
|
-
throw new Error("Device code expired");
|
|
1504
|
-
}
|
|
1505
1519
|
async function getServerWrapSecret(client) {
|
|
1506
1520
|
const response = await client.post("/webauthn/initialize", {});
|
|
1507
1521
|
return base64Decode(response.serverWrapSecret);
|
|
@@ -1567,25 +1581,23 @@ async function decryptDeviceCredentials(encryptedPayload) {
|
|
|
1567
1581
|
async function login() {
|
|
1568
1582
|
console.log("\nEstateHelm Login");
|
|
1569
1583
|
console.log("================\n");
|
|
1570
|
-
console.log("Starting authentication...");
|
|
1571
|
-
const
|
|
1584
|
+
console.log("Starting authentication server...");
|
|
1585
|
+
const port = await findAvailablePort();
|
|
1586
|
+
const callbackUrl = `http://127.0.0.1:${port}/callback`;
|
|
1587
|
+
const loginUrl = `${APP_URL}/signin?redirect=/cli-auth?callback=${encodeURIComponent(callbackUrl)}`;
|
|
1572
1588
|
console.log(`
|
|
1573
|
-
|
|
1574
|
-
console.log(`
|
|
1589
|
+
Opening browser for authentication...`);
|
|
1590
|
+
console.log(`If the browser doesn't open, visit: ${loginUrl}
|
|
1575
1591
|
`);
|
|
1576
|
-
|
|
1577
|
-
await (0, import_open.default)(
|
|
1592
|
+
const callbackPromise = waitForCallback(port);
|
|
1593
|
+
await (0, import_open.default)(loginUrl);
|
|
1578
1594
|
console.log("Waiting for authentication...");
|
|
1579
|
-
const
|
|
1580
|
-
deviceCodeResponse.deviceCode,
|
|
1581
|
-
deviceCodeResponse.interval,
|
|
1582
|
-
deviceCodeResponse.expiresIn
|
|
1583
|
-
);
|
|
1595
|
+
const sessionToken = await callbackPromise;
|
|
1584
1596
|
console.log("Authentication successful!");
|
|
1585
|
-
console.log(`Token: ${sanitizeToken(
|
|
1586
|
-
await saveBearerToken(
|
|
1587
|
-
await saveRefreshToken(
|
|
1588
|
-
const client = createApiClient(
|
|
1597
|
+
console.log(`Token: ${sanitizeToken(sessionToken)}`);
|
|
1598
|
+
await saveBearerToken(sessionToken);
|
|
1599
|
+
await saveRefreshToken("");
|
|
1600
|
+
const client = createApiClient(sessionToken);
|
|
1589
1601
|
console.log("\nFetching encryption keys...");
|
|
1590
1602
|
const serverWrapSecret = await getServerWrapSecret(client);
|
|
1591
1603
|
const keyBundle = await getCurrentKeyBundle(client);
|