estatehelm 1.0.0 → 1.0.2
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 +103 -74
- 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,17 @@ 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
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
}
|
|
1316
|
+
var APP_URL = process.env.ESTATEHELM_APP_URL || "https://app.estatehelm.com";
|
|
1317
|
+
function setServerUrls(apiUrl, appUrl) {
|
|
1318
|
+
if (apiUrl) {
|
|
1319
|
+
API_BASE_URL = apiUrl;
|
|
1320
|
+
console.error(`[Config] Using API: ${apiUrl}`);
|
|
1321
|
+
}
|
|
1322
|
+
if (appUrl) {
|
|
1323
|
+
APP_URL = appUrl;
|
|
1324
|
+
console.error(`[Config] Using App: ${appUrl}`);
|
|
1325
|
+
}
|
|
1326
|
+
}
|
|
1325
1327
|
var DEFAULT_CONFIG = {
|
|
1326
1328
|
defaultMode: "full"
|
|
1327
1329
|
};
|
|
@@ -1447,6 +1449,76 @@ function prompt(question) {
|
|
|
1447
1449
|
});
|
|
1448
1450
|
});
|
|
1449
1451
|
}
|
|
1452
|
+
async function findAvailablePort(startPort = 19847) {
|
|
1453
|
+
return new Promise((resolve, reject) => {
|
|
1454
|
+
const server = http.createServer();
|
|
1455
|
+
server.listen(startPort, "127.0.0.1", () => {
|
|
1456
|
+
const address = server.address();
|
|
1457
|
+
const port = typeof address === "object" && address ? address.port : startPort;
|
|
1458
|
+
server.close(() => resolve(port));
|
|
1459
|
+
});
|
|
1460
|
+
server.on("error", (err) => {
|
|
1461
|
+
if (err.code === "EADDRINUSE") {
|
|
1462
|
+
resolve(findAvailablePort(startPort + 1));
|
|
1463
|
+
} else {
|
|
1464
|
+
reject(err);
|
|
1465
|
+
}
|
|
1466
|
+
});
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1469
|
+
function waitForCallback(port) {
|
|
1470
|
+
return new Promise((resolve, reject) => {
|
|
1471
|
+
const server = http.createServer((req, res) => {
|
|
1472
|
+
const url = new URL(req.url || "/", `http://127.0.0.1:${port}`);
|
|
1473
|
+
const sessionToken = url.searchParams.get("session_token");
|
|
1474
|
+
if (sessionToken) {
|
|
1475
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
1476
|
+
res.end(`
|
|
1477
|
+
<!DOCTYPE html>
|
|
1478
|
+
<html>
|
|
1479
|
+
<head>
|
|
1480
|
+
<title>CLI Authentication Successful</title>
|
|
1481
|
+
<style>
|
|
1482
|
+
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%); }
|
|
1483
|
+
.card { background: white; padding: 2rem; border-radius: 1rem; box-shadow: 0 10px 25px rgba(0,0,0,0.1); text-align: center; }
|
|
1484
|
+
h1 { color: #059669; margin: 0 0 0.5rem; }
|
|
1485
|
+
p { color: #6b7280; margin: 0; }
|
|
1486
|
+
</style>
|
|
1487
|
+
</head>
|
|
1488
|
+
<body>
|
|
1489
|
+
<div class="card">
|
|
1490
|
+
<h1>\u2713 Authentication Successful</h1>
|
|
1491
|
+
<p>You can close this window and return to your terminal.</p>
|
|
1492
|
+
</div>
|
|
1493
|
+
</body>
|
|
1494
|
+
</html>
|
|
1495
|
+
`);
|
|
1496
|
+
server.close();
|
|
1497
|
+
resolve(sessionToken);
|
|
1498
|
+
} else {
|
|
1499
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
1500
|
+
res.end(`
|
|
1501
|
+
<!DOCTYPE html>
|
|
1502
|
+
<html>
|
|
1503
|
+
<head><title>Authentication Failed</title></head>
|
|
1504
|
+
<body>
|
|
1505
|
+
<h1>Authentication Failed</h1>
|
|
1506
|
+
<p>No session token received. Please try again.</p>
|
|
1507
|
+
</body>
|
|
1508
|
+
</html>
|
|
1509
|
+
`);
|
|
1510
|
+
}
|
|
1511
|
+
});
|
|
1512
|
+
server.listen(port, "127.0.0.1", () => {
|
|
1513
|
+
console.log(`Callback server listening on http://127.0.0.1:${port}`);
|
|
1514
|
+
});
|
|
1515
|
+
server.on("error", reject);
|
|
1516
|
+
setTimeout(() => {
|
|
1517
|
+
server.close();
|
|
1518
|
+
reject(new Error("Authentication timed out. Please try again."));
|
|
1519
|
+
}, 5 * 60 * 1e3);
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1450
1522
|
function createApiClient(token) {
|
|
1451
1523
|
return new ApiClient({
|
|
1452
1524
|
baseUrl: API_BASE_URL,
|
|
@@ -1454,54 +1526,6 @@ function createApiClient(token) {
|
|
|
1454
1526
|
auth: new TokenAuthAdapter(async () => token)
|
|
1455
1527
|
});
|
|
1456
1528
|
}
|
|
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
1529
|
async function getServerWrapSecret(client) {
|
|
1506
1530
|
const response = await client.post("/webauthn/initialize", {});
|
|
1507
1531
|
return base64Decode(response.serverWrapSecret);
|
|
@@ -1567,25 +1591,23 @@ async function decryptDeviceCredentials(encryptedPayload) {
|
|
|
1567
1591
|
async function login() {
|
|
1568
1592
|
console.log("\nEstateHelm Login");
|
|
1569
1593
|
console.log("================\n");
|
|
1570
|
-
console.log("Starting authentication...");
|
|
1571
|
-
const
|
|
1594
|
+
console.log("Starting authentication server...");
|
|
1595
|
+
const port = await findAvailablePort();
|
|
1596
|
+
const callbackUrl = `http://127.0.0.1:${port}/callback`;
|
|
1597
|
+
const loginUrl = `${APP_URL}/cli-auth?callback=${encodeURIComponent(callbackUrl)}`;
|
|
1572
1598
|
console.log(`
|
|
1573
|
-
|
|
1574
|
-
console.log(`
|
|
1599
|
+
Opening browser for authentication...`);
|
|
1600
|
+
console.log(`If the browser doesn't open, visit: ${loginUrl}
|
|
1575
1601
|
`);
|
|
1576
|
-
|
|
1577
|
-
await (0, import_open.default)(
|
|
1602
|
+
const callbackPromise = waitForCallback(port);
|
|
1603
|
+
await (0, import_open.default)(loginUrl);
|
|
1578
1604
|
console.log("Waiting for authentication...");
|
|
1579
|
-
const
|
|
1580
|
-
deviceCodeResponse.deviceCode,
|
|
1581
|
-
deviceCodeResponse.interval,
|
|
1582
|
-
deviceCodeResponse.expiresIn
|
|
1583
|
-
);
|
|
1605
|
+
const sessionToken = await callbackPromise;
|
|
1584
1606
|
console.log("Authentication successful!");
|
|
1585
|
-
console.log(`Token: ${sanitizeToken(
|
|
1586
|
-
await saveBearerToken(
|
|
1587
|
-
await saveRefreshToken(
|
|
1588
|
-
const client = createApiClient(
|
|
1607
|
+
console.log(`Token: ${sanitizeToken(sessionToken)}`);
|
|
1608
|
+
await saveBearerToken(sessionToken);
|
|
1609
|
+
await saveRefreshToken("");
|
|
1610
|
+
const client = createApiClient(sessionToken);
|
|
1589
1611
|
console.log("\nFetching encryption keys...");
|
|
1590
1612
|
const serverWrapSecret = await getServerWrapSecret(client);
|
|
1591
1613
|
const keyBundle = await getCurrentKeyBundle(client);
|
|
@@ -2853,7 +2875,14 @@ function searchEntity(entity, query) {
|
|
|
2853
2875
|
|
|
2854
2876
|
// src/index.ts
|
|
2855
2877
|
var program = new import_commander.Command();
|
|
2856
|
-
program.name("estatehelm").description("EstateHelm CLI - MCP server for AI assistants").version("1.0.0")
|
|
2878
|
+
program.name("estatehelm").description("EstateHelm CLI - MCP server for AI assistants").version("1.0.0").option("--staging", "Use staging environment (previewapi/previewapp.estatehelm.com)").option("--api-url <url>", "API server URL (default: https://api.estatehelm.com)").option("--app-url <url>", "App server URL (default: https://app.estatehelm.com)").hook("preAction", (thisCommand) => {
|
|
2879
|
+
const opts = thisCommand.opts();
|
|
2880
|
+
if (opts.staging) {
|
|
2881
|
+
setServerUrls("https://previewapi.estatehelm.com", "https://previewapp.estatehelm.com");
|
|
2882
|
+
} else if (opts.apiUrl || opts.appUrl) {
|
|
2883
|
+
setServerUrls(opts.apiUrl, opts.appUrl);
|
|
2884
|
+
}
|
|
2885
|
+
});
|
|
2857
2886
|
program.command("login").description("Authenticate with EstateHelm").action(async () => {
|
|
2858
2887
|
try {
|
|
2859
2888
|
await login();
|