leedab 0.1.5 → 0.1.7

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
@@ -3,6 +3,7 @@
3
3
  import { createRequire } from "node:module";
4
4
  import { existsSync } from "node:fs";
5
5
  import { resolve } from "node:path";
6
+ import { homedir } from "node:os";
6
7
  import { program } from "commander";
7
8
  import chalk from "chalk";
8
9
  import ora from "ora";
@@ -19,7 +20,7 @@ import { ensureLicense } from "../dist/license.js";
19
20
 
20
21
  const pkg = createRequire(import.meta.url)("../package.json");
21
22
  const OPENCLAW_BIN = resolveOpenClawBin();
22
- const STATE_DIR = resolve(".leedab");
23
+ const STATE_DIR = resolve(homedir(), ".leedab");
23
24
  const ocEnv = openclawEnv(STATE_DIR);
24
25
 
25
26
  /** Commands that don't require an existing project directory or license. */
@@ -37,12 +38,9 @@ function requireProject(command) {
37
38
  const parentName = command.parent?.name();
38
39
  if (NO_PROJECT_REQUIRED.has(name) || NO_PROJECT_REQUIRED.has(parentName)) return;
39
40
 
40
- const hasConfig = existsSync(resolve("leedab.config.json"));
41
- const hasState = existsSync(STATE_DIR);
42
- if (!hasConfig && !hasState) {
43
- console.error(chalk.red("\n Not a LeedAB project directory.\n"));
44
- console.error(chalk.dim(" Run ") + chalk.cyan("leedab onboard") + chalk.dim(" to set up a new project here,"));
45
- console.error(chalk.dim(" or cd into an existing project directory.\n"));
41
+ if (!existsSync(STATE_DIR)) {
42
+ console.error(chalk.red("\n LeedAB is not set up yet.\n"));
43
+ console.error(chalk.dim(" Run ") + chalk.cyan("leedab onboard") + chalk.dim(" to get started.\n"));
46
44
  process.exit(1);
47
45
  }
48
46
  }
@@ -114,7 +112,19 @@ program
114
112
  console.log(` Key: ${license.key.slice(0, 16)}...`);
115
113
  console.log(` Tier: ${license.tier}`);
116
114
  console.log(` Status: ${license.valid ? chalk.green(license.status) : chalk.red(license.status)}`);
117
- console.log(` Seats: ${license.seatsUsed} / ${license.maxSeats}`);
115
+ // Count local allowlist users as seats
116
+ let localSeats = 0;
117
+ try {
118
+ const { readFile } = await import("node:fs/promises");
119
+ const { resolve } = await import("node:path");
120
+ const ocConfig = JSON.parse(await readFile(resolve(STATE_DIR, "openclaw.json"), "utf-8"));
121
+ const allUsers = new Set();
122
+ for (const ch of Object.values(ocConfig.channels || {})) {
123
+ for (const u of ch.allowFrom || []) allUsers.add(u);
124
+ }
125
+ localSeats = allUsers.size;
126
+ } catch {}
127
+ console.log(` Seats: ${localSeats} / ${license.maxSeats}`);
118
128
  console.log(` Checked: ${new Date(license.validatedAt).toLocaleDateString()}`);
119
129
  console.log("");
120
130
  });
@@ -131,7 +141,7 @@ configure
131
141
  const result = await onboardProvider();
132
142
  if (result) {
133
143
  const { readFile, writeFile } = await import("node:fs/promises");
134
- const configPath = resolve("leedab.config.json");
144
+ const configPath = resolve(resolve(STATE_DIR, "config.json"));
135
145
  try {
136
146
  const raw = JSON.parse(await readFile(configPath, "utf-8"));
137
147
  raw.agent.model = result.model;
@@ -161,7 +171,7 @@ program
161
171
  program
162
172
  .command("start")
163
173
  .description("Start the LeedAB agent and dashboard")
164
- .option("-c, --config <path>", "Path to config file", "leedab.config.json")
174
+ .option("-c, --config <path>", "Path to config file", resolve(STATE_DIR, "config.json"))
165
175
  .option("-p, --dashboard-port <port>", "Dashboard port", "3000")
166
176
  .action(async (opts) => {
167
177
  const spinner = ora("Starting LeedAB agent...").start();
@@ -196,7 +206,7 @@ program
196
206
  program
197
207
  .command("dashboard")
198
208
  .description("Open the setup dashboard (without starting the agent)")
199
- .option("-c, --config <path>", "Path to config file", "leedab.config.json")
209
+ .option("-c, --config <path>", "Path to config file", resolve(STATE_DIR, "config.json"))
200
210
  .option("-p, --port <port>", "Dashboard port", "3000")
201
211
  .action(async (opts) => {
202
212
  try {
@@ -374,7 +384,7 @@ pairing
374
384
  program
375
385
  .command("channels")
376
386
  .description("List configured channels and their status")
377
- .option("-c, --config <path>", "Path to config file", "leedab.config.json")
387
+ .option("-c, --config <path>", "Path to config file", resolve(STATE_DIR, "config.json"))
378
388
  .action(async (opts) => {
379
389
  const config = await loadConfig(opts.config);
380
390
  await setupChannels(config);
@@ -618,8 +628,7 @@ team
618
628
 
619
629
  // Default action: show status if in a project, nudge to onboard if not
620
630
  if (process.argv.length <= 2) {
621
- const hasProject = existsSync(resolve("leedab.config.json")) || existsSync(STATE_DIR);
622
- if (!hasProject) {
631
+ if (!existsSync(STATE_DIR)) {
623
632
  console.log(chalk.bold("\n LeedAB") + chalk.dim(" — Your enterprise AI agent\n"));
624
633
  console.log(chalk.dim(" Get started:\n"));
625
634
  console.log(` ${chalk.cyan("leedab onboard")} Interactive setup wizard`);
@@ -630,7 +639,7 @@ if (process.argv.length <= 2) {
630
639
  // Show project status
631
640
  (async () => {
632
641
  try {
633
- const config = await loadConfig("leedab.config.json");
642
+ const config = await loadConfig(resolve(STATE_DIR, "config.json"));
634
643
  const channels = Object.entries(config.channels)
635
644
  .filter(([_, v]) => v?.enabled)
636
645
  .map(([k]) => k);
package/dist/audit.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { appendFile, readFile, mkdir } from "node:fs/promises";
2
2
  import { resolve, dirname } from "node:path";
3
- const AUDIT_PATH = resolve(".leedab", "logs", "audit.jsonl");
3
+ import { STATE_DIR } from "./paths.js";
4
+ const AUDIT_PATH = resolve(STATE_DIR, "logs", "audit.jsonl");
4
5
  export async function logAudit(entry) {
5
6
  await mkdir(dirname(AUDIT_PATH), { recursive: true });
6
7
  const line = JSON.stringify({
@@ -5,6 +5,6 @@ export { type LeedABConfig } from "./schema.js";
5
5
  */
6
6
  export declare function loadConfig(configPath?: string): Promise<LeedABConfig>;
7
7
  /**
8
- * Write default config to leedab.config.json.
8
+ * Write default config to ~/.leedab/config.json.
9
9
  */
10
10
  export declare function initConfig(): Promise<void>;
@@ -1,10 +1,12 @@
1
1
  import { readFile, writeFile, access } from "node:fs/promises";
2
2
  import { resolve } from "node:path";
3
+ import { STATE_DIR } from "../paths.js";
3
4
  import { DEFAULT_CONFIG } from "./schema.js";
5
+ const DEFAULT_CONFIG_PATH = resolve(STATE_DIR, "config.json");
4
6
  /**
5
7
  * Load and validate a LeedAB config file, merging with defaults.
6
8
  */
7
- export async function loadConfig(configPath = "leedab.config.json") {
9
+ export async function loadConfig(configPath = DEFAULT_CONFIG_PATH) {
8
10
  const fullPath = resolve(configPath);
9
11
  let userConfig = {};
10
12
  try {
@@ -17,19 +19,18 @@ export async function loadConfig(configPath = "leedab.config.json") {
17
19
  return deepMerge(DEFAULT_CONFIG, userConfig);
18
20
  }
19
21
  /**
20
- * Write default config to leedab.config.json.
22
+ * Write default config to ~/.leedab/config.json.
21
23
  */
22
24
  export async function initConfig() {
23
- const configPath = resolve("leedab.config.json");
24
25
  try {
25
- await access(configPath);
26
- throw new Error("leedab.config.json already exists");
26
+ await access(DEFAULT_CONFIG_PATH);
27
+ throw new Error("config.json already exists");
27
28
  }
28
29
  catch (err) {
29
30
  if (err.code !== "ENOENT")
30
31
  throw err;
31
32
  }
32
- await writeFile(configPath, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n");
33
+ await writeFile(DEFAULT_CONFIG_PATH, JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n");
33
34
  }
34
35
  function deepMerge(target, source) {
35
36
  const result = { ...target };
@@ -28,7 +28,7 @@ export interface GatewayConfig {
28
28
  };
29
29
  }
30
30
  export interface MemoryConfig {
31
- /** Path to memory directory (default: .leedab/memory) */
31
+ /** Path to memory directory (default: ~/.leedab/memory) */
32
32
  dir: string;
33
33
  /** Enable vector search if embedding provider is set */
34
34
  vectorSearch: boolean;
@@ -8,6 +8,7 @@ import { addEntry, removeEntry, listEntries } from "../vault.js";
8
8
  import { readAuditLog } from "../audit.js";
9
9
  import { getAnalytics } from "../analytics.js";
10
10
  import { loadTeam, addMember, removeMember, updateRole } from "../team.js";
11
+ import { STATE_DIR } from "../paths.js";
11
12
  const execFileAsync = promisify(execFile);
12
13
  async function restartGatewayQuietly(stateDir) {
13
14
  try {
@@ -22,7 +23,7 @@ async function restartGatewayQuietly(stateDir) {
22
23
  }
23
24
  export function createRoutes(config) {
24
25
  const bin = resolveOpenClawBin();
25
- const stateDir = resolve(".leedab");
26
+ const stateDir = STATE_DIR;
26
27
  return {
27
28
  /**
28
29
  * POST /api/chat — send a message to the agent, get a response
package/dist/gateway.js CHANGED
@@ -6,6 +6,7 @@ import { fileURLToPath } from "node:url";
6
6
  import { promisify } from "node:util";
7
7
  import { resolveOpenClawBin, openclawEnv } from "./openclaw.js";
8
8
  import { ensureLicense } from "./license.js";
9
+ import { STATE_DIR } from "./paths.js";
9
10
  const execFileAsync = promisify(execFile);
10
11
  let gatewayProcess = null;
11
12
  /**
@@ -18,7 +19,7 @@ export async function startGateway(config) {
18
19
  throw new Error("No valid license found. Run `leedab onboard` to enter your license key.");
19
20
  }
20
21
  const bin = resolveOpenClawBin();
21
- const stateDir = resolve(".leedab");
22
+ const stateDir = STATE_DIR;
22
23
  const env = openclawEnv(stateDir);
23
24
  // Seed workspace templates (skip files that already exist)
24
25
  await seedWorkspace();
@@ -64,7 +65,7 @@ export async function startGateway(config) {
64
65
  }
65
66
  export async function stopGateway() {
66
67
  const bin = resolveOpenClawBin();
67
- const stateDir = resolve(".leedab");
68
+ const stateDir = STATE_DIR;
68
69
  try {
69
70
  await execFileAsync(bin, ["gateway", "stop"], {
70
71
  env: openclawEnv(stateDir),
@@ -199,7 +200,7 @@ async function seedWorkspace() {
199
200
  }
200
201
  async function waitForGateway(port, timeoutMs = 20000) {
201
202
  const bin = resolveOpenClawBin();
202
- const stateDir = resolve(".leedab");
203
+ const stateDir = STATE_DIR;
203
204
  const start = Date.now();
204
205
  while (Date.now() - start < timeoutMs) {
205
206
  try {
package/dist/license.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readFile, writeFile, mkdir } from "node:fs/promises";
2
2
  import { resolve } from "node:path";
3
- const LICENSE_PATH = resolve(".leedab", "license.json");
3
+ import { STATE_DIR } from "./paths.js";
4
+ const LICENSE_PATH = resolve(STATE_DIR, "license.json");
4
5
  const VERIFY_URL = "https://api.leedab.com/api/v1/licensing/verify";
5
6
  const REVALIDATE_DAYS = 7;
6
7
  /**
@@ -40,7 +41,7 @@ export async function validateLicenseKey(key) {
40
41
  * Save license info to disk.
41
42
  */
42
43
  export async function saveLicense(license) {
43
- await mkdir(resolve(".leedab"), { recursive: true });
44
+ await mkdir(STATE_DIR, { recursive: true });
44
45
  await writeFile(LICENSE_PATH, JSON.stringify(license, null, 2) + "\n");
45
46
  }
46
47
  /**
@@ -12,6 +12,7 @@ import { DEFAULT_CONFIG } from "../config/schema.js";
12
12
  import { initMemory } from "../memory/index.js";
13
13
  import { addEntry, listEntries } from "../vault.js";
14
14
  import { validateLicenseKey, saveLicense, loadLicense } from "../license.js";
15
+ import { STATE_DIR } from "../paths.js";
15
16
  /**
16
17
  * Run the full onboarding wizard. Writes leedab.config.json and .leedab/ secrets.
17
18
  */
@@ -198,15 +199,16 @@ export async function runOnboard() {
198
199
  chalk.cyan("leedab vault add <service>"));
199
200
  }
200
201
  // --- Write everything ---
201
- const configDir = resolve(".leedab");
202
+ const configDir = STATE_DIR;
202
203
  await mkdir(configDir, { recursive: true });
203
204
  // Write config (no secrets)
204
- await writeFile(resolve("leedab.config.json"), JSON.stringify(config, null, 2) + "\n");
205
+ await writeFile(resolve(STATE_DIR, "config.json"), JSON.stringify(config, null, 2) + "\n");
205
206
  // Write secrets separately
206
207
  if (Object.keys(secrets).length > 0) {
207
208
  await writeFile(resolve(configDir, "secrets.json"), JSON.stringify(secrets, null, 2) + "\n");
208
209
  }
209
210
  // Init memory directory
211
+ config.memory.dir = resolve(STATE_DIR, "memory");
210
212
  await initMemory(config.memory.dir);
211
213
  // Summary
212
214
  console.log(chalk.bold("\n Setup complete!\n"));
@@ -229,10 +231,10 @@ export async function runOnboard() {
229
231
  if (vaultEntries.length > 0) {
230
232
  console.log(chalk.green(` Vault: ${vaultEntries.length} service(s) stored`));
231
233
  }
232
- console.log(chalk.dim(`\n Config: ./leedab.config.json`));
233
- console.log(chalk.dim(` Secrets: ./.leedab/secrets.json`));
234
- console.log(chalk.dim(` Vault: ./.leedab/vault.json`));
235
- console.log(chalk.dim(` Memory: ./${config.memory.dir}/`));
234
+ console.log(chalk.dim(`\n Config: ~/.leedab/config.json`));
235
+ console.log(chalk.dim(` Secrets: ~/.leedab/secrets.json`));
236
+ console.log(chalk.dim(` Vault: ~/.leedab/vault.json`));
237
+ console.log(chalk.dim(` Memory: ~/.leedab/memory/`));
236
238
  console.log(chalk.bold(`\n Start your agent: ${chalk.cyan("leedab start")}\n`));
237
239
  }
238
240
  /**
@@ -5,6 +5,7 @@ import { promisify } from "node:util";
5
5
  import inquirer from "inquirer";
6
6
  import chalk from "chalk";
7
7
  import { resolveOpenClawBin, openclawEnv } from "../../openclaw.js";
8
+ import { STATE_DIR } from "../../paths.js";
8
9
  const execFileAsync = promisify(execFile);
9
10
  const PROVIDERS = [
10
11
  {
@@ -164,7 +165,7 @@ async function passKeyToOpenClaw(flag, apiKey) {
164
165
  * Write the chosen model into .leedab/openclaw.json so the gateway actually uses it.
165
166
  */
166
167
  export async function updateOpenClawModel(openclawModel) {
167
- const configPath = resolve(".leedab", "openclaw.json");
168
+ const configPath = resolve(STATE_DIR, "openclaw.json");
168
169
  try {
169
170
  const raw = JSON.parse(await readFile(configPath, "utf-8"));
170
171
  if (!raw.agents)
package/dist/openclaw.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  import { dirname, resolve } from "node:path";
3
+ import { STATE_DIR } from "./paths.js";
3
4
  const require = createRequire(import.meta.url);
4
5
  /**
5
6
  * Resolve the OpenClaw binary path relative to the leedab package,
@@ -15,6 +16,6 @@ export function resolveOpenClawBin() {
15
16
  export function openclawEnv(stateDir) {
16
17
  return {
17
18
  ...process.env,
18
- OPENCLAW_STATE_DIR: stateDir ?? resolve(".leedab"),
19
+ OPENCLAW_STATE_DIR: stateDir ?? STATE_DIR,
19
20
  };
20
21
  }
@@ -0,0 +1,2 @@
1
+ /** Global LeedAB state directory - always ~/.leedab */
2
+ export declare const STATE_DIR: string;
package/dist/paths.js ADDED
@@ -0,0 +1,4 @@
1
+ import { resolve } from "node:path";
2
+ import { homedir } from "node:os";
3
+ /** Global LeedAB state directory - always ~/.leedab */
4
+ export const STATE_DIR = resolve(homedir(), ".leedab");
package/dist/vault.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { readFile, writeFile, mkdir } from "node:fs/promises";
2
2
  import { resolve, dirname } from "node:path";
3
3
  import { randomBytes, scryptSync, createCipheriv, createDecipheriv } from "node:crypto";
4
- const VAULT_PATH = resolve(".leedab", "vault.json");
4
+ import { STATE_DIR } from "./paths.js";
5
+ const VAULT_PATH = resolve(STATE_DIR, "vault.json");
5
6
  const MAGIC = "LEEDAB_VAULT_V1";
6
7
  const ALGORITHM = "aes-256-gcm";
7
8
  async function ensureDir() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leedab",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
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>",