leedab 0.1.2 → 0.1.4

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/README.md CHANGED
@@ -1,39 +1,36 @@
1
1
  # LeedAB
2
2
 
3
- **Your enterprise AI agent. Local-first, private by default.**
3
+ **Living OS. An autonomous AI agent that runs on your machine.**
4
4
 
5
- LeedAB deploys an AI agent on your own hardware Mac, Linux, or VPS. It connects to your team's messaging apps, remembers context across conversations, handles tasks with browser automation, and keeps every byte of data on your machine.
5
+ LeedAB installs on any Mac, Windows, or Linux computer and becomes an AI agent for your enterprise. It talks to your team on WhatsApp and Telegram, learns your operations, uses your computer like a team member would, and keeps every byte of data on your hardware.
6
6
 
7
- ## Quick Start
7
+ ## Install
8
8
 
9
9
  ```bash
10
- mkdir my-agent && cd my-agent
11
- npx leedab onboard
12
- npx leedab start
10
+ curl -fsSL https://leedab.com/install.sh | bash
13
11
  ```
14
12
 
15
- That's it. The onboarding wizard walks you through:
13
+ Then run the setup wizard:
16
14
 
17
- 1. **LLM provider** — Anthropic, OpenAI, Gemini, DeepSeek, Bedrock, or OpenRouter
18
- 2. **Channels** — Telegram, WhatsApp, Microsoft Teams
19
- 3. **Credential vault** — encrypted storage for service logins the agent uses
20
-
21
- ## What You Get
22
-
23
- **Multi-channel messaging** — your team talks to the agent on Telegram, WhatsApp, or Teams. Each user gets an isolated session with their own context.
24
-
25
- **Persistent memory** — the agent remembers across conversations. Daily notes, long-term memory, and workspace files survive restarts.
15
+ ```bash
16
+ leedab onboard
17
+ ```
26
18
 
27
- **Credential vault** AES-256-GCM encrypted storage. The agent logs into services (Gmail, CRM, carrier portals) using stored credentials via browser automation.
19
+ You'll need a license key. Get one at [leedab.com](https://www.leedab.com).
28
20
 
29
- **Dashboard** web console at `localhost:3000` showing agent status, channel health, session history, and team management.
21
+ ## What you get
30
22
 
31
- **Your data stays local** files, credentials, memory, and logs live on your machine. Messages route through your channel provider and prompts go to your AI provider, but nothing is stored externally.
23
+ - **Messaging** - your team talks to the agent on WhatsApp, Telegram, or Slack. No new app to learn.
24
+ - **Computer use** - the agent opens browsers, reads screens, navigates apps, and fills forms.
25
+ - **Memory** - persistent across conversations. It learns your business and never starts from scratch.
26
+ - **Credential vault** - AES-256-GCM encrypted. The agent logs into services using stored credentials.
27
+ - **Dashboard** - web console at `localhost:3000` with agent status, sessions, and team management.
28
+ - **Local-first** - files, credentials, memory, and logs stay on your machine.
32
29
 
33
30
  ## Commands
34
31
 
35
32
  ```
36
- leedab onboard Interactive setup wizard
33
+ leedab onboard Setup wizard
37
34
  leedab start Start the agent and dashboard
38
35
  leedab terminal Chat with the agent in terminal
39
36
  leedab stop Stop the agent
@@ -47,37 +44,18 @@ leedab --help All commands
47
44
 
48
45
  ## Requirements
49
46
 
50
- - Node.js 22+
51
- - An LLM API key (Anthropic, OpenAI, etc.)
52
- - At least one messaging channel (Telegram is the easiest to set up)
47
+ - macOS, Windows, or Linux
48
+ - A license key from [leedab.com](https://www.leedab.com)
49
+ - An LLM API key (Anthropic, OpenAI, Google, DeepSeek, or OpenRouter)
53
50
 
54
- ## How It Works
55
-
56
- LeedAB runs an AI agent gateway on your machine with enterprise onboarding, a management dashboard, encrypted credential vault, team roles, and audit logging.
57
-
58
- ```
59
- ┌─────────────────────────────────────────────┐
60
- │ LeedAB │
61
- │ ┌─────────┐ ┌──────────┐ ┌──────────────┐ │
62
- │ │ Onboard │ │Dashboard │ │ Vault/Audit │ │
63
- │ └─────────┘ └──────────┘ └──────────────┘ │
64
- │ ┌─────────────────────────────────────────┐ │
65
- │ │ Agent Gateway │ │
66
- │ │ Sessions │ Skills │ Memory │ Browser │ │
67
- │ └─────────────────────────────────────────┘ │
68
- │ ┌──────────┐ ┌──────────┐ ┌─────────────┐ │
69
- │ │ Telegram │ │ WhatsApp │ │ Teams │ │
70
- │ └──────────┘ └──────────┘ └─────────────┘ │
71
- └─────────────────────────────────────────────┘
72
- ```
51
+ The one-liner installer handles Node.js and all dependencies for you.
73
52
 
74
53
  ## Security
75
54
 
76
- - All data stored locally in `.leedab/` (gitignored)
55
+ - All data stored locally in `.leedab/`
77
56
  - Credentials encrypted with AES-256-GCM
78
57
  - Per-channel allowlists control who can message the agent
79
- - DM policies: open, pairing (approval required), or closed
80
- - Team roles: admin, operator, viewer
58
+ - Team roles with admin, operator, and viewer access
81
59
  - Full audit log of every interaction
82
60
 
83
61
  ## License
package/bin/leedab.js CHANGED
@@ -91,6 +91,34 @@ program
91
91
  }
92
92
  });
93
93
 
94
+ program
95
+ .command("license")
96
+ .description("Show license details — tier, seats, status")
97
+ .action(async () => {
98
+ const { loadLicense, validateLicenseKey, saveLicense } = await import("../dist/license.js");
99
+ const cached = await loadLicense();
100
+ if (!cached?.key) {
101
+ console.log(chalk.red("\n No valid license found. Run `leedab onboard` to enter your key.\n"));
102
+ process.exit(1);
103
+ }
104
+ // Always fetch live
105
+ const license = await validateLicenseKey(cached.key);
106
+ await saveLicense(license);
107
+ if (!license) {
108
+ console.log(chalk.red("\n No valid license found. Run `leedab onboard` to enter your key.\n"));
109
+ process.exit(1);
110
+ }
111
+ console.log("");
112
+ console.log(chalk.bold(" License"));
113
+ console.log("");
114
+ console.log(` Key: ${license.key.slice(0, 16)}...`);
115
+ console.log(` Tier: ${license.tier}`);
116
+ console.log(` Status: ${license.valid ? chalk.green(license.status) : chalk.red(license.status)}`);
117
+ console.log(` Seats: ${license.seatsUsed} / ${license.maxSeats}`);
118
+ console.log(` Checked: ${new Date(license.validatedAt).toLocaleDateString()}`);
119
+ console.log("");
120
+ });
121
+
94
122
  const configure = program
95
123
  .command("configure")
96
124
  .description("Reconfigure LeedAB settings");
@@ -429,9 +429,8 @@ export function createRoutes(config) {
429
429
  }
430
430
  const member = await addMember({
431
431
  name: name.trim(),
432
- email: email || undefined,
433
- role: role || "operator",
434
- channels: channels || [],
432
+ email: email || "",
433
+ role: role || "member",
435
434
  });
436
435
  json(res, member, 201);
437
436
  },
package/dist/gateway.js CHANGED
@@ -5,12 +5,18 @@ import { readFile, writeFile, readdir, copyFile, mkdir, access } from "node:fs/p
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { promisify } from "node:util";
7
7
  import { resolveOpenClawBin, openclawEnv } from "./openclaw.js";
8
+ import { ensureLicense } from "./license.js";
8
9
  const execFileAsync = promisify(execFile);
9
10
  let gatewayProcess = null;
10
11
  /**
11
12
  * Start the LeedAB agent gateway.
12
13
  */
13
14
  export async function startGateway(config) {
15
+ // Verify license before starting
16
+ const license = await ensureLicense();
17
+ if (!license) {
18
+ throw new Error("No valid license found. Run `leedab onboard` to enter your license key.");
19
+ }
14
20
  const bin = resolveOpenClawBin();
15
21
  const stateDir = resolve(".leedab");
16
22
  const env = openclawEnv(stateDir);
@@ -24,7 +24,7 @@ export async function runOnboard() {
24
24
  }
25
25
  else {
26
26
  console.log(chalk.bold(" License Key\n"));
27
- console.log(chalk.dim(" Get your key at ") + chalk.cyan("https://leedab.com") + "\n");
27
+ console.log(chalk.dim(" Buy at ") + chalk.cyan("https://leedab.com") + chalk.dim(" — key is sent to your email.") + "\n");
28
28
  let licensed = false;
29
29
  while (!licensed) {
30
30
  let licenseKey;
package/dist/team.d.ts CHANGED
@@ -1,13 +1,17 @@
1
- export type Role = "admin" | "operator" | "viewer";
1
+ export type Role = "admin" | "member" | "owner";
2
2
  export interface TeamMember {
3
3
  id: string;
4
- name: string;
5
- email?: string;
4
+ user_id: number;
5
+ email: string;
6
+ username: string;
6
7
  role: Role;
7
- channels: string[];
8
- createdAt: string;
8
+ joined_at: string;
9
9
  }
10
10
  export declare function loadTeam(): Promise<TeamMember[]>;
11
- export declare function addMember(member: Omit<TeamMember, "id" | "createdAt">): Promise<TeamMember>;
12
- export declare function removeMember(id: string): Promise<boolean>;
13
- export declare function updateRole(id: string, role: Role): Promise<boolean>;
11
+ export declare function addMember(member: {
12
+ name: string;
13
+ email: string;
14
+ role: Role;
15
+ }): Promise<TeamMember>;
16
+ export declare function removeMember(memberId: string): Promise<boolean>;
17
+ export declare function updateRole(memberId: string, role: Role): Promise<boolean>;
package/dist/team.js CHANGED
@@ -1,49 +1,75 @@
1
- import { readFile, writeFile, mkdir } from "node:fs/promises";
2
- import { resolve, dirname } from "node:path";
3
- import { randomUUID } from "node:crypto";
4
- const TEAM_PATH = resolve(".leedab", "team.json");
5
- async function ensureDir() {
6
- await mkdir(dirname(TEAM_PATH), { recursive: true });
1
+ import { loadLicense } from "./license.js";
2
+ const API_URL = "https://api.leedab.com/api/v1";
3
+ /**
4
+ * Get the license key for API auth.
5
+ */
6
+ async function getAuthHeader() {
7
+ const license = await loadLicense();
8
+ if (!license?.key) {
9
+ throw new Error("No license key found. Run `leedab onboard` first.");
10
+ }
11
+ return {
12
+ Authorization: `Bearer ${license.key}`,
13
+ "Content-Type": "application/json",
14
+ };
7
15
  }
8
- export async function loadTeam() {
9
- try {
10
- const raw = await readFile(TEAM_PATH, "utf-8");
11
- return JSON.parse(raw);
16
+ /**
17
+ * Get the user's organization slug.
18
+ */
19
+ async function getOrgSlug(headers) {
20
+ const res = await fetch(`${API_URL}/organizations`, { headers });
21
+ if (!res.ok) {
22
+ throw new Error("Failed to fetch organizations.");
12
23
  }
13
- catch {
14
- return [];
24
+ const data = await res.json();
25
+ if (!data.length) {
26
+ throw new Error("No organization found. Create one in the dashboard first.");
15
27
  }
28
+ return data[0].slug;
16
29
  }
17
- async function saveTeam(team) {
18
- await ensureDir();
19
- await writeFile(TEAM_PATH, JSON.stringify(team, null, 2) + "\n", "utf-8");
30
+ export async function loadTeam() {
31
+ const headers = await getAuthHeader();
32
+ const slug = await getOrgSlug(headers);
33
+ const res = await fetch(`${API_URL}/organizations/${slug}/members/`, { headers });
34
+ if (!res.ok) {
35
+ throw new Error("Failed to fetch team members.");
36
+ }
37
+ const data = await res.json();
38
+ return data.members;
20
39
  }
21
40
  export async function addMember(member) {
22
- const team = await loadTeam();
23
- const newMember = {
24
- ...member,
25
- id: randomUUID(),
26
- createdAt: new Date().toISOString(),
27
- };
28
- team.push(newMember);
29
- await saveTeam(team);
30
- return newMember;
41
+ const headers = await getAuthHeader();
42
+ const slug = await getOrgSlug(headers);
43
+ const res = await fetch(`${API_URL}/organizations/${slug}/invites/`, {
44
+ method: "POST",
45
+ headers,
46
+ body: JSON.stringify({
47
+ email: member.email,
48
+ role: member.role,
49
+ }),
50
+ });
51
+ if (!res.ok) {
52
+ const err = await res.json().catch(() => ({}));
53
+ throw new Error(err.error || "Failed to add team member.");
54
+ }
55
+ return await res.json();
31
56
  }
32
- export async function removeMember(id) {
33
- const team = await loadTeam();
34
- const index = team.findIndex((m) => m.id === id);
35
- if (index === -1)
36
- return false;
37
- team.splice(index, 1);
38
- await saveTeam(team);
39
- return true;
57
+ export async function removeMember(memberId) {
58
+ const headers = await getAuthHeader();
59
+ const slug = await getOrgSlug(headers);
60
+ const res = await fetch(`${API_URL}/organizations/${slug}/members/${memberId}/`, {
61
+ method: "DELETE",
62
+ headers,
63
+ });
64
+ return res.ok;
40
65
  }
41
- export async function updateRole(id, role) {
42
- const team = await loadTeam();
43
- const member = team.find((m) => m.id === id);
44
- if (!member)
45
- return false;
46
- member.role = role;
47
- await saveTeam(team);
48
- return true;
66
+ export async function updateRole(memberId, role) {
67
+ const headers = await getAuthHeader();
68
+ const slug = await getOrgSlug(headers);
69
+ const res = await fetch(`${API_URL}/organizations/${slug}/members/${memberId}/`, {
70
+ method: "PUT",
71
+ headers,
72
+ body: JSON.stringify({ role }),
73
+ });
74
+ return res.ok;
49
75
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leedab",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
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>",