@askexenow/exe-os 0.8.0 → 0.8.1

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.
Files changed (90) hide show
  1. package/README.md +178 -79
  2. package/dist/bin/backfill-responses.js +160 -8
  3. package/dist/bin/backfill-vectors.js +130 -1
  4. package/dist/bin/cleanup-stale-review-tasks.js +130 -1
  5. package/dist/bin/cli.js +10111 -7540
  6. package/dist/bin/exe-agent.js +159 -1
  7. package/dist/bin/exe-assign.js +235 -16
  8. package/dist/bin/exe-boot.js +344 -472
  9. package/dist/bin/exe-call.js +145 -1
  10. package/dist/bin/exe-cloud.js +11 -0
  11. package/dist/bin/exe-dispatch.js +37 -24
  12. package/dist/bin/exe-doctor.js +130 -1
  13. package/dist/bin/exe-export-behaviors.js +150 -7
  14. package/dist/bin/exe-forget.js +822 -665
  15. package/dist/bin/exe-gateway.js +470 -62
  16. package/dist/bin/exe-heartbeat.js +133 -2
  17. package/dist/bin/exe-kill.js +150 -7
  18. package/dist/bin/exe-launch-agent.js +150 -7
  19. package/dist/bin/exe-new-employee.js +756 -224
  20. package/dist/bin/exe-pending-messages.js +132 -2
  21. package/dist/bin/exe-pending-notifications.js +130 -1
  22. package/dist/bin/exe-pending-reviews.js +132 -2
  23. package/dist/bin/exe-review.js +160 -8
  24. package/dist/bin/exe-search.js +2473 -2008
  25. package/dist/bin/exe-session-cleanup.js +238 -51
  26. package/dist/bin/exe-settings.js +11 -0
  27. package/dist/bin/exe-status.js +130 -1
  28. package/dist/bin/exe-team.js +130 -1
  29. package/dist/bin/git-sweep.js +272 -16
  30. package/dist/bin/graph-backfill.js +150 -7
  31. package/dist/bin/graph-export.js +150 -7
  32. package/dist/bin/install.js +5 -0
  33. package/dist/bin/scan-tasks.js +238 -19
  34. package/dist/bin/setup.js +1776 -10
  35. package/dist/bin/shard-migrate.js +150 -7
  36. package/dist/bin/update.js +9 -6
  37. package/dist/bin/wiki-sync.js +150 -7
  38. package/dist/gateway/index.js +470 -62
  39. package/dist/hooks/bug-report-worker.js +195 -35
  40. package/dist/hooks/commit-complete.js +272 -16
  41. package/dist/hooks/error-recall.js +2313 -1847
  42. package/dist/hooks/exe-heartbeat-hook.js +5 -0
  43. package/dist/hooks/ingest-worker.js +330 -58
  44. package/dist/hooks/ingest.js +11 -0
  45. package/dist/hooks/instructions-loaded.js +199 -10
  46. package/dist/hooks/notification.js +199 -10
  47. package/dist/hooks/post-compact.js +199 -10
  48. package/dist/hooks/pre-compact.js +199 -10
  49. package/dist/hooks/pre-tool-use.js +199 -10
  50. package/dist/hooks/prompt-ingest-worker.js +179 -14
  51. package/dist/hooks/prompt-submit.js +781 -285
  52. package/dist/hooks/response-ingest-worker.js +1900 -1405
  53. package/dist/hooks/session-end.js +456 -12
  54. package/dist/hooks/session-start.js +2188 -1724
  55. package/dist/hooks/stop.js +200 -10
  56. package/dist/hooks/subagent-stop.js +199 -10
  57. package/dist/hooks/summary-worker.js +604 -334
  58. package/dist/index.js +554 -61
  59. package/dist/lib/cloud-sync.js +5 -0
  60. package/dist/lib/config.js +13 -0
  61. package/dist/lib/consolidation.js +5 -0
  62. package/dist/lib/database.js +104 -0
  63. package/dist/lib/device-registry.js +109 -0
  64. package/dist/lib/embedder.js +13 -0
  65. package/dist/lib/employee-templates.js +53 -26
  66. package/dist/lib/employees.js +5 -0
  67. package/dist/lib/exe-daemon-client.js +5 -0
  68. package/dist/lib/exe-daemon.js +493 -79
  69. package/dist/lib/file-grep.js +20 -4
  70. package/dist/lib/hybrid-search.js +1435 -190
  71. package/dist/lib/identity-templates.js +126 -5
  72. package/dist/lib/identity.js +5 -0
  73. package/dist/lib/license.js +5 -0
  74. package/dist/lib/messaging.js +37 -24
  75. package/dist/lib/schedules.js +130 -1
  76. package/dist/lib/skill-learning.js +11 -0
  77. package/dist/lib/status-brief.js +5 -0
  78. package/dist/lib/store.js +199 -10
  79. package/dist/lib/task-router.js +72 -6
  80. package/dist/lib/tasks.js +179 -50
  81. package/dist/lib/tmux-routing.js +179 -46
  82. package/dist/mcp/server.js +2129 -1855
  83. package/dist/mcp/tools/create-task.js +86 -36
  84. package/dist/mcp/tools/deactivate-behavior.js +5 -0
  85. package/dist/mcp/tools/list-tasks.js +39 -11
  86. package/dist/mcp/tools/send-message.js +37 -24
  87. package/dist/mcp/tools/update-task.js +153 -38
  88. package/dist/runtime/index.js +451 -59
  89. package/dist/tui/App.js +454 -59
  90. package/package.json +1 -1
@@ -38,6 +38,9 @@ async function initDatabase(config) {
38
38
  }
39
39
  _client = createClient(opts);
40
40
  }
41
+ function isInitialized() {
42
+ return _client !== null;
43
+ }
41
44
  function getClient() {
42
45
  if (!_client) {
43
46
  throw new Error("Database client not initialized. Call initDatabase() first.");
@@ -231,6 +234,27 @@ async function ensureSchema() {
231
234
  });
232
235
  } catch {
233
236
  }
237
+ try {
238
+ await client.execute({
239
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint TEXT`,
240
+ args: []
241
+ });
242
+ } catch {
243
+ }
244
+ try {
245
+ await client.execute({
246
+ sql: `ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER NOT NULL DEFAULT 0`,
247
+ args: []
248
+ });
249
+ } catch {
250
+ }
251
+ try {
252
+ await client.execute({
253
+ sql: `ALTER TABLE tasks ADD COLUMN complexity TEXT NOT NULL DEFAULT 'standard'`,
254
+ args: []
255
+ });
256
+ } catch {
257
+ }
234
258
  try {
235
259
  await client.execute({
236
260
  sql: `ALTER TABLE memories ADD COLUMN task_id TEXT`,
@@ -641,6 +665,15 @@ async function ensureSchema() {
641
665
  } catch {
642
666
  }
643
667
  }
668
+ for (const col of [
669
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
670
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'"
671
+ ]) {
672
+ try {
673
+ await client.execute(col);
674
+ } catch {
675
+ }
676
+ }
644
677
  await client.executeMultiple(`
645
678
  CREATE INDEX IF NOT EXISTS idx_memories_workspace
646
679
  ON memories(workspace_id);
@@ -705,6 +738,34 @@ async function ensureSchema() {
705
738
  CREATE INDEX IF NOT EXISTS idx_conversations_channel
706
739
  ON conversations(channel_id);
707
740
  `);
741
+ try {
742
+ await client.execute({
743
+ sql: `ALTER TABLE tasks ADD COLUMN budget_tokens INTEGER`,
744
+ args: []
745
+ });
746
+ } catch {
747
+ }
748
+ try {
749
+ await client.execute({
750
+ sql: `ALTER TABLE tasks ADD COLUMN budget_fallback_model TEXT`,
751
+ args: []
752
+ });
753
+ } catch {
754
+ }
755
+ try {
756
+ await client.execute({
757
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_used INTEGER DEFAULT 0`,
758
+ args: []
759
+ });
760
+ } catch {
761
+ }
762
+ try {
763
+ await client.execute({
764
+ sql: `ALTER TABLE tasks ADD COLUMN tokens_warned_at INTEGER`,
765
+ args: []
766
+ });
767
+ } catch {
768
+ }
708
769
  await client.executeMultiple(`
709
770
  CREATE VIRTUAL TABLE IF NOT EXISTS conversations_fts USING fts5(
710
771
  content_text,
@@ -731,6 +792,52 @@ async function ensureSchema() {
731
792
  VALUES (new.rowid, new.content_text, new.sender_name, new.agent_response);
732
793
  END;
733
794
  `);
795
+ try {
796
+ await client.execute({
797
+ sql: `ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3`,
798
+ args: []
799
+ });
800
+ } catch {
801
+ }
802
+ try {
803
+ await client.execute(
804
+ `CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)`
805
+ );
806
+ } catch {
807
+ }
808
+ try {
809
+ await client.execute({
810
+ sql: `UPDATE memories SET tier = 1 WHERE tool_name = 'commit_to_long_term_memory' AND importance >= 8 AND tier = 3`,
811
+ args: []
812
+ });
813
+ await client.execute({
814
+ sql: `UPDATE memories SET tier = 2 WHERE tool_name IN ('store_memory', 'manual') AND importance >= 5 AND tier = 3`,
815
+ args: []
816
+ });
817
+ } catch {
818
+ }
819
+ try {
820
+ await client.execute({
821
+ sql: `ALTER TABLE memories ADD COLUMN supersedes_id TEXT`,
822
+ args: []
823
+ });
824
+ } catch {
825
+ }
826
+ try {
827
+ await client.execute(
828
+ `CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL`
829
+ );
830
+ } catch {
831
+ }
832
+ for (const col of [
833
+ "ALTER TABLE tasks ADD COLUMN checkpoint TEXT",
834
+ "ALTER TABLE tasks ADD COLUMN checkpoint_count INTEGER DEFAULT 0"
835
+ ]) {
836
+ try {
837
+ await client.execute(col);
838
+ } catch {
839
+ }
840
+ }
734
841
  }
735
842
  var _client, initTurso;
736
843
  var init_database = __esm({
@@ -968,6 +1075,11 @@ function normalizeSessionLifecycle(raw) {
968
1075
  const userSL = raw.sessionLifecycle ?? {};
969
1076
  raw.sessionLifecycle = { ...defaultSL, ...userSL };
970
1077
  }
1078
+ function normalizeAutoUpdate(raw) {
1079
+ const defaultAU = DEFAULT_CONFIG.autoUpdate;
1080
+ const userAU = raw.autoUpdate ?? {};
1081
+ raw.autoUpdate = { ...defaultAU, ...userAU };
1082
+ }
971
1083
  async function loadConfig() {
972
1084
  const dir = process.env.EXE_OS_DIR ?? process.env.EXE_MEM_DIR ?? EXE_AI_DIR;
973
1085
  await mkdir2(dir, { recursive: true });
@@ -990,6 +1102,7 @@ async function loadConfig() {
990
1102
  }
991
1103
  normalizeScalingRoadmap(migratedCfg);
992
1104
  normalizeSessionLifecycle(migratedCfg);
1105
+ normalizeAutoUpdate(migratedCfg);
993
1106
  const config = { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
994
1107
  if (config.dbPath.startsWith("~")) {
995
1108
  config.dbPath = config.dbPath.replace(/^~/, os.homedir());
@@ -1012,6 +1125,7 @@ function loadConfigSync() {
1012
1125
  const { config: migratedCfg } = migrateConfig(parsed);
1013
1126
  normalizeScalingRoadmap(migratedCfg);
1014
1127
  normalizeSessionLifecycle(migratedCfg);
1128
+ normalizeAutoUpdate(migratedCfg);
1015
1129
  return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db"), ...migratedCfg };
1016
1130
  } catch {
1017
1131
  return { ...DEFAULT_CONFIG, dbPath: path2.join(dir, "memories.db") };
@@ -1031,6 +1145,7 @@ async function loadConfigFrom(configPath) {
1031
1145
  const { config: migratedCfg } = migrateConfig(parsed);
1032
1146
  normalizeScalingRoadmap(migratedCfg);
1033
1147
  normalizeSessionLifecycle(migratedCfg);
1148
+ normalizeAutoUpdate(migratedCfg);
1034
1149
  return { ...DEFAULT_CONFIG, ...migratedCfg };
1035
1150
  } catch {
1036
1151
  return { ...DEFAULT_CONFIG };
@@ -1102,6 +1217,11 @@ var init_config = __esm({
1102
1217
  idleKillTicksRequired: 3,
1103
1218
  idleKillIntercomAckWindowMs: 1e4,
1104
1219
  maxAutoInstances: 10
1220
+ },
1221
+ autoUpdate: {
1222
+ checkOnBoot: true,
1223
+ autoInstall: false,
1224
+ checkIntervalMs: 24 * 60 * 60 * 1e3
1105
1225
  }
1106
1226
  };
1107
1227
  CONFIG_MIGRATIONS = [
@@ -1235,13 +1355,27 @@ async function ensureShardSchema(client) {
1235
1355
  "ALTER TABLE memories ADD COLUMN document_id TEXT",
1236
1356
  "ALTER TABLE memories ADD COLUMN user_id TEXT",
1237
1357
  "ALTER TABLE memories ADD COLUMN char_offset INTEGER",
1238
- "ALTER TABLE memories ADD COLUMN page_number INTEGER"
1358
+ "ALTER TABLE memories ADD COLUMN page_number INTEGER",
1359
+ // Source provenance columns (must match database.ts)
1360
+ "ALTER TABLE memories ADD COLUMN source_path TEXT",
1361
+ "ALTER TABLE memories ADD COLUMN source_type TEXT DEFAULT 'text'",
1362
+ "ALTER TABLE memories ADD COLUMN tier INTEGER DEFAULT 3",
1363
+ "ALTER TABLE memories ADD COLUMN supersedes_id TEXT"
1239
1364
  ]) {
1240
1365
  try {
1241
1366
  await client.execute(col);
1242
1367
  } catch {
1243
1368
  }
1244
1369
  }
1370
+ for (const idx of [
1371
+ "CREATE INDEX IF NOT EXISTS idx_memories_tier ON memories(tier)",
1372
+ "CREATE INDEX IF NOT EXISTS idx_memories_supersedes ON memories(supersedes_id) WHERE supersedes_id IS NOT NULL"
1373
+ ]) {
1374
+ try {
1375
+ await client.execute(idx);
1376
+ } catch {
1377
+ }
1378
+ }
1245
1379
  try {
1246
1380
  await client.execute("CREATE INDEX IF NOT EXISTS idx_memories_status ON memories(status)");
1247
1381
  } catch {
@@ -1345,139 +1479,453 @@ var init_shard_manager = __esm({
1345
1479
  }
1346
1480
  });
1347
1481
 
1348
- // src/lib/exe-daemon-client.ts
1349
- import net from "net";
1350
- import { spawn } from "child_process";
1351
- import { randomUUID } from "crypto";
1352
- import { existsSync as existsSync5, unlinkSync as unlinkSync2, readFileSync as readFileSync3, openSync, closeSync, statSync } from "fs";
1482
+ // src/lib/employees.ts
1483
+ import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1484
+ import { existsSync as existsSync5, symlinkSync, readlinkSync } from "fs";
1485
+ import { execSync } from "child_process";
1353
1486
  import path5 from "path";
1354
- import { fileURLToPath } from "url";
1355
- function handleData(chunk) {
1356
- _buffer += chunk.toString();
1357
- let newlineIdx;
1358
- while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1359
- const line = _buffer.slice(0, newlineIdx).trim();
1360
- _buffer = _buffer.slice(newlineIdx + 1);
1361
- if (!line) continue;
1362
- try {
1363
- const response = JSON.parse(line);
1364
- const entry = _pending.get(response.id);
1365
- if (entry) {
1366
- clearTimeout(entry.timer);
1367
- _pending.delete(response.id);
1368
- entry.resolve(response);
1369
- }
1370
- } catch {
1371
- }
1487
+ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1488
+ if (!existsSync5(employeesPath)) {
1489
+ return [];
1490
+ }
1491
+ const raw = await readFile3(employeesPath, "utf-8");
1492
+ try {
1493
+ return JSON.parse(raw);
1494
+ } catch {
1495
+ return [];
1372
1496
  }
1373
1497
  }
1374
- function cleanupStaleFiles() {
1375
- if (existsSync5(PID_PATH)) {
1376
- try {
1377
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
1378
- if (pid > 0) {
1379
- try {
1380
- process.kill(pid, 0);
1381
- return;
1382
- } catch {
1383
- }
1384
- }
1385
- } catch {
1386
- }
1387
- try {
1388
- unlinkSync2(PID_PATH);
1389
- } catch {
1498
+ var EMPLOYEES_PATH;
1499
+ var init_employees = __esm({
1500
+ "src/lib/employees.ts"() {
1501
+ "use strict";
1502
+ init_config();
1503
+ EMPLOYEES_PATH = path5.join(EXE_AI_DIR, "exe-employees.json");
1504
+ }
1505
+ });
1506
+
1507
+ // src/lib/license.ts
1508
+ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync6, mkdirSync as mkdirSync2 } from "fs";
1509
+ import { randomUUID } from "crypto";
1510
+ import path6 from "path";
1511
+ import { jwtVerify, importSPKI } from "jose";
1512
+ function loadDeviceId() {
1513
+ const deviceJsonPath = path6.join(EXE_AI_DIR, "device.json");
1514
+ try {
1515
+ if (existsSync6(deviceJsonPath)) {
1516
+ const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
1517
+ if (data.deviceId) return data.deviceId;
1390
1518
  }
1391
- try {
1392
- unlinkSync2(SOCKET_PATH);
1393
- } catch {
1519
+ } catch {
1520
+ }
1521
+ try {
1522
+ if (existsSync6(DEVICE_ID_PATH)) {
1523
+ const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
1524
+ if (id2) return id2;
1394
1525
  }
1526
+ } catch {
1395
1527
  }
1528
+ const id = randomUUID();
1529
+ mkdirSync2(EXE_AI_DIR, { recursive: true });
1530
+ writeFileSync(DEVICE_ID_PATH, id, "utf8");
1531
+ return id;
1396
1532
  }
1397
- function findPackageRoot() {
1398
- let dir = path5.dirname(fileURLToPath(import.meta.url));
1399
- const { root } = path5.parse(dir);
1400
- while (dir !== root) {
1401
- if (existsSync5(path5.join(dir, "package.json"))) return dir;
1402
- dir = path5.dirname(dir);
1533
+ function loadLicense() {
1534
+ try {
1535
+ if (!existsSync6(LICENSE_PATH)) return null;
1536
+ return readFileSync3(LICENSE_PATH, "utf8").trim();
1537
+ } catch {
1538
+ return null;
1403
1539
  }
1404
- return null;
1405
1540
  }
1406
- function spawnDaemon() {
1407
- const pkgRoot = findPackageRoot();
1408
- if (!pkgRoot) {
1409
- process.stderr.write("[exed-client] WARN: cannot find package root\n");
1410
- return;
1411
- }
1412
- const daemonPath = path5.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1413
- if (!existsSync5(daemonPath)) {
1414
- process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1415
- `);
1416
- return;
1417
- }
1418
- const resolvedPath = daemonPath;
1419
- process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1420
- `);
1421
- const logPath = path5.join(path5.dirname(SOCKET_PATH), "exed.log");
1422
- let stderrFd = "ignore";
1541
+ async function verifyLicenseJwt(token) {
1423
1542
  try {
1424
- stderrFd = openSync(logPath, "a");
1543
+ const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
1544
+ const { payload } = await jwtVerify(token, key, {
1545
+ algorithms: [LICENSE_JWT_ALG]
1546
+ });
1547
+ const plan = payload.plan ?? "free";
1548
+ const email = payload.sub ?? "";
1549
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
1550
+ return {
1551
+ valid: true,
1552
+ plan,
1553
+ email,
1554
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
1555
+ deviceLimit: limits.devices,
1556
+ employeeLimit: limits.employees,
1557
+ memoryLimit: limits.memories
1558
+ };
1425
1559
  } catch {
1426
- }
1427
- const child = spawn(process.execPath, [resolvedPath], {
1428
- detached: true,
1429
- stdio: ["ignore", "ignore", stderrFd],
1430
- env: {
1431
- ...process.env,
1432
- EXE_DAEMON_SOCK: SOCKET_PATH,
1433
- EXE_DAEMON_PID: PID_PATH
1434
- }
1435
- });
1436
- child.unref();
1437
- if (typeof stderrFd === "number") {
1438
- try {
1439
- closeSync(stderrFd);
1440
- } catch {
1441
- }
1560
+ return null;
1442
1561
  }
1443
1562
  }
1444
- function acquireSpawnLock() {
1563
+ async function getCachedLicense() {
1445
1564
  try {
1446
- const fd = openSync(SPAWN_LOCK_PATH, "wx");
1447
- closeSync(fd);
1448
- return true;
1565
+ if (!existsSync6(CACHE_PATH)) return null;
1566
+ const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
1567
+ if (!raw.token || typeof raw.token !== "string") return null;
1568
+ return await verifyLicenseJwt(raw.token);
1449
1569
  } catch {
1450
- try {
1451
- const stat = statSync(SPAWN_LOCK_PATH);
1452
- if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
1453
- try {
1454
- unlinkSync2(SPAWN_LOCK_PATH);
1455
- } catch {
1456
- }
1457
- try {
1458
- const fd = openSync(SPAWN_LOCK_PATH, "wx");
1459
- closeSync(fd);
1460
- return true;
1461
- } catch {
1462
- }
1463
- }
1464
- } catch {
1465
- }
1466
- return false;
1570
+ return null;
1467
1571
  }
1468
1572
  }
1469
- function releaseSpawnLock() {
1573
+ function cacheResponse(token) {
1470
1574
  try {
1471
- unlinkSync2(SPAWN_LOCK_PATH);
1575
+ writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
1472
1576
  } catch {
1473
1577
  }
1474
1578
  }
1475
- function connectToSocket() {
1476
- return new Promise((resolve) => {
1477
- if (_socket && _connected) {
1478
- resolve(true);
1479
- return;
1480
- }
1579
+ async function validateLicense(apiKey, deviceId) {
1580
+ const did = deviceId ?? loadDeviceId();
1581
+ try {
1582
+ const res = await fetch(`${API_BASE}/auth/activate`, {
1583
+ method: "POST",
1584
+ headers: { "Content-Type": "application/json" },
1585
+ body: JSON.stringify({ apiKey, deviceId: did }),
1586
+ signal: AbortSignal.timeout(1e4)
1587
+ });
1588
+ if (res.ok) {
1589
+ const data = await res.json();
1590
+ if (data.error === "device_limit_exceeded") {
1591
+ const cached2 = await getCachedLicense();
1592
+ if (cached2) return cached2;
1593
+ return { ...FREE_LICENSE, valid: false, plan: "free" };
1594
+ }
1595
+ if (data.token) {
1596
+ cacheResponse(data.token);
1597
+ const verified = await verifyLicenseJwt(data.token);
1598
+ if (verified) return verified;
1599
+ }
1600
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
1601
+ return {
1602
+ valid: data.valid,
1603
+ plan: data.plan,
1604
+ email: data.email,
1605
+ expiresAt: data.expiresAt,
1606
+ deviceLimit: limits.devices,
1607
+ employeeLimit: limits.employees,
1608
+ memoryLimit: limits.memories
1609
+ };
1610
+ }
1611
+ const cached = await getCachedLicense();
1612
+ if (cached) return cached;
1613
+ return { ...FREE_LICENSE, valid: false, plan: "free" };
1614
+ } catch {
1615
+ const cached = await getCachedLicense();
1616
+ if (cached) return cached;
1617
+ return FREE_LICENSE;
1618
+ }
1619
+ }
1620
+ async function checkLicense() {
1621
+ const key = loadLicense();
1622
+ if (!key) return FREE_LICENSE;
1623
+ const cached = await getCachedLicense();
1624
+ if (cached) return cached;
1625
+ const deviceId = loadDeviceId();
1626
+ return validateLicense(key, deviceId);
1627
+ }
1628
+ function isFeatureAllowed(license, feature) {
1629
+ switch (feature) {
1630
+ case "cloud_sync":
1631
+ case "external_agents":
1632
+ case "wiki":
1633
+ return license.plan !== "free";
1634
+ case "unlimited_employees":
1635
+ return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
1636
+ }
1637
+ }
1638
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE;
1639
+ var init_license = __esm({
1640
+ "src/lib/license.ts"() {
1641
+ "use strict";
1642
+ init_config();
1643
+ LICENSE_PATH = path6.join(EXE_AI_DIR, "license.key");
1644
+ CACHE_PATH = path6.join(EXE_AI_DIR, "license-cache.json");
1645
+ DEVICE_ID_PATH = path6.join(EXE_AI_DIR, "device-id");
1646
+ API_BASE = "https://askexe.com/cloud";
1647
+ LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1648
+ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1649
+ 4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
1650
+ -----END PUBLIC KEY-----`;
1651
+ LICENSE_JWT_ALG = "ES256";
1652
+ PLAN_LIMITS = {
1653
+ free: { devices: 1, employees: 1, memories: 5e3 },
1654
+ pro: { devices: 2, employees: 5, memories: 1e5 },
1655
+ team: { devices: 10, employees: 20, memories: 1e6 },
1656
+ agency: { devices: 50, employees: 100, memories: 1e7 },
1657
+ enterprise: { devices: -1, employees: -1, memories: -1 }
1658
+ };
1659
+ FREE_LICENSE = {
1660
+ valid: true,
1661
+ plan: "free",
1662
+ email: "",
1663
+ expiresAt: null,
1664
+ deviceLimit: 1,
1665
+ employeeLimit: 1,
1666
+ memoryLimit: 5e3
1667
+ };
1668
+ }
1669
+ });
1670
+
1671
+ // src/lib/plan-limits.ts
1672
+ var plan_limits_exports = {};
1673
+ __export(plan_limits_exports, {
1674
+ PlanLimitError: () => PlanLimitError,
1675
+ assertEmployeeLimit: () => assertEmployeeLimit,
1676
+ assertEmployeeLimitSync: () => assertEmployeeLimitSync,
1677
+ assertFeature: () => assertFeature,
1678
+ assertMemoryLimit: () => assertMemoryLimit,
1679
+ countActiveMemories: () => countActiveMemories,
1680
+ getLicenseSync: () => getLicenseSync
1681
+ });
1682
+ import { readFileSync as readFileSync4, existsSync as existsSync7 } from "fs";
1683
+ import path7 from "path";
1684
+ function getLicenseSync() {
1685
+ try {
1686
+ if (!existsSync7(CACHE_PATH2)) return freeLicense();
1687
+ const raw = JSON.parse(readFileSync4(CACHE_PATH2, "utf8"));
1688
+ if (!raw.token || typeof raw.token !== "string") return freeLicense();
1689
+ const parts = raw.token.split(".");
1690
+ if (parts.length !== 3) return freeLicense();
1691
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
1692
+ const plan = payload.plan ?? "free";
1693
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
1694
+ return {
1695
+ valid: true,
1696
+ plan,
1697
+ email: payload.sub ?? "",
1698
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
1699
+ deviceLimit: limits.devices,
1700
+ employeeLimit: limits.employees,
1701
+ memoryLimit: limits.memories
1702
+ };
1703
+ } catch {
1704
+ return freeLicense();
1705
+ }
1706
+ }
1707
+ function freeLicense() {
1708
+ const limits = PLAN_LIMITS.free;
1709
+ return {
1710
+ valid: true,
1711
+ plan: "free",
1712
+ email: "",
1713
+ expiresAt: null,
1714
+ deviceLimit: limits.devices,
1715
+ employeeLimit: limits.employees,
1716
+ memoryLimit: limits.memories
1717
+ };
1718
+ }
1719
+ async function countActiveMemories() {
1720
+ if (!isInitialized()) return 0;
1721
+ const client = getClient();
1722
+ const result = await client.execute(
1723
+ "SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL"
1724
+ );
1725
+ const row = result.rows[0];
1726
+ return Number(row?.cnt ?? 0);
1727
+ }
1728
+ async function assertMemoryLimit() {
1729
+ const license = await checkLicense();
1730
+ if (license.memoryLimit < 0) return;
1731
+ const count = await countActiveMemories();
1732
+ if (count >= license.memoryLimit) {
1733
+ throw new PlanLimitError(
1734
+ `Memory limit reached: ${count}/${license.memoryLimit} active memories on the ${license.plan} plan. Upgrade at https://askexe.com to store more.`
1735
+ );
1736
+ }
1737
+ }
1738
+ async function assertEmployeeLimit(license, rosterPath) {
1739
+ const lic = license ?? await checkLicense();
1740
+ if (lic.employeeLimit < 0) return;
1741
+ const employees = await loadEmployees(rosterPath ?? EMPLOYEES_PATH);
1742
+ if (employees.length >= lic.employeeLimit) {
1743
+ throw new PlanLimitError(
1744
+ `Employee limit reached: ${employees.length}/${lic.employeeLimit} employees on the ${lic.plan} plan. Upgrade at https://askexe.com to add more.`
1745
+ );
1746
+ }
1747
+ }
1748
+ function assertEmployeeLimitSync(rosterPath) {
1749
+ const license = getLicenseSync();
1750
+ if (license.employeeLimit < 0) return;
1751
+ const filePath = rosterPath ?? EMPLOYEES_PATH;
1752
+ let count = 0;
1753
+ try {
1754
+ if (existsSync7(filePath)) {
1755
+ const raw = readFileSync4(filePath, "utf8");
1756
+ const employees = JSON.parse(raw);
1757
+ count = Array.isArray(employees) ? employees.length : 0;
1758
+ }
1759
+ } catch {
1760
+ throw new PlanLimitError(
1761
+ `Cannot verify employee count: roster unreadable at ${filePath}. Refusing to proceed. Check file permissions or upgrade plan.`
1762
+ );
1763
+ }
1764
+ if (count >= license.employeeLimit) {
1765
+ throw new PlanLimitError(
1766
+ `Employee limit reached: ${count}/${license.employeeLimit} employees on the ${license.plan} plan. Upgrade at https://askexe.com to add more.`
1767
+ );
1768
+ }
1769
+ }
1770
+ async function assertFeature(feature) {
1771
+ const license = await checkLicense();
1772
+ if (!isFeatureAllowed(license, feature)) {
1773
+ throw new PlanLimitError(
1774
+ `Feature "${feature}" requires a paid plan. Current plan: ${license.plan}. Upgrade at https://askexe.com.`
1775
+ );
1776
+ }
1777
+ }
1778
+ var PlanLimitError, CACHE_PATH2;
1779
+ var init_plan_limits = __esm({
1780
+ "src/lib/plan-limits.ts"() {
1781
+ "use strict";
1782
+ init_database();
1783
+ init_employees();
1784
+ init_license();
1785
+ init_config();
1786
+ PlanLimitError = class extends Error {
1787
+ constructor(message) {
1788
+ super(message);
1789
+ this.name = "PlanLimitError";
1790
+ }
1791
+ };
1792
+ CACHE_PATH2 = path7.join(EXE_AI_DIR, "license-cache.json");
1793
+ }
1794
+ });
1795
+
1796
+ // src/lib/exe-daemon-client.ts
1797
+ import net from "net";
1798
+ import { spawn } from "child_process";
1799
+ import { randomUUID as randomUUID2 } from "crypto";
1800
+ import { existsSync as existsSync8, unlinkSync as unlinkSync2, readFileSync as readFileSync5, openSync, closeSync, statSync } from "fs";
1801
+ import path8 from "path";
1802
+ import { fileURLToPath } from "url";
1803
+ function handleData(chunk) {
1804
+ _buffer += chunk.toString();
1805
+ let newlineIdx;
1806
+ while ((newlineIdx = _buffer.indexOf("\n")) !== -1) {
1807
+ const line = _buffer.slice(0, newlineIdx).trim();
1808
+ _buffer = _buffer.slice(newlineIdx + 1);
1809
+ if (!line) continue;
1810
+ try {
1811
+ const response = JSON.parse(line);
1812
+ const entry = _pending.get(response.id);
1813
+ if (entry) {
1814
+ clearTimeout(entry.timer);
1815
+ _pending.delete(response.id);
1816
+ entry.resolve(response);
1817
+ }
1818
+ } catch {
1819
+ }
1820
+ }
1821
+ }
1822
+ function cleanupStaleFiles() {
1823
+ if (existsSync8(PID_PATH)) {
1824
+ try {
1825
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1826
+ if (pid > 0) {
1827
+ try {
1828
+ process.kill(pid, 0);
1829
+ return;
1830
+ } catch {
1831
+ }
1832
+ }
1833
+ } catch {
1834
+ }
1835
+ try {
1836
+ unlinkSync2(PID_PATH);
1837
+ } catch {
1838
+ }
1839
+ try {
1840
+ unlinkSync2(SOCKET_PATH);
1841
+ } catch {
1842
+ }
1843
+ }
1844
+ }
1845
+ function findPackageRoot() {
1846
+ let dir = path8.dirname(fileURLToPath(import.meta.url));
1847
+ const { root } = path8.parse(dir);
1848
+ while (dir !== root) {
1849
+ if (existsSync8(path8.join(dir, "package.json"))) return dir;
1850
+ dir = path8.dirname(dir);
1851
+ }
1852
+ return null;
1853
+ }
1854
+ function spawnDaemon() {
1855
+ const pkgRoot = findPackageRoot();
1856
+ if (!pkgRoot) {
1857
+ process.stderr.write("[exed-client] WARN: cannot find package root\n");
1858
+ return;
1859
+ }
1860
+ const daemonPath = path8.join(pkgRoot, "dist", "lib", "exe-daemon.js");
1861
+ if (!existsSync8(daemonPath)) {
1862
+ process.stderr.write(`[exed-client] WARN: daemon script not found at ${daemonPath}
1863
+ `);
1864
+ return;
1865
+ }
1866
+ const resolvedPath = daemonPath;
1867
+ process.stderr.write(`[exed-client] Spawning daemon: ${resolvedPath}
1868
+ `);
1869
+ const logPath = path8.join(path8.dirname(SOCKET_PATH), "exed.log");
1870
+ let stderrFd = "ignore";
1871
+ try {
1872
+ stderrFd = openSync(logPath, "a");
1873
+ } catch {
1874
+ }
1875
+ const child = spawn(process.execPath, [resolvedPath], {
1876
+ detached: true,
1877
+ stdio: ["ignore", "ignore", stderrFd],
1878
+ env: {
1879
+ ...process.env,
1880
+ EXE_DAEMON_SOCK: SOCKET_PATH,
1881
+ EXE_DAEMON_PID: PID_PATH
1882
+ }
1883
+ });
1884
+ child.unref();
1885
+ if (typeof stderrFd === "number") {
1886
+ try {
1887
+ closeSync(stderrFd);
1888
+ } catch {
1889
+ }
1890
+ }
1891
+ }
1892
+ function acquireSpawnLock() {
1893
+ try {
1894
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1895
+ closeSync(fd);
1896
+ return true;
1897
+ } catch {
1898
+ try {
1899
+ const stat = statSync(SPAWN_LOCK_PATH);
1900
+ if (Date.now() - stat.mtimeMs > SPAWN_LOCK_STALE_MS) {
1901
+ try {
1902
+ unlinkSync2(SPAWN_LOCK_PATH);
1903
+ } catch {
1904
+ }
1905
+ try {
1906
+ const fd = openSync(SPAWN_LOCK_PATH, "wx");
1907
+ closeSync(fd);
1908
+ return true;
1909
+ } catch {
1910
+ }
1911
+ }
1912
+ } catch {
1913
+ }
1914
+ return false;
1915
+ }
1916
+ }
1917
+ function releaseSpawnLock() {
1918
+ try {
1919
+ unlinkSync2(SPAWN_LOCK_PATH);
1920
+ } catch {
1921
+ }
1922
+ }
1923
+ function connectToSocket() {
1924
+ return new Promise((resolve) => {
1925
+ if (_socket && _connected) {
1926
+ resolve(true);
1927
+ return;
1928
+ }
1481
1929
  const socket = net.createConnection({ path: SOCKET_PATH });
1482
1930
  const connectTimeout = setTimeout(() => {
1483
1931
  socket.destroy();
@@ -1536,7 +1984,7 @@ function sendRequest(texts, priority) {
1536
1984
  resolve({ error: "Not connected" });
1537
1985
  return;
1538
1986
  }
1539
- const id = randomUUID();
1987
+ const id = randomUUID2();
1540
1988
  const timer = setTimeout(() => {
1541
1989
  _pending.delete(id);
1542
1990
  resolve({ error: "Request timeout" });
@@ -1554,7 +2002,7 @@ function sendRequest(texts, priority) {
1554
2002
  async function pingDaemon() {
1555
2003
  if (!_socket || !_connected) return null;
1556
2004
  return new Promise((resolve) => {
1557
- const id = randomUUID();
2005
+ const id = randomUUID2();
1558
2006
  const timer = setTimeout(() => {
1559
2007
  _pending.delete(id);
1560
2008
  resolve(null);
@@ -1580,9 +2028,9 @@ async function pingDaemon() {
1580
2028
  }
1581
2029
  function killAndRespawnDaemon() {
1582
2030
  process.stderr.write("[exed-client] Killing daemon for restart...\n");
1583
- if (existsSync5(PID_PATH)) {
2031
+ if (existsSync8(PID_PATH)) {
1584
2032
  try {
1585
- const pid = parseInt(readFileSync3(PID_PATH, "utf8").trim(), 10);
2033
+ const pid = parseInt(readFileSync5(PID_PATH, "utf8").trim(), 10);
1586
2034
  if (pid > 0) {
1587
2035
  try {
1588
2036
  process.kill(pid, "SIGKILL");
@@ -1666,9 +2114,9 @@ var init_exe_daemon_client = __esm({
1666
2114
  "src/lib/exe-daemon-client.ts"() {
1667
2115
  "use strict";
1668
2116
  init_config();
1669
- SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path5.join(EXE_AI_DIR, "exed.sock");
1670
- PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path5.join(EXE_AI_DIR, "exed.pid");
1671
- SPAWN_LOCK_PATH = path5.join(EXE_AI_DIR, "exed-spawn.lock");
2117
+ SOCKET_PATH = process.env.EXE_DAEMON_SOCK ?? process.env.EXE_EMBED_SOCK ?? path8.join(EXE_AI_DIR, "exed.sock");
2118
+ PID_PATH = process.env.EXE_DAEMON_PID ?? process.env.EXE_EMBED_PID ?? path8.join(EXE_AI_DIR, "exed.pid");
2119
+ SPAWN_LOCK_PATH = path8.join(EXE_AI_DIR, "exed-spawn.lock");
1672
2120
  SPAWN_LOCK_STALE_MS = 3e4;
1673
2121
  CONNECT_TIMEOUT_MS = 15e3;
1674
2122
  REQUEST_TIMEOUT_MS = 3e4;
@@ -1828,213 +2276,6 @@ var init_compress = __esm({
1828
2276
  }
1829
2277
  });
1830
2278
 
1831
- // src/lib/employees.ts
1832
- import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1833
- import { existsSync as existsSync6, symlinkSync, readlinkSync } from "fs";
1834
- import { execSync } from "child_process";
1835
- import path6 from "path";
1836
- var EMPLOYEES_PATH;
1837
- var init_employees = __esm({
1838
- "src/lib/employees.ts"() {
1839
- "use strict";
1840
- init_config();
1841
- EMPLOYEES_PATH = path6.join(EXE_AI_DIR, "exe-employees.json");
1842
- }
1843
- });
1844
-
1845
- // src/lib/license.ts
1846
- import { readFileSync as readFileSync4, writeFileSync, existsSync as existsSync7, mkdirSync as mkdirSync2 } from "fs";
1847
- import { randomUUID as randomUUID2 } from "crypto";
1848
- import path7 from "path";
1849
- import { jwtVerify, importSPKI } from "jose";
1850
- function loadDeviceId() {
1851
- const deviceJsonPath = path7.join(EXE_AI_DIR, "device.json");
1852
- try {
1853
- if (existsSync7(deviceJsonPath)) {
1854
- const data = JSON.parse(readFileSync4(deviceJsonPath, "utf8"));
1855
- if (data.deviceId) return data.deviceId;
1856
- }
1857
- } catch {
1858
- }
1859
- try {
1860
- if (existsSync7(DEVICE_ID_PATH)) {
1861
- const id2 = readFileSync4(DEVICE_ID_PATH, "utf8").trim();
1862
- if (id2) return id2;
1863
- }
1864
- } catch {
1865
- }
1866
- const id = randomUUID2();
1867
- mkdirSync2(EXE_AI_DIR, { recursive: true });
1868
- writeFileSync(DEVICE_ID_PATH, id, "utf8");
1869
- return id;
1870
- }
1871
- function loadLicense() {
1872
- try {
1873
- if (!existsSync7(LICENSE_PATH)) return null;
1874
- return readFileSync4(LICENSE_PATH, "utf8").trim();
1875
- } catch {
1876
- return null;
1877
- }
1878
- }
1879
- async function verifyLicenseJwt(token) {
1880
- try {
1881
- const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
1882
- const { payload } = await jwtVerify(token, key, {
1883
- algorithms: [LICENSE_JWT_ALG]
1884
- });
1885
- const plan = payload.plan ?? "free";
1886
- const email = payload.sub ?? "";
1887
- const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
1888
- return {
1889
- valid: true,
1890
- plan,
1891
- email,
1892
- expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
1893
- deviceLimit: limits.devices,
1894
- employeeLimit: limits.employees,
1895
- memoryLimit: limits.memories
1896
- };
1897
- } catch {
1898
- return null;
1899
- }
1900
- }
1901
- async function getCachedLicense() {
1902
- try {
1903
- if (!existsSync7(CACHE_PATH)) return null;
1904
- const raw = JSON.parse(readFileSync4(CACHE_PATH, "utf8"));
1905
- if (!raw.token || typeof raw.token !== "string") return null;
1906
- return await verifyLicenseJwt(raw.token);
1907
- } catch {
1908
- return null;
1909
- }
1910
- }
1911
- function cacheResponse(token) {
1912
- try {
1913
- writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
1914
- } catch {
1915
- }
1916
- }
1917
- async function validateLicense(apiKey, deviceId) {
1918
- const did = deviceId ?? loadDeviceId();
1919
- try {
1920
- const res = await fetch(`${API_BASE}/auth/activate`, {
1921
- method: "POST",
1922
- headers: { "Content-Type": "application/json" },
1923
- body: JSON.stringify({ apiKey, deviceId: did }),
1924
- signal: AbortSignal.timeout(1e4)
1925
- });
1926
- if (res.ok) {
1927
- const data = await res.json();
1928
- if (data.error === "device_limit_exceeded") {
1929
- const cached2 = await getCachedLicense();
1930
- if (cached2) return cached2;
1931
- return { ...FREE_LICENSE, valid: false, plan: "free" };
1932
- }
1933
- if (data.token) {
1934
- cacheResponse(data.token);
1935
- const verified = await verifyLicenseJwt(data.token);
1936
- if (verified) return verified;
1937
- }
1938
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
1939
- return {
1940
- valid: data.valid,
1941
- plan: data.plan,
1942
- email: data.email,
1943
- expiresAt: data.expiresAt,
1944
- deviceLimit: limits.devices,
1945
- employeeLimit: limits.employees,
1946
- memoryLimit: limits.memories
1947
- };
1948
- }
1949
- const cached = await getCachedLicense();
1950
- if (cached) return cached;
1951
- return { ...FREE_LICENSE, valid: false, plan: "free" };
1952
- } catch {
1953
- const cached = await getCachedLicense();
1954
- if (cached) return cached;
1955
- return FREE_LICENSE;
1956
- }
1957
- }
1958
- async function checkLicense() {
1959
- const key = loadLicense();
1960
- if (!key) return FREE_LICENSE;
1961
- const cached = await getCachedLicense();
1962
- if (cached) return cached;
1963
- const deviceId = loadDeviceId();
1964
- return validateLicense(key, deviceId);
1965
- }
1966
- function isFeatureAllowed(license, feature) {
1967
- switch (feature) {
1968
- case "cloud_sync":
1969
- case "external_agents":
1970
- case "wiki":
1971
- return license.plan !== "free";
1972
- case "unlimited_employees":
1973
- return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
1974
- }
1975
- }
1976
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE;
1977
- var init_license = __esm({
1978
- "src/lib/license.ts"() {
1979
- "use strict";
1980
- init_config();
1981
- LICENSE_PATH = path7.join(EXE_AI_DIR, "license.key");
1982
- CACHE_PATH = path7.join(EXE_AI_DIR, "license-cache.json");
1983
- DEVICE_ID_PATH = path7.join(EXE_AI_DIR, "device-id");
1984
- API_BASE = "https://askexe.com/cloud";
1985
- LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1986
- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1987
- 4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
1988
- -----END PUBLIC KEY-----`;
1989
- LICENSE_JWT_ALG = "ES256";
1990
- PLAN_LIMITS = {
1991
- free: { devices: 1, employees: 1, memories: 5e3 },
1992
- pro: { devices: 2, employees: 5, memories: 1e5 },
1993
- team: { devices: 10, employees: 20, memories: 1e6 },
1994
- agency: { devices: 50, employees: 100, memories: 1e7 },
1995
- enterprise: { devices: -1, employees: -1, memories: -1 }
1996
- };
1997
- FREE_LICENSE = {
1998
- valid: true,
1999
- plan: "free",
2000
- email: "",
2001
- expiresAt: null,
2002
- deviceLimit: 1,
2003
- employeeLimit: 1,
2004
- memoryLimit: 5e3
2005
- };
2006
- }
2007
- });
2008
-
2009
- // src/lib/plan-limits.ts
2010
- import { readFileSync as readFileSync5, existsSync as existsSync8 } from "fs";
2011
- import path8 from "path";
2012
- async function assertFeature(feature) {
2013
- const license = await checkLicense();
2014
- if (!isFeatureAllowed(license, feature)) {
2015
- throw new PlanLimitError(
2016
- `Feature "${feature}" requires a paid plan. Current plan: ${license.plan}. Upgrade at https://askexe.com.`
2017
- );
2018
- }
2019
- }
2020
- var PlanLimitError, CACHE_PATH2;
2021
- var init_plan_limits = __esm({
2022
- "src/lib/plan-limits.ts"() {
2023
- "use strict";
2024
- init_database();
2025
- init_employees();
2026
- init_license();
2027
- init_config();
2028
- PlanLimitError = class extends Error {
2029
- constructor(message) {
2030
- super(message);
2031
- this.name = "PlanLimitError";
2032
- }
2033
- };
2034
- CACHE_PATH2 = path8.join(EXE_AI_DIR, "license-cache.json");
2035
- }
2036
- });
2037
-
2038
2279
  // src/lib/cloud-sync.ts
2039
2280
  var cloud_sync_exports = {};
2040
2281
  __export(cloud_sync_exports, {
@@ -2260,6 +2501,11 @@ async function initStore(options) {
2260
2501
  const vResult = await client.execute("SELECT MAX(version) as max_v FROM memories");
2261
2502
  _nextVersion = (Number(vResult.rows[0]?.max_v) || 0) + 1;
2262
2503
  }
2504
+ function classifyTier(record) {
2505
+ if (record.tool_name === "commit_to_long_term_memory" && (record.importance ?? 0) >= 8) return 1;
2506
+ if (["store_memory", "manual"].includes(record.tool_name ?? "") && (record.importance ?? 0) >= 5) return 2;
2507
+ return 3;
2508
+ }
2263
2509
  async function writeMemory(record) {
2264
2510
  if (record.vector !== null && record.vector.length !== EMBEDDING_DIM) {
2265
2511
  throw new Error(
@@ -2287,7 +2533,11 @@ async function writeMemory(record) {
2287
2533
  document_id: record.document_id ?? null,
2288
2534
  user_id: record.user_id ?? null,
2289
2535
  char_offset: record.char_offset ?? null,
2290
- page_number: record.page_number ?? null
2536
+ page_number: record.page_number ?? null,
2537
+ source_path: record.source_path ?? null,
2538
+ source_type: record.source_type ?? null,
2539
+ tier: record.tier ?? classifyTier(record),
2540
+ supersedes_id: record.supersedes_id ?? null
2291
2541
  };
2292
2542
  _pendingRecords.push(dbRow);
2293
2543
  if (_flushTimer === null) {
@@ -2319,20 +2569,26 @@ async function flushBatch() {
2319
2569
  const userId = row.user_id ?? null;
2320
2570
  const charOffset = row.char_offset ?? null;
2321
2571
  const pageNumber = row.page_number ?? null;
2572
+ const sourcePath = row.source_path ?? null;
2573
+ const sourceType = row.source_type ?? null;
2574
+ const tier = row.tier ?? 3;
2575
+ const supersedesId = row.supersedes_id ?? null;
2322
2576
  return {
2323
2577
  sql: hasVector ? `INSERT OR IGNORE INTO memories
2324
2578
  (id, agent_id, agent_role, session_id, timestamp,
2325
2579
  tool_name, project_name,
2326
2580
  has_error, raw_text, vector, version, task_id, importance, status,
2327
2581
  confidence, last_accessed,
2328
- workspace_id, document_id, user_id, char_offset, page_number)
2329
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
2582
+ workspace_id, document_id, user_id, char_offset, page_number,
2583
+ source_path, source_type, tier, supersedes_id)
2584
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)` : `INSERT OR IGNORE INTO memories
2330
2585
  (id, agent_id, agent_role, session_id, timestamp,
2331
2586
  tool_name, project_name,
2332
2587
  has_error, raw_text, vector, version, task_id, importance, status,
2333
2588
  confidence, last_accessed,
2334
- workspace_id, document_id, user_id, char_offset, page_number)
2335
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2589
+ workspace_id, document_id, user_id, char_offset, page_number,
2590
+ source_path, source_type, tier, supersedes_id)
2591
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2336
2592
  args: hasVector ? [
2337
2593
  row.id,
2338
2594
  row.agent_id,
@@ -2354,7 +2610,11 @@ async function flushBatch() {
2354
2610
  documentId,
2355
2611
  userId,
2356
2612
  charOffset,
2357
- pageNumber
2613
+ pageNumber,
2614
+ sourcePath,
2615
+ sourceType,
2616
+ tier,
2617
+ supersedesId
2358
2618
  ] : [
2359
2619
  row.id,
2360
2620
  row.agent_id,
@@ -2375,7 +2635,11 @@ async function flushBatch() {
2375
2635
  documentId,
2376
2636
  userId,
2377
2637
  charOffset,
2378
- pageNumber
2638
+ pageNumber,
2639
+ sourcePath,
2640
+ sourceType,
2641
+ tier,
2642
+ supersedesId
2379
2643
  ]
2380
2644
  };
2381
2645
  };
@@ -2523,6 +2787,12 @@ async function main() {
2523
2787
  primaryProject = p;
2524
2788
  }
2525
2789
  }
2790
+ try {
2791
+ const { assertMemoryLimit: assertMemoryLimit2 } = await Promise.resolve().then(() => (init_plan_limits(), plan_limits_exports));
2792
+ await assertMemoryLimit2();
2793
+ } catch {
2794
+ process.exit(0);
2795
+ }
2526
2796
  await writeMemory({
2527
2797
  id: crypto4.randomUUID(),
2528
2798
  agent_id: agentId,