leedab 0.1.3 → 0.1.5

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/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");
@@ -268,6 +296,21 @@ pairing
268
296
  return;
269
297
  }
270
298
 
299
+ // Check seat limit
300
+ const { ensureLicense } = await import("../dist/license.js");
301
+ const license = await ensureLicense();
302
+ if (license && license.maxSeats > 0) {
303
+ // Count total unique users across all channel allowlists
304
+ const allUsers = new Set();
305
+ for (const ch of Object.values(config.channels || {})) {
306
+ for (const u of ch.allowFrom || []) allUsers.add(u);
307
+ }
308
+ if (allUsers.size >= license.maxSeats) {
309
+ console.error(chalk.red(`Seat limit reached (${allUsers.size}/${license.maxSeats}). Upgrade your plan to add more users.`));
310
+ process.exit(1);
311
+ }
312
+ }
313
+
271
314
  config.channels[channel].allowFrom.push(userId);
272
315
  config.channels[channel].dmPolicy = "allowlist";
273
316
  await writeFile(configPath, JSON.stringify(config, null, 2) + "\n");
@@ -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.3",
3
+ "version": "0.1.5",
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>",