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 +23 -45
- package/bin/leedab.js +28 -0
- package/dist/dashboard/routes.js +2 -3
- package/dist/gateway.js +6 -0
- package/dist/onboard/index.js +1 -1
- package/dist/team.d.ts +12 -8
- package/dist/team.js +66 -40
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,39 +1,36 @@
|
|
|
1
1
|
# LeedAB
|
|
2
2
|
|
|
3
|
-
**
|
|
3
|
+
**Living OS. An autonomous AI agent that runs on your machine.**
|
|
4
4
|
|
|
5
|
-
LeedAB
|
|
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
|
-
##
|
|
7
|
+
## Install
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
11
|
-
npx leedab onboard
|
|
12
|
-
npx leedab start
|
|
10
|
+
curl -fsSL https://leedab.com/install.sh | bash
|
|
13
11
|
```
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
Then run the setup wizard:
|
|
16
14
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
19
|
+
You'll need a license key. Get one at [leedab.com](https://www.leedab.com).
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
## What you get
|
|
30
22
|
|
|
31
|
-
|
|
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
|
|
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
|
-
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
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
|
-
|
|
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/`
|
|
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
|
-
-
|
|
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");
|
package/dist/dashboard/routes.js
CHANGED
|
@@ -429,9 +429,8 @@ export function createRoutes(config) {
|
|
|
429
429
|
}
|
|
430
430
|
const member = await addMember({
|
|
431
431
|
name: name.trim(),
|
|
432
|
-
email: email ||
|
|
433
|
-
role: role || "
|
|
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);
|
package/dist/onboard/index.js
CHANGED
|
@@ -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("
|
|
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" | "
|
|
1
|
+
export type Role = "admin" | "member" | "owner";
|
|
2
2
|
export interface TeamMember {
|
|
3
3
|
id: string;
|
|
4
|
-
|
|
5
|
-
email
|
|
4
|
+
user_id: number;
|
|
5
|
+
email: string;
|
|
6
|
+
username: string;
|
|
6
7
|
role: Role;
|
|
7
|
-
|
|
8
|
-
createdAt: string;
|
|
8
|
+
joined_at: string;
|
|
9
9
|
}
|
|
10
10
|
export declare function loadTeam(): Promise<TeamMember[]>;
|
|
11
|
-
export declare function addMember(member:
|
|
12
|
-
|
|
13
|
-
|
|
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 {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
|
18
|
-
await
|
|
19
|
-
|
|
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
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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(
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
return
|
|
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(
|
|
42
|
-
const
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
}
|