@eve-horizon/cli 0.2.38 → 0.2.40

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.
Files changed (2) hide show
  1. package/dist/index.js +220 -39
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9811,8 +9811,8 @@ var require_sync = __commonJS({
9811
9811
  }
9812
9812
  return x;
9813
9813
  };
9814
- var defaultReadPackageSync = function defaultReadPackageSync2(readFileSync24, pkgfile) {
9815
- var body = readFileSync24(pkgfile);
9814
+ var defaultReadPackageSync = function defaultReadPackageSync2(readFileSync25, pkgfile) {
9815
+ var body = readFileSync25(pkgfile);
9816
9816
  try {
9817
9817
  var pkg = JSON.parse(body);
9818
9818
  return pkg;
@@ -9832,7 +9832,7 @@ var require_sync = __commonJS({
9832
9832
  }
9833
9833
  var opts = normalizeOptions(x, options);
9834
9834
  var isFile = opts.isFile || defaultIsFile;
9835
- var readFileSync24 = opts.readFileSync || fs6.readFileSync;
9835
+ var readFileSync25 = opts.readFileSync || fs6.readFileSync;
9836
9836
  var isDirectory = opts.isDirectory || defaultIsDir;
9837
9837
  var realpathSync = opts.realpathSync || defaultRealpathSync;
9838
9838
  var readPackageSync = opts.readPackageSync || defaultReadPackageSync;
@@ -9889,7 +9889,7 @@ var require_sync = __commonJS({
9889
9889
  if (!isFile(pkgfile)) {
9890
9890
  return loadpkg(path8.dirname(dir));
9891
9891
  }
9892
- var pkg = readPackageSync(readFileSync24, pkgfile);
9892
+ var pkg = readPackageSync(readFileSync25, pkgfile);
9893
9893
  if (pkg && opts.packageFilter) {
9894
9894
  pkg = opts.packageFilter(
9895
9895
  pkg,
@@ -9903,7 +9903,7 @@ var require_sync = __commonJS({
9903
9903
  var pkgfile = path8.join(maybeRealpathSync(realpathSync, x2, opts), "/package.json");
9904
9904
  if (isFile(pkgfile)) {
9905
9905
  try {
9906
- var pkg = readPackageSync(readFileSync24, pkgfile);
9906
+ var pkg = readPackageSync(readFileSync25, pkgfile);
9907
9907
  } catch (e) {
9908
9908
  }
9909
9909
  if (pkg && opts.packageFilter) {
@@ -48552,6 +48552,10 @@ var require_src57 = __commonJS({
48552
48552
  }
48553
48553
  });
48554
48554
 
48555
+ // src/index.ts
48556
+ var import_fs4 = require("fs");
48557
+ var import_path3 = require("path");
48558
+
48555
48559
  // src/lib/args.ts
48556
48560
  function parseArgs(args) {
48557
48561
  const flags = {};
@@ -48648,7 +48652,7 @@ var import_node_fs2 = require("node:fs");
48648
48652
  var import_node_path2 = require("node:path");
48649
48653
  var import_yaml = require("yaml");
48650
48654
  var DEFAULT_PROFILE = "default";
48651
- var DEFAULT_API_URL = "http://api.eve.lvh.me";
48655
+ var DEFAULT_API_URL = "https://api.eh1.incept5.dev";
48652
48656
  function getRepoProfilePath() {
48653
48657
  return (0, import_node_path2.join)(process.cwd(), ".eve", "profile.yaml");
48654
48658
  }
@@ -49522,7 +49526,7 @@ for cloud deployments. Credentials are stored globally per API URL.`,
49522
49526
  options: [
49523
49527
  "--email <email> Email address for SSH login (uses profile default_email if not provided)",
49524
49528
  "--user-id <id> User id for SSH login",
49525
- "--ssh-key <path> Path to SSH private key (uses profile default_ssh_key, then ~/.ssh/id_ed25519)",
49529
+ "--ssh-key <path> Path to SSH private key (auto-discovers from ~/.ssh/ if omitted)",
49526
49530
  "--ttl <days> Token TTL in days (1-90, default: server configured)",
49527
49531
  "--password <pass> Supabase password (triggers Supabase login)",
49528
49532
  "--supabase-url <url> Supabase URL",
@@ -51553,6 +51557,22 @@ async function requestRaw(context2, path8, options = {}) {
51553
51557
  body: options.body ? JSON.stringify(options.body) : void 0
51554
51558
  });
51555
51559
  } catch (error) {
51560
+ const cause = error instanceof Error ? error.cause : void 0;
51561
+ const code = cause?.code;
51562
+ const isConnectionError = code === "ECONNREFUSED" || code === "ENOTFOUND" || code === "ETIMEDOUT" || error instanceof Error && error.message.includes("fetch failed");
51563
+ if (isConnectionError) {
51564
+ throw new Error(
51565
+ `Could not connect to ${context2.apiUrl}
51566
+
51567
+ The API is unreachable. Check that:
51568
+ - The URL is correct (current: ${context2.apiUrl})
51569
+ - Your internet connection is working
51570
+
51571
+ To change the API URL:
51572
+ eve profile set default --api-url <url>
51573
+ or set EVE_API_URL=<url>`
51574
+ );
51575
+ }
51556
51576
  const message = error instanceof Error ? error.message : String(error);
51557
51577
  throw new Error(`Request failed for ${method} ${url}: ${message}`);
51558
51578
  }
@@ -62200,7 +62220,7 @@ var import_child_process = require("child_process");
62200
62220
  var import_util4 = require("util");
62201
62221
  var execFileAsync = (0, import_util4.promisify)(import_child_process.execFile);
62202
62222
 
62203
- // src/lib/logs.ts
62223
+ // ../shared/dist/log-normalize.js
62204
62224
  function normalizeLogLine(line) {
62205
62225
  const kind = line.kind;
62206
62226
  const raw = line.raw;
@@ -66153,6 +66173,41 @@ function pickFreshestCodeAuth() {
66153
66173
  if (candidates.length === 0) return null;
66154
66174
  return candidates.reduce((best, c) => c.expiresAt > best.expiresAt ? c : best);
66155
66175
  }
66176
+ var SSH_KEY_IGNORE = /* @__PURE__ */ new Set([
66177
+ "config",
66178
+ "known_hosts",
66179
+ "known_hosts.old",
66180
+ "authorized_keys",
66181
+ "environment"
66182
+ ]);
66183
+ var SSH_KEY_PRIORITY = {
66184
+ id_ed25519: 0,
66185
+ id_ecdsa: 1,
66186
+ id_rsa: 2
66187
+ };
66188
+ function discoverSshKeys() {
66189
+ const sshDir = (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".ssh");
66190
+ if (!(0, import_node_fs5.existsSync)(sshDir)) return [];
66191
+ let entries;
66192
+ try {
66193
+ entries = (0, import_node_fs5.readdirSync)(sshDir);
66194
+ } catch {
66195
+ return [];
66196
+ }
66197
+ const candidates = [];
66198
+ for (const name of entries) {
66199
+ if (name.startsWith(".")) continue;
66200
+ if (name.endsWith(".pub")) continue;
66201
+ if (SSH_KEY_IGNORE.has(name)) continue;
66202
+ if (!name.startsWith("id_") && !name.includes("key")) continue;
66203
+ const fullPath = (0, import_node_path5.join)(sshDir, name);
66204
+ const pubPath = `${fullPath}.pub`;
66205
+ if (!(0, import_node_fs5.existsSync)(pubPath)) continue;
66206
+ const priority = SSH_KEY_PRIORITY[name] ?? 10;
66207
+ candidates.push({ path: fullPath, priority });
66208
+ }
66209
+ return candidates.sort((a, b2) => a.priority - b2.priority).map((c) => c.path);
66210
+ }
66156
66211
  function signNonceWithSsh(keyPath, nonce) {
66157
66212
  const tempDir = (0, import_node_fs5.mkdtempSync)((0, import_node_path5.join)((0, import_node_os3.tmpdir)(), "eve-auth-"));
66158
66213
  const noncePath = (0, import_node_path5.join)(tempDir, "nonce");
@@ -66171,12 +66226,17 @@ function signNonceWithSsh(keyPath, nonce) {
66171
66226
  }
66172
66227
  }
66173
66228
  async function attemptSshLogin(context2, credentials, flags, email, userId, ttlDays) {
66229
+ const explicitKey = getStringFlag(flags, ["ssh-key"]) || process.env.EVE_AUTH_SSH_KEY || context2.profile.default_ssh_key;
66230
+ const keysToTry = explicitKey ? [explicitKey] : discoverSshKeys();
66231
+ if (keysToTry.length === 0) {
66232
+ return {
66233
+ success: false,
66234
+ error: "No SSH keys found. Provide --ssh-key <path> or add a keypair to ~/.ssh/"
66235
+ };
66236
+ }
66174
66237
  const challengeResponse = await requestRaw(context2, "/auth/challenge", {
66175
66238
  method: "POST",
66176
- body: {
66177
- email,
66178
- user_id: userId
66179
- }
66239
+ body: { email, user_id: userId }
66180
66240
  });
66181
66241
  if (!challengeResponse.ok) {
66182
66242
  const message = typeof challengeResponse.data === "string" ? challengeResponse.data : challengeResponse.text;
@@ -66186,33 +66246,42 @@ async function attemptSshLogin(context2, credentials, flags, email, userId, ttlD
66186
66246
  if (!challenge.challenge_id || !challenge.nonce) {
66187
66247
  return { success: false, error: "Challenge response missing fields" };
66188
66248
  }
66189
- const sshKeyPath = getStringFlag(flags, ["ssh-key"]) || process.env.EVE_AUTH_SSH_KEY || context2.profile.default_ssh_key || (0, import_node_path5.join)((0, import_node_os3.homedir)(), ".ssh", "id_ed25519");
66190
- let signature;
66191
- try {
66192
- signature = signNonceWithSsh(sshKeyPath, challenge.nonce);
66193
- } catch (err) {
66194
- const msg = err instanceof Error ? err.message : String(err);
66195
- return { success: false, error: `Failed to sign challenge: ${msg}` };
66196
- }
66197
- const verifyResponse = await requestRaw(context2, "/auth/verify", {
66198
- method: "POST",
66199
- body: { challenge_id: challenge.challenge_id, signature, ...ttlDays !== void 0 && { ttl_days: ttlDays } }
66200
- });
66201
- if (!verifyResponse.ok) {
66202
- const message = typeof verifyResponse.data === "string" ? verifyResponse.data : verifyResponse.text;
66203
- return { success: false, error: `Auth verify failed: ${message}` };
66204
- }
66205
- const payload = verifyResponse.data;
66206
- if (!payload.access_token) {
66207
- return { success: false, error: "Auth verify response missing access_token" };
66249
+ let lastError = "";
66250
+ for (const keyPath of keysToTry) {
66251
+ let signature;
66252
+ try {
66253
+ signature = signNonceWithSsh(keyPath, challenge.nonce);
66254
+ } catch {
66255
+ continue;
66256
+ }
66257
+ const verifyResponse = await requestRaw(context2, "/auth/verify", {
66258
+ method: "POST",
66259
+ body: {
66260
+ challenge_id: challenge.challenge_id,
66261
+ signature,
66262
+ ...ttlDays !== void 0 && { ttl_days: ttlDays }
66263
+ }
66264
+ });
66265
+ if (verifyResponse.ok) {
66266
+ const payload = verifyResponse.data;
66267
+ if (!payload.access_token) {
66268
+ return { success: false, error: "Auth verify response missing access_token" };
66269
+ }
66270
+ credentials.tokens[context2.authKey] = {
66271
+ access_token: payload.access_token,
66272
+ expires_at: payload.expires_at,
66273
+ token_type: payload.token_type
66274
+ };
66275
+ saveCredentials(credentials);
66276
+ if (!explicitKey) {
66277
+ const shortPath = keyPath.replace((0, import_node_os3.homedir)(), "~");
66278
+ console.log(`Using SSH key: ${shortPath}`);
66279
+ }
66280
+ return { success: true, tokenType: payload.token_type };
66281
+ }
66282
+ lastError = typeof verifyResponse.data === "string" ? verifyResponse.data : verifyResponse.text;
66208
66283
  }
66209
- credentials.tokens[context2.authKey] = {
66210
- access_token: payload.access_token,
66211
- expires_at: payload.expires_at,
66212
- token_type: payload.token_type
66213
- };
66214
- saveCredentials(credentials);
66215
- return { success: true, tokenType: payload.token_type };
66284
+ return { success: false, error: `Auth verify failed: ${lastError}` };
66216
66285
  }
66217
66286
  async function fetchGitHubKeys(username) {
66218
66287
  const response = await fetch(`https://github.com/${username}.keys`);
@@ -73349,6 +73418,9 @@ var import_node_fs10 = require("node:fs");
73349
73418
  async function handleAdmin(subcommand, positionals, flags, context2) {
73350
73419
  const json = Boolean(flags.json);
73351
73420
  switch (subcommand) {
73421
+ case "users": {
73422
+ return handleUsers(flags, context2, json);
73423
+ }
73352
73424
  case "invite": {
73353
73425
  const githubUsername = getStringFlag(flags, ["github"]);
73354
73426
  const sshKeyPath = getStringFlag(flags, ["ssh-key"]);
@@ -73834,8 +73906,68 @@ ${rows.length} alias(es)`);
73834
73906
  }
73835
73907
  }
73836
73908
  default:
73837
- throw new Error("Usage: eve admin <invite|pricing|receipts|balance|usage|ingress-aliases|access-requests>");
73909
+ throw new Error("Usage: eve admin <users|invite|pricing|receipts|balance|usage|ingress-aliases|access-requests>");
73910
+ }
73911
+ }
73912
+ async function handleUsers(flags, context2, json) {
73913
+ const users = await requestJson(context2, "/system/users");
73914
+ if (json) {
73915
+ outputJson(users, true);
73916
+ return;
73917
+ }
73918
+ if (users.length === 0) {
73919
+ console.log("No users found.");
73920
+ return;
73921
+ }
73922
+ const rows = [];
73923
+ for (const user of users) {
73924
+ const base = {
73925
+ id: user.id,
73926
+ email: user.email,
73927
+ name: user.display_name ?? "-",
73928
+ admin: user.is_admin ? "yes" : "",
73929
+ created: user.created_at?.split("T")[0] ?? ""
73930
+ };
73931
+ if (user.memberships.length === 0) {
73932
+ rows.push({ ...base, org: "-", role: "-" });
73933
+ } else {
73934
+ for (const m of user.memberships) {
73935
+ rows.push({ ...base, org: m.org_slug || m.org_name, role: m.role });
73936
+ }
73937
+ }
73938
+ }
73939
+ const col = (key, header2) => Math.max(header2.length, ...rows.map((r) => r[key].length));
73940
+ const w = {
73941
+ email: col("email", "Email"),
73942
+ name: col("name", "Name"),
73943
+ admin: col("admin", "Admin"),
73944
+ org: col("org", "Org"),
73945
+ role: col("role", "Role"),
73946
+ created: col("created", "Created")
73947
+ };
73948
+ const pad3 = (s, n) => s + " ".repeat(Math.max(0, n - s.length));
73949
+ const header = [
73950
+ pad3("Email", w.email),
73951
+ pad3("Name", w.name),
73952
+ pad3("Admin", w.admin),
73953
+ pad3("Org", w.org),
73954
+ pad3("Role", w.role),
73955
+ pad3("Created", w.created)
73956
+ ].join(" ");
73957
+ console.log(header);
73958
+ console.log("-".repeat(header.length));
73959
+ for (const row of rows) {
73960
+ console.log([
73961
+ pad3(row.email, w.email),
73962
+ pad3(row.name, w.name),
73963
+ pad3(row.admin, w.admin),
73964
+ pad3(row.org, w.org),
73965
+ pad3(row.role, w.role),
73966
+ pad3(row.created, w.created)
73967
+ ].join(" "));
73838
73968
  }
73969
+ console.log("");
73970
+ console.log(`${users.length} user(s)`);
73839
73971
  }
73840
73972
  function formatBalanceSummary(data) {
73841
73973
  if (!data.currency) {
@@ -80498,11 +80630,60 @@ function sleep(ms) {
80498
80630
  }
80499
80631
 
80500
80632
  // src/index.ts
80633
+ function getCliVersion() {
80634
+ try {
80635
+ const pkg = JSON.parse((0, import_fs4.readFileSync)((0, import_path3.join)(__dirname, "..", "package.json"), "utf-8"));
80636
+ return pkg.version;
80637
+ } catch {
80638
+ return "unknown";
80639
+ }
80640
+ }
80641
+ async function showVersion(json, apiUrl) {
80642
+ const cliVersion = getCliVersion();
80643
+ let platformInfo = null;
80644
+ if (apiUrl) {
80645
+ try {
80646
+ const res = await fetch(`${apiUrl}/health/version`);
80647
+ if (res.ok) {
80648
+ platformInfo = await res.json();
80649
+ }
80650
+ } catch {
80651
+ }
80652
+ }
80653
+ if (json) {
80654
+ const result = { cli: cliVersion };
80655
+ if (platformInfo) {
80656
+ result.platform = platformInfo;
80657
+ }
80658
+ if (apiUrl) {
80659
+ result.apiUrl = apiUrl;
80660
+ }
80661
+ console.log(JSON.stringify(result, null, 2));
80662
+ } else {
80663
+ console.log(`eve v${cliVersion}`);
80664
+ if (platformInfo) {
80665
+ const sha = platformInfo.gitSha !== "unknown" ? ` (${platformInfo.gitSha.slice(0, 7)})` : "";
80666
+ console.log(`platform v${platformInfo.version}${sha}`);
80667
+ }
80668
+ }
80669
+ }
80501
80670
  async function main2() {
80502
80671
  const { flags, positionals } = parseArgs(process.argv.slice(2));
80503
80672
  const command = positionals[0];
80504
80673
  const subcommand = positionals[1];
80505
80674
  const rest = positionals.slice(2);
80675
+ if (flags.version || command === "-v" || command === "version") {
80676
+ const json = Boolean(flags.json);
80677
+ let apiUrl;
80678
+ try {
80679
+ const credentials2 = loadCredentials();
80680
+ const context3 = resolveContext(flags, credentials2);
80681
+ apiUrl = context3.apiUrl;
80682
+ } catch {
80683
+ }
80684
+ await showVersion(json, apiUrl);
80685
+ return;
80686
+ }
80506
80687
  if (!command || command === "-h" || command === "--help") {
80507
80688
  showMainHelp();
80508
80689
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eve-horizon/cli",
3
- "version": "0.2.38",
3
+ "version": "0.2.40",
4
4
  "description": "Eve Horizon CLI",
5
5
  "license": "MIT",
6
6
  "repository": {