leedab 0.1.1 → 0.1.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.
@@ -2,6 +2,7 @@ import { execFile, spawn } from "node:child_process";
2
2
  import { readFile, writeFile, mkdir, readdir } from "node:fs/promises";
3
3
  import { resolve } from "node:path";
4
4
  import { promisify } from "node:util";
5
+ import { userInfo } from "node:os";
5
6
  import { resolveOpenClawBin, openclawEnv } from "../openclaw.js";
6
7
  import { addEntry, removeEntry, listEntries } from "../vault.js";
7
8
  import { readAuditLog } from "../audit.js";
@@ -130,6 +131,20 @@ export function createRoutes(config) {
130
131
  json(res, []);
131
132
  }
132
133
  },
134
+ /**
135
+ * GET /api/whoami — current OS user's first name
136
+ */
137
+ "GET /api/whoami": async (_req, res) => {
138
+ let firstName = userInfo().username;
139
+ try {
140
+ const { stdout } = await execFileAsync("id", ["-F"], { timeout: 2000 });
141
+ const fullName = stdout.trim();
142
+ if (fullName)
143
+ firstName = fullName.split(/\s+/)[0];
144
+ }
145
+ catch { }
146
+ json(res, { name: firstName });
147
+ },
133
148
  /**
134
149
  * GET /api/status — channel health status
135
150
  */
@@ -297,6 +297,10 @@
297
297
  .msg-bubble.agent strong { font-weight: 600; }
298
298
  .msg-bubble.agent em { font-style: italic; }
299
299
  .msg-bubble.agent blockquote { border-left: 3px solid var(--accent); padding-left: 10px; color: var(--text-dim); margin: 6px 0; }
300
+ .msg-bubble.agent table { width: 100%; border-collapse: collapse; margin: 8px 0; font-size: 13px; }
301
+ .msg-bubble.agent th, .msg-bubble.agent td { text-align: left; padding: 6px 10px; border: 1px solid var(--border); }
302
+ .msg-bubble.agent th { background: var(--surface-raised); font-weight: 600; color: var(--text-secondary); font-size: 12px; }
303
+ .msg-bubble.agent td { color: var(--text); }
300
304
 
301
305
  /* Thinking */
302
306
  .thinking-row {
@@ -635,6 +639,15 @@
635
639
  const params = new URLSearchParams(window.location.search);
636
640
  let sessionId = params.get("session") || "console";
637
641
 
642
+ let userName = "You";
643
+ let userInitial = "Y";
644
+ fetch("/api/whoami").then(r => r.json()).then(d => {
645
+ if (d.name) {
646
+ userName = d.name;
647
+ userInitial = d.name.charAt(0).toUpperCase();
648
+ }
649
+ }).catch(() => {});
650
+
638
651
  input.addEventListener("input", () => {
639
652
  input.style.height = "auto";
640
653
  input.style.height = Math.min(input.scrollHeight, 120) + "px";
@@ -727,7 +740,7 @@
727
740
  img.alt = "LeedAB";
728
741
  avatar.appendChild(img);
729
742
  } else {
730
- avatar.textContent = "A";
743
+ avatar.textContent = userInitial;
731
744
  }
732
745
 
733
746
  const content = document.createElement("div");
@@ -735,7 +748,7 @@
735
748
 
736
749
  const meta = document.createElement("div");
737
750
  meta.className = "msg-meta";
738
- meta.innerHTML = `<span class="msg-name">${type === "agent" ? "LeedAB" : "Admin"}</span><span class="msg-time">${now()}</span>`;
751
+ meta.innerHTML = `<span class="msg-name">${type === "agent" ? "LeedAB" : userName}</span><span class="msg-time">${now()}</span>`;
739
752
 
740
753
  const bubble = document.createElement("div");
741
754
  bubble.className = `msg-bubble ${type}`;
@@ -760,6 +773,19 @@
760
773
  let h = text.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;");
761
774
  // Code blocks
762
775
  h = h.replace(/```(\w*)\n([\s\S]*?)```/g, (_,lang,code) => `<pre><code>${code.trim()}</code></pre>`);
776
+ // Tables (must run before inline formatting)
777
+ h = h.replace(/(\n|^)(\|[^\n]+\|\n\|[\s\-:|]+\|\n(?:\|[^\n]+\|\n?)+)/g, function(_, prefix, block) {
778
+ const rows = block.trim().split("\n");
779
+ const parseRow = r => r.replace(/^\|/, "").replace(/\|$/, "").split("|").map(c => c.trim());
780
+ const headers = parseRow(rows[0]);
781
+ const body = rows.slice(2).map(parseRow);
782
+ let t = "<table><thead><tr>" + headers.map(h => `<th>${h}</th>`).join("") + "</tr></thead><tbody>";
783
+ for (const cells of body) {
784
+ t += "<tr>" + cells.map(c => `<td>${c}</td>`).join("") + "</tr>";
785
+ }
786
+ t += "</tbody></table>";
787
+ return prefix + t;
788
+ });
763
789
  // Inline code
764
790
  h = h.replace(/`([^`]+)`/g, "<code>$1</code>");
765
791
  // Bold
@@ -27,15 +27,23 @@ export async function runOnboard() {
27
27
  console.log(chalk.dim(" Get your key at ") + chalk.cyan("https://leedab.com") + "\n");
28
28
  let licensed = false;
29
29
  while (!licensed) {
30
- const { licenseKey } = await inquirer.prompt([
31
- {
32
- type: "password",
33
- name: "licenseKey",
34
- message: "License key:",
35
- mask: "*",
36
- validate: (v) => v.trim().startsWith("am_live_") || "Key should start with am_live_",
37
- },
38
- ]);
30
+ let licenseKey;
31
+ try {
32
+ const answers = await inquirer.prompt([
33
+ {
34
+ type: "password",
35
+ name: "licenseKey",
36
+ message: "License key:",
37
+ mask: "*",
38
+ validate: (v) => v.trim().startsWith("am_live_") || "Key should start with am_live_",
39
+ },
40
+ ]);
41
+ licenseKey = answers.licenseKey;
42
+ }
43
+ catch {
44
+ console.log(chalk.dim("\n\n Exiting. Run `leedab onboard` when you have your key.\n"));
45
+ process.exit(0);
46
+ }
39
47
  process.stdout.write(chalk.dim(" Validating..."));
40
48
  const result = await validateLicenseKey(licenseKey.trim());
41
49
  if (result.valid) {
@@ -45,9 +53,16 @@ export async function runOnboard() {
45
53
  }
46
54
  else {
47
55
  console.log(chalk.red(" invalid key."));
48
- const { retry } = await inquirer.prompt([
49
- { type: "confirm", name: "retry", message: "Try again?", default: true },
50
- ]);
56
+ let retry = true;
57
+ try {
58
+ const answers = await inquirer.prompt([
59
+ { type: "confirm", name: "retry", message: "Try again?", default: true },
60
+ ]);
61
+ retry = answers.retry;
62
+ }
63
+ catch {
64
+ retry = false;
65
+ }
51
66
  if (!retry) {
52
67
  console.log(chalk.red("\n A valid license key is required to use LeedAB."));
53
68
  console.log(chalk.dim(" Get one at ") + chalk.cyan("https://leedab.com\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leedab",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "LeedAB — Your enterprise AI agent. Local-first, private by default.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "author": "LeedAB <hello@leedab.com>",