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 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 OAUTH_CONFIG = {
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";
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 deviceCodeResponse = await startDeviceCodeFlow();
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
- Please visit: ${deviceCodeResponse.verificationUri}`);
1574
- console.log(`Enter code: ${deviceCodeResponse.userCode}
1599
+ Opening browser for authentication...`);
1600
+ console.log(`If the browser doesn't open, visit: ${loginUrl}
1575
1601
  `);
1576
- console.log("Opening browser...");
1577
- await (0, import_open.default)(deviceCodeResponse.verificationUri);
1602
+ const callbackPromise = waitForCallback(port);
1603
+ await (0, import_open.default)(loginUrl);
1578
1604
  console.log("Waiting for authentication...");
1579
- const tokenResponse = await pollForToken(
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(tokenResponse.accessToken)}`);
1586
- await saveBearerToken(tokenResponse.accessToken);
1587
- await saveRefreshToken(tokenResponse.refreshToken);
1588
- const client = createApiClient(tokenResponse.accessToken);
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();