leedab 0.2.5 → 0.3.0

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
@@ -11,7 +11,7 @@ import { startGateway, stopGateway } from "../dist/gateway.js";
11
11
  import { loadConfig, initConfig } from "../dist/config/index.js";
12
12
  import { setupChannels } from "../dist/channels/index.js";
13
13
  import { runOnboard } from "../dist/onboard/index.js";
14
- import { startDashboard } from "../dist/dashboard/server.js";
14
+ import { startConsole, stopConsole } from "../dist/console-launcher.js";
15
15
  import { execBranded } from "../dist/brand.js";
16
16
  import { resolveOpenClawBin, openclawEnv } from "../dist/openclaw.js";
17
17
  import { addEntry, removeEntry, listEntries, encryptVault, decryptVault } from "../dist/vault.js";
@@ -174,12 +174,38 @@ program
174
174
  .option("-c, --config <path>", "Path to config file", resolve(STATE_DIR, "config.json"))
175
175
  .option("-p, --dashboard-port <port>", "Dashboard port", "3000")
176
176
  .action(async (opts) => {
177
+ const startupMessages = [
178
+ "Loading skills...",
179
+ "Waking up the agent...",
180
+ "Connecting channels...",
181
+ "Warming up the model...",
182
+ "Syncing memory...",
183
+ "Checking in with the gateway...",
184
+ "Polishing the SOUL...",
185
+ "Calibrating heartbeat...",
186
+ "Reading the workspace...",
187
+ "Tuning the agent's instincts...",
188
+ "Pouring the coffee...",
189
+ "Almost there...",
190
+ ];
177
191
  const spinner = ora("Starting LeedAB agent...").start();
192
+ let lastIdx = -1;
193
+ const rotate = setInterval(() => {
194
+ let next;
195
+ do {
196
+ next = Math.floor(Math.random() * startupMessages.length);
197
+ } while (next === lastIdx && startupMessages.length > 1);
198
+ lastIdx = next;
199
+ spinner.text = startupMessages[next];
200
+ }, 4500);
178
201
  try {
179
202
  const config = await loadConfig(opts.config);
180
203
  const { logPath } = await startGateway(config);
204
+ clearInterval(rotate);
181
205
  spinner.succeed(chalk.green("LeedAB is live"));
182
- await startDashboard(config, parseInt(opts.dashboardPort));
206
+
207
+ // Boot the Next.js console (apps/console).
208
+ const { logPath: consoleLogPath } = await startConsole(parseInt(opts.dashboardPort));
183
209
 
184
210
  const enabledChannels = Object.entries(config.channels)
185
211
  .filter(([_, v]) => v?.enabled)
@@ -190,14 +216,16 @@ program
190
216
  console.log();
191
217
  const dashUrl = `http://localhost:${opts.dashboardPort}`;
192
218
  const link = `\x1b]8;;${dashUrl}\x1b\\${dashUrl}\x1b]8;;\x1b\\`;
193
- console.log(` ${chalk.cyan(link)} Dashboard`);
219
+ console.log(` ${chalk.cyan(link)} Console`);
194
220
  if (enabledChannels.length > 0) {
195
221
  console.log(` ${chalk.green("Channels")} ${enabledChannels.join(", ")}`);
196
222
  }
197
223
  console.log();
198
- console.log(chalk.dim(` Logs: ${logPath}`));
224
+ console.log(chalk.dim(` Gateway log: ${logPath}`));
225
+ console.log(chalk.dim(` Console log: ${consoleLogPath}`));
199
226
  console.log();
200
227
  } catch (err) {
228
+ clearInterval(rotate);
201
229
  spinner.fail(chalk.red(`Failed to start: ${err.message}`));
202
230
  process.exit(1);
203
231
  }
@@ -205,14 +233,14 @@ program
205
233
 
206
234
  program
207
235
  .command("dashboard")
208
- .description("Open the setup dashboard (without starting the agent)")
236
+ .description("Open the LeedAB console (without starting the agent)")
209
237
  .option("-c, --config <path>", "Path to config file", resolve(STATE_DIR, "config.json"))
210
- .option("-p, --port <port>", "Dashboard port", "3000")
238
+ .option("-p, --port <port>", "Console port", "3000")
211
239
  .action(async (opts) => {
212
240
  try {
213
- const config = await loadConfig(opts.config);
214
- console.log(chalk.bold("\n LeedAB Dashboard\n"));
215
- await startDashboard(config, parseInt(opts.port));
241
+ await loadConfig(opts.config);
242
+ console.log(chalk.bold("\n LeedAB Console\n"));
243
+ await startConsole(parseInt(opts.port));
216
244
  console.log(chalk.dim(" Open the URL above in your browser.\n"));
217
245
  } catch (err) {
218
246
  console.error(chalk.red(`Failed: ${err.message}`));
@@ -258,6 +286,7 @@ program
258
286
  .command("stop")
259
287
  .description("Stop the LeedAB agent")
260
288
  .action(async () => {
289
+ await stopConsole().catch(() => {});
261
290
  await stopGateway();
262
291
  console.log(chalk.green("LeedAB agent stopped."));
263
292
  });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Start the console subprocess on the given port. Resolves once the
3
+ * subprocess is alive (we don't poll the port; `next start` exits on its
4
+ * own if the port is taken or the build is missing, so a process-error
5
+ * would surface there).
6
+ */
7
+ export declare function startConsole(port?: number): Promise<{
8
+ url: string;
9
+ logPath: string;
10
+ }>;
11
+ export declare function stopConsole(): Promise<void>;
@@ -0,0 +1,184 @@
1
+ // Spawn the LeedAB console (Next.js app at apps/console) as a subprocess.
2
+ // `leedab start` and `leedab dashboard` use this to swap the legacy
3
+ // src/dashboard server for the modern Next.js admin UI.
4
+ //
5
+ // Production-only: requires a built `.next` directory (`npm run build`
6
+ // inside apps/console). For dev, run `npm run dev` from apps/console
7
+ // directly — that gives hot reload and is independent of the gateway.
8
+ import { spawn } from "node:child_process";
9
+ import { createWriteStream, existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
10
+ import { mkdir } from "node:fs/promises";
11
+ import { resolve, dirname } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+ import { STATE_DIR } from "./paths.js";
14
+ let consoleProcess = null;
15
+ // Cross-process handle for `leedab stop`. The leedab start process and the
16
+ // leedab stop process are different OS processes, so an in-memory ref to
17
+ // the spawned console isn't enough — stop reads the PID from this file.
18
+ const PID_FILE = resolve(STATE_DIR, "console.pid");
19
+ function writePidFile(pid) {
20
+ try {
21
+ writeFileSync(PID_FILE, String(pid), "utf8");
22
+ }
23
+ catch {
24
+ // best-effort — losing the pid file just means leedab stop falls back
25
+ // to the in-process handle, which is correct in the common case.
26
+ }
27
+ }
28
+ function readPidFile() {
29
+ try {
30
+ const raw = readFileSync(PID_FILE, "utf8").trim();
31
+ const pid = Number.parseInt(raw, 10);
32
+ return Number.isFinite(pid) && pid > 0 ? pid : null;
33
+ }
34
+ catch {
35
+ return null;
36
+ }
37
+ }
38
+ function clearPidFile() {
39
+ try {
40
+ unlinkSync(PID_FILE);
41
+ }
42
+ catch {
43
+ // already gone, fine
44
+ }
45
+ }
46
+ function isAlive(pid) {
47
+ try {
48
+ process.kill(pid, 0);
49
+ return true;
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
55
+ /** Walk up from this module to find the leedab repo root. */
56
+ function findRepoRoot() {
57
+ let dir = dirname(fileURLToPath(import.meta.url));
58
+ for (let i = 0; i < 10; i++) {
59
+ const pkgPath = resolve(dir, "package.json");
60
+ if (existsSync(pkgPath)) {
61
+ try {
62
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
63
+ if (pkg.name === "leedab")
64
+ return dir;
65
+ }
66
+ catch {
67
+ // ignore unreadable / non-JSON
68
+ }
69
+ }
70
+ const parent = resolve(dir, "..");
71
+ if (parent === dir)
72
+ break;
73
+ dir = parent;
74
+ }
75
+ throw new Error("Could not locate the leedab repo root from the console launcher.");
76
+ }
77
+ function consoleDir() {
78
+ return resolve(findRepoRoot(), "apps", "console");
79
+ }
80
+ /**
81
+ * Start the console subprocess on the given port. Resolves once the
82
+ * subprocess is alive (we don't poll the port; `next start` exits on its
83
+ * own if the port is taken or the build is missing, so a process-error
84
+ * would surface there).
85
+ */
86
+ export async function startConsole(port = 3000) {
87
+ const dir = consoleDir();
88
+ const buildDir = resolve(dir, ".next");
89
+ if (!existsSync(buildDir)) {
90
+ throw new Error(`Console build not found at ${buildDir}.\n` +
91
+ `Run \`cd ${dir} && npm run build\` once, then retry.`);
92
+ }
93
+ const nextBin = resolve(dir, "node_modules", ".bin", "next");
94
+ if (!existsSync(nextBin)) {
95
+ throw new Error(`Next.js binary not found at ${nextBin}.\n` +
96
+ `Run \`cd ${dir} && npm install\` first.`);
97
+ }
98
+ const logDir = resolve(STATE_DIR, "logs");
99
+ await mkdir(logDir, { recursive: true });
100
+ const today = new Date().toISOString().slice(0, 10);
101
+ const logPath = resolve(logDir, `console-${today}.log`);
102
+ const logStream = createWriteStream(logPath, { flags: "a" });
103
+ // If a previous run left a stale pid, clear it before spawning so a
104
+ // failed start doesn't leave us pointing at a dead process.
105
+ const stale = readPidFile();
106
+ if (stale && !isAlive(stale))
107
+ clearPidFile();
108
+ consoleProcess = spawn(nextBin, ["start", "-H", "127.0.0.1", "-p", String(port)], {
109
+ cwd: dir,
110
+ env: {
111
+ ...process.env,
112
+ LEEDAB_STATE_DIR: STATE_DIR,
113
+ NODE_ENV: "production",
114
+ },
115
+ stdio: ["inherit", "pipe", "pipe"],
116
+ });
117
+ if (consoleProcess.pid)
118
+ writePidFile(consoleProcess.pid);
119
+ if (consoleProcess.stdout)
120
+ consoleProcess.stdout.pipe(logStream);
121
+ if (consoleProcess.stderr)
122
+ consoleProcess.stderr.pipe(logStream);
123
+ consoleProcess.on("error", (err) => {
124
+ console.error(`Console process error: ${err.message}`);
125
+ });
126
+ consoleProcess.on("exit", () => {
127
+ // If the subprocess crashed or was killed externally, drop the pid
128
+ // file so the next stop/start sequence isn't confused.
129
+ clearPidFile();
130
+ });
131
+ // Clean up on parent SIGTERM/SIGINT alongside the gateway.
132
+ const cleanup = () => {
133
+ if (consoleProcess) {
134
+ consoleProcess.kill("SIGTERM");
135
+ consoleProcess = null;
136
+ }
137
+ clearPidFile();
138
+ };
139
+ process.once("SIGTERM", cleanup);
140
+ process.once("SIGINT", cleanup);
141
+ return { url: `http://localhost:${port}`, logPath };
142
+ }
143
+ export async function stopConsole() {
144
+ // Same-process path first: if we hold the handle, kill directly.
145
+ if (consoleProcess) {
146
+ consoleProcess.kill("SIGTERM");
147
+ consoleProcess = null;
148
+ clearPidFile();
149
+ return;
150
+ }
151
+ // Cross-process path (`leedab stop` from a different terminal): signal
152
+ // the PID we wrote at start time. If it's already gone or was reaped,
153
+ // clear the stale file and return cleanly.
154
+ const pid = readPidFile();
155
+ if (!pid)
156
+ return;
157
+ if (!isAlive(pid)) {
158
+ clearPidFile();
159
+ return;
160
+ }
161
+ try {
162
+ process.kill(pid, "SIGTERM");
163
+ }
164
+ catch {
165
+ // already gone in the race window — fine.
166
+ }
167
+ // Give next a moment to flush + exit; if it's still alive after the
168
+ // grace window, escalate to SIGKILL so the operator isn't left with a
169
+ // zombie they have to hunt with `lsof`.
170
+ for (let i = 0; i < 20; i++) {
171
+ if (!isAlive(pid))
172
+ break;
173
+ await new Promise((r) => setTimeout(r, 250));
174
+ }
175
+ if (isAlive(pid)) {
176
+ try {
177
+ process.kill(pid, "SIGKILL");
178
+ }
179
+ catch {
180
+ // already gone
181
+ }
182
+ }
183
+ clearPidFile();
184
+ }
package/dist/gateway.js CHANGED
@@ -25,6 +25,10 @@ export async function startGateway(config) {
25
25
  await seedWorkspace();
26
26
  // Auto-approve all tool executions (enterprise local deployment)
27
27
  await ensureAutoApprove(bin, env);
28
+ // Make sure the OpenAI-compat /v1/chat/completions HTTP endpoint is on.
29
+ // The Next.js console proxies every chat through it, and OpenClaw ships
30
+ // it disabled by default.
31
+ await ensureChatCompletionsEndpoint(stateDir);
28
32
  // Register channels before starting gateway
29
33
  await registerChannels(bin, stateDir, config);
30
34
  // Start gateway, pipe output to log file instead of terminal
@@ -129,6 +133,41 @@ async function registerChannels(bin, stateDir, config) {
129
133
  }
130
134
  }
131
135
  }
136
+ /**
137
+ * Idempotently flip `gateway.http.endpoints.chatCompletions.enabled` to true
138
+ * in ~/.leedab/openclaw.json. The console (Next.js, apps/console) proxies
139
+ * every chat call through that endpoint, and OpenClaw ships it disabled.
140
+ *
141
+ * Safe to run on every startGateway: if the file is missing (first run
142
+ * before onboard) we no-op; if the flag is already true we don't write.
143
+ */
144
+ async function ensureChatCompletionsEndpoint(stateDir) {
145
+ const configPath = resolve(stateDir, "openclaw.json");
146
+ let raw;
147
+ try {
148
+ raw = await readFile(configPath, "utf-8");
149
+ }
150
+ catch {
151
+ // No openclaw.json yet — onboard hasn't run. Skip; openclaw will create
152
+ // the file and a subsequent `leedab start` will flip the flag.
153
+ return;
154
+ }
155
+ let cfg;
156
+ try {
157
+ cfg = JSON.parse(raw);
158
+ }
159
+ catch {
160
+ return;
161
+ }
162
+ cfg.gateway = cfg.gateway ?? {};
163
+ cfg.gateway.http = cfg.gateway.http ?? {};
164
+ cfg.gateway.http.endpoints = cfg.gateway.http.endpoints ?? {};
165
+ cfg.gateway.http.endpoints.chatCompletions = cfg.gateway.http.endpoints.chatCompletions ?? {};
166
+ if (cfg.gateway.http.endpoints.chatCompletions.enabled === true)
167
+ return;
168
+ cfg.gateway.http.endpoints.chatCompletions.enabled = true;
169
+ await writeFile(configPath, JSON.stringify(cfg, null, 2) + "\n");
170
+ }
132
171
  /**
133
172
  * Set up exec-approvals so the agent can run tools without prompting.
134
173
  * Safe for local enterprise deployments where the admin controls the box.
@@ -198,16 +237,17 @@ async function seedWorkspace() {
198
237
  console.warn("[leedab] workspace seed failed:", err);
199
238
  }
200
239
  }
201
- async function waitForGateway(port, timeoutMs = 20000) {
240
+ async function waitForGateway(port, timeoutMs = 90000) {
202
241
  const bin = resolveOpenClawBin();
203
242
  const stateDir = STATE_DIR;
204
243
  const start = Date.now();
205
244
  while (Date.now() - start < timeoutMs) {
206
245
  try {
207
- // Use openclaw's own health check which knows the WS protocol
246
+ // Use openclaw's own health check which knows the WS protocol.
247
+ // Child timeout must exceed openclaw CLI cold-start time (~15s).
208
248
  await execFileAsync(bin, ["gateway", "health"], {
209
249
  env: openclawEnv(stateDir),
210
- timeout: 5000,
250
+ timeout: 30000,
211
251
  });
212
252
  return;
213
253
  }
package/dist/index.d.ts CHANGED
@@ -2,5 +2,5 @@ export { startGateway, stopGateway } from "./gateway.js";
2
2
  export { loadConfig, initConfig } from "./config/index.js";
3
3
  export { setupChannels } from "./channels/index.js";
4
4
  export { runOnboard } from "./onboard/index.js";
5
- export { startDashboard } from "./dashboard/server.js";
5
+ export { startConsole, stopConsole } from "./console-launcher.js";
6
6
  export type { LeedABConfig } from "./config/schema.js";
package/dist/index.js CHANGED
@@ -2,4 +2,4 @@ export { startGateway, stopGateway } from "./gateway.js";
2
2
  export { loadConfig, initConfig } from "./config/index.js";
3
3
  export { setupChannels } from "./channels/index.js";
4
4
  export { runOnboard } from "./onboard/index.js";
5
- export { startDashboard } from "./dashboard/server.js";
5
+ export { startConsole, stopConsole } from "./console-launcher.js";
package/dist/license.d.ts CHANGED
@@ -6,6 +6,8 @@ export interface LicenseInfo {
6
6
  seatsUsed: number;
7
7
  maxSeats: number;
8
8
  validatedAt: string;
9
+ expiresAt?: string;
10
+ signature?: string;
9
11
  email?: string;
10
12
  name?: string;
11
13
  orgName?: string;
package/dist/license.js CHANGED
@@ -38,6 +38,8 @@ export async function validateLicenseKey(key) {
38
38
  seatsUsed: data.seats_used ?? 0,
39
39
  maxSeats: data.max_seats ?? 1,
40
40
  validatedAt: new Date().toISOString(),
41
+ expiresAt: data.expires_at ?? undefined,
42
+ signature: data.signature ?? undefined,
41
43
  email: data.email ?? undefined,
42
44
  name: data.name ?? undefined,
43
45
  orgName: data.org_name ?? undefined,
@@ -5,6 +5,12 @@ export interface ProviderResult {
5
5
  }
6
6
  /**
7
7
  * Write the chosen model into .leedab/openclaw.json so the gateway actually uses it.
8
+ *
9
+ * Allowlist also gets every sibling model for the chosen provider, so the console's
10
+ * per-agent defaultModel (which can vary across COO, Procurement, CX, etc.) is
11
+ * accepted without re-onboarding. A single-model allowlist forces every agent to
12
+ * the same backend and surfaces as "Model X is not allowed for agent main" the
13
+ * first time a workflow routes through a non-primary agent.
8
14
  */
9
- export declare function updateOpenClawModel(openclawModel: string): Promise<void>;
15
+ export declare function updateOpenClawModel(openclawModel: string, providerValue?: string): Promise<void>;
10
16
  export declare function onboardProvider(): Promise<ProviderResult | null>;
@@ -12,9 +12,9 @@ const PROVIDERS = [
12
12
  name: "Anthropic (Claude)", value: "anthropic", flag: "--anthropic-api-key", envVar: "ANTHROPIC_API_KEY",
13
13
  defaultModelIndex: 1,
14
14
  models: [
15
- { name: "Claude Opus 4.6 — most capable", value: "claude-opus-4-6", openclaw: "anthropic/claude-opus-4-6" },
15
+ { name: "Claude Opus 4.7 — most capable", value: "claude-opus-4-7", openclaw: "anthropic/claude-opus-4-7" },
16
16
  { name: "Claude Sonnet 4.6 — fast, capable (recommended)", value: "claude-sonnet-4-6", openclaw: "anthropic/claude-sonnet-4-6" },
17
- { name: "Claude Haiku 4.5 — fastest, cheapest", value: "claude-haiku-4-5", openclaw: "anthropic/claude-haiku-4-5" },
17
+ { name: "Claude Haiku 4.5 — fastest, cheapest", value: "claude-haiku-4-5", openclaw: "anthropic/claude-haiku-4-5-20251001" },
18
18
  ],
19
19
  },
20
20
  {
@@ -55,8 +55,8 @@ const PROVIDERS = [
55
55
  defaultModelIndex: 0,
56
56
  models: [
57
57
  { name: "Claude Sonnet 4.6 via Bedrock", value: "anthropic.claude-sonnet-4-6", openclaw: "anthropic/claude-sonnet-4-6" },
58
- { name: "Claude Opus 4.6 via Bedrock", value: "anthropic.claude-opus-4-6", openclaw: "anthropic/claude-opus-4-6" },
59
- { name: "Claude Haiku 4.5 via Bedrock", value: "anthropic.claude-haiku-4-5", openclaw: "anthropic/claude-haiku-4-5" },
58
+ { name: "Claude Opus 4.7 via Bedrock", value: "anthropic.claude-opus-4-7", openclaw: "anthropic/claude-opus-4-7" },
59
+ { name: "Claude Haiku 4.5 via Bedrock", value: "anthropic.claude-haiku-4-5", openclaw: "anthropic/claude-haiku-4-5-20251001" },
60
60
  ],
61
61
  },
62
62
  {
@@ -163,8 +163,14 @@ async function passKeyToOpenClaw(flag, apiKey) {
163
163
  }
164
164
  /**
165
165
  * Write the chosen model into .leedab/openclaw.json so the gateway actually uses it.
166
+ *
167
+ * Allowlist also gets every sibling model for the chosen provider, so the console's
168
+ * per-agent defaultModel (which can vary across COO, Procurement, CX, etc.) is
169
+ * accepted without re-onboarding. A single-model allowlist forces every agent to
170
+ * the same backend and surfaces as "Model X is not allowed for agent main" the
171
+ * first time a workflow routes through a non-primary agent.
166
172
  */
167
- export async function updateOpenClawModel(openclawModel) {
173
+ export async function updateOpenClawModel(openclawModel, providerValue) {
168
174
  const configPath = resolve(STATE_DIR, "openclaw.json");
169
175
  try {
170
176
  const raw = JSON.parse(await readFile(configPath, "utf-8"));
@@ -173,7 +179,17 @@ export async function updateOpenClawModel(openclawModel) {
173
179
  if (!raw.agents.defaults)
174
180
  raw.agents.defaults = {};
175
181
  raw.agents.defaults.model = { primary: openclawModel };
176
- raw.agents.defaults.models = { [openclawModel]: {} };
182
+ const allowed = new Set([openclawModel]);
183
+ if (providerValue) {
184
+ const provider = PROVIDERS.find(p => p.value === providerValue);
185
+ if (provider)
186
+ for (const m of provider.models)
187
+ allowed.add(m.openclaw);
188
+ }
189
+ const models = {};
190
+ for (const id of allowed)
191
+ models[id] = {};
192
+ raw.agents.defaults.models = models;
177
193
  await writeFile(configPath, JSON.stringify(raw, null, 2) + "\n");
178
194
  }
179
195
  catch {
@@ -203,7 +219,7 @@ export async function onboardProvider() {
203
219
  if (existing.provider.flag) {
204
220
  await passKeyToOpenClaw(existing.provider.flag, apiKey);
205
221
  }
206
- await updateOpenClawModel(model.openclaw);
222
+ await updateOpenClawModel(model.openclaw, existing.provider.value);
207
223
  console.log(chalk.green(` Using ${existing.provider.name}. Model: ${model.value}`));
208
224
  return {
209
225
  provider: existing.provider.value,
@@ -238,7 +254,7 @@ export async function onboardProvider() {
238
254
  }
239
255
  const bedrockProvider = PROVIDERS.find((p) => p.value === "bedrock");
240
256
  const model = await pickModel(bedrockProvider);
241
- await updateOpenClawModel(model.openclaw);
257
+ await updateOpenClawModel(model.openclaw, "bedrock");
242
258
  return {
243
259
  provider: "bedrock",
244
260
  apiKey: "",
@@ -283,7 +299,7 @@ export async function onboardProvider() {
283
299
  await passKeyToOpenClaw(selected.flag, trimmedKey);
284
300
  }
285
301
  const model = await pickModel(selected);
286
- await updateOpenClawModel(model.openclaw);
302
+ await updateOpenClawModel(model.openclaw, selected.value);
287
303
  console.log(chalk.green(`\n Using ${selected.name}. Model: ${model.value}`));
288
304
  return {
289
305
  provider: selected.value,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leedab",
3
- "version": "0.2.5",
3
+ "version": "0.3.0",
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>",
@@ -16,7 +16,7 @@
16
16
  "LICENSE"
17
17
  ],
18
18
  "scripts": {
19
- "build": "tsc && rm -rf dist/dashboard/static dist/templates && cp -r src/dashboard/static dist/dashboard/static && cp -r src/templates dist/templates",
19
+ "build": "tsc && rm -rf dist/templates && cp -r src/templates dist/templates",
20
20
  "dev": "tsc --watch",
21
21
  "start": "node bin/leedab.js",
22
22
  "lint": "eslint src/",
@@ -31,7 +31,7 @@
31
31
  "conf": "^13.0.0",
32
32
  "grammy": "^1.41.1",
33
33
  "inquirer": "^12.11.1",
34
- "openclaw": "^2026.3.31",
34
+ "openclaw": "2026.4.15",
35
35
  "ora": "^8.1.0"
36
36
  },
37
37
  "devDependencies": {
@@ -1,17 +0,0 @@
1
- import { type IncomingMessage, type ServerResponse } from "node:http";
2
- import type { LeedABConfig } from "../config/schema.js";
3
- import { type ChannelName } from "../team/permissions.js";
4
- type RouteHandler = (req: IncomingMessage, res: ServerResponse, url: URL) => Promise<void>;
5
- export declare function createRoutes(config: LeedABConfig): Record<string, RouteHandler>;
6
- /**
7
- * Build the permission preamble injected into the agent prompt for a given
8
- * channel message. Returns null when we have nothing to add (unknown user
9
- * on a non-dashboard channel — the allowlist will already have rejected
10
- * those).
11
- *
12
- * This preamble is the sole workflow-permission gate. We trust the agent to
13
- * honor it. There is no server-side hard gate; admins restrict access by
14
- * naming the workflows a member may use in the team page.
15
- */
16
- export declare function buildPermissionPreamble(channel: ChannelName, userId: string): Promise<string | null>;
17
- export {};