@askexenow/exe-os 0.8.45 → 0.8.46

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.
@@ -2806,14 +2806,6 @@ function assertEmployeeLimitSync(rosterPath) {
2806
2806
  );
2807
2807
  }
2808
2808
  }
2809
- async function assertFeature(feature) {
2810
- const license = await checkLicense();
2811
- if (!isFeatureAllowed(license, feature)) {
2812
- throw new PlanLimitError(
2813
- `Feature "${feature}" requires a paid plan. Current plan: ${license.plan}. Upgrade at https://askexe.com.`
2814
- );
2815
- }
2816
- }
2817
2809
  var PlanLimitError, CACHE_PATH2;
2818
2810
  var init_plan_limits = __esm({
2819
2811
  "src/lib/plan-limits.ts"() {
@@ -5594,7 +5586,6 @@ async function cloudPull(sinceVersion, config) {
5594
5586
  }
5595
5587
  }
5596
5588
  async function cloudSync(config) {
5597
- await assertFeature("cloud_sync");
5598
5589
  let client;
5599
5590
  try {
5600
5591
  client = getClient();
@@ -6330,7 +6321,6 @@ var init_cloud_sync = __esm({
6330
6321
  init_database();
6331
6322
  init_crypto();
6332
6323
  init_compress();
6333
- init_plan_limits();
6334
6324
  init_license();
6335
6325
  init_config();
6336
6326
  init_employees();
@@ -246,9 +246,9 @@ __export(license_exports, {
246
246
  stopLicenseRevalidation: () => stopLicenseRevalidation,
247
247
  validateLicense: () => validateLicense
248
248
  });
249
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync } from "fs";
249
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
250
250
  import { randomUUID } from "crypto";
251
- import path4 from "path";
251
+ import path3 from "path";
252
252
  import { jwtVerify, importSPKI } from "jose";
253
253
  async function fetchRetry(url, init) {
254
254
  try {
@@ -259,17 +259,17 @@ async function fetchRetry(url, init) {
259
259
  }
260
260
  }
261
261
  function loadDeviceId() {
262
- const deviceJsonPath = path4.join(EXE_AI_DIR, "device.json");
262
+ const deviceJsonPath = path3.join(EXE_AI_DIR, "device.json");
263
263
  try {
264
- if (existsSync4(deviceJsonPath)) {
265
- const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
264
+ if (existsSync3(deviceJsonPath)) {
265
+ const data = JSON.parse(readFileSync2(deviceJsonPath, "utf8"));
266
266
  if (data.deviceId) return data.deviceId;
267
267
  }
268
268
  } catch {
269
269
  }
270
270
  try {
271
- if (existsSync4(DEVICE_ID_PATH)) {
272
- const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
271
+ if (existsSync3(DEVICE_ID_PATH)) {
272
+ const id2 = readFileSync2(DEVICE_ID_PATH, "utf8").trim();
273
273
  if (id2) return id2;
274
274
  }
275
275
  } catch {
@@ -281,8 +281,8 @@ function loadDeviceId() {
281
281
  }
282
282
  function loadLicense() {
283
283
  try {
284
- if (!existsSync4(LICENSE_PATH)) return null;
285
- return readFileSync3(LICENSE_PATH, "utf8").trim();
284
+ if (!existsSync3(LICENSE_PATH)) return null;
285
+ return readFileSync2(LICENSE_PATH, "utf8").trim();
286
286
  } catch {
287
287
  return null;
288
288
  }
@@ -315,8 +315,8 @@ async function verifyLicenseJwt(token) {
315
315
  }
316
316
  async function getCachedLicense() {
317
317
  try {
318
- if (!existsSync4(CACHE_PATH)) return null;
319
- const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
318
+ if (!existsSync3(CACHE_PATH)) return null;
319
+ const raw = JSON.parse(readFileSync2(CACHE_PATH, "utf8"));
320
320
  if (!raw.token || typeof raw.token !== "string") return null;
321
321
  return await verifyLicenseJwt(raw.token);
322
322
  } catch {
@@ -325,8 +325,8 @@ async function getCachedLicense() {
325
325
  }
326
326
  function readCachedToken() {
327
327
  try {
328
- if (!existsSync4(CACHE_PATH)) return null;
329
- const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
328
+ if (!existsSync3(CACHE_PATH)) return null;
329
+ const raw = JSON.parse(readFileSync2(CACHE_PATH, "utf8"));
330
330
  return typeof raw.token === "string" ? raw.token : null;
331
331
  } catch {
332
332
  return null;
@@ -392,9 +392,9 @@ async function checkLicense() {
392
392
  let key = loadLicense();
393
393
  if (!key) {
394
394
  try {
395
- const configPath = path4.join(EXE_AI_DIR, "config.json");
396
- if (existsSync4(configPath)) {
397
- const raw = JSON.parse(readFileSync3(configPath, "utf8"));
395
+ const configPath = path3.join(EXE_AI_DIR, "config.json");
396
+ if (existsSync3(configPath)) {
397
+ const raw = JSON.parse(readFileSync2(configPath, "utf8"));
398
398
  const cloud = raw.cloud;
399
399
  if (cloud?.apiKey) {
400
400
  key = cloud.apiKey;
@@ -553,9 +553,9 @@ var init_license = __esm({
553
553
  "src/lib/license.ts"() {
554
554
  "use strict";
555
555
  init_config();
556
- LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
557
- CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
558
- DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
556
+ LICENSE_PATH = path3.join(EXE_AI_DIR, "license.key");
557
+ CACHE_PATH = path3.join(EXE_AI_DIR, "license-cache.json");
558
+ DEVICE_ID_PATH = path3.join(EXE_AI_DIR, "device-id");
559
559
  API_BASE = "https://askexe.com/cloud";
560
560
  RETRY_DELAY_MS = 500;
561
561
  LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
@@ -741,9 +741,9 @@ function isMainModule(importMetaUrl) {
741
741
 
742
742
  // src/lib/cloud-sync.ts
743
743
  init_database();
744
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
744
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
745
745
  import crypto4 from "crypto";
746
- import path6 from "path";
746
+ import path5 from "path";
747
747
  import { homedir } from "os";
748
748
 
749
749
  // src/lib/crypto.ts
@@ -752,29 +752,21 @@ import crypto3 from "crypto";
752
752
  // src/lib/compress.ts
753
753
  import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
754
754
 
755
- // src/lib/plan-limits.ts
756
- init_database();
757
- import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
758
- import path5 from "path";
755
+ // src/lib/cloud-sync.ts
756
+ init_license();
757
+ init_config();
759
758
 
760
759
  // src/lib/employees.ts
761
760
  init_config();
762
761
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
763
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
762
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
764
763
  import { execSync } from "child_process";
765
- import path3 from "path";
766
- var EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
767
-
768
- // src/lib/plan-limits.ts
769
- init_license();
770
- init_config();
771
- var CACHE_PATH2 = path5.join(EXE_AI_DIR, "license-cache.json");
764
+ import path4 from "path";
765
+ var EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
772
766
 
773
767
  // src/lib/cloud-sync.ts
774
- init_license();
775
- init_config();
776
768
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
777
- var ROSTER_LOCK_PATH = path6.join(EXE_AI_DIR, "roster-merge.lock");
769
+ var ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
778
770
  function assertSecureEndpoint(endpoint) {
779
771
  if (endpoint.startsWith("https://")) return;
780
772
  if (endpoint.startsWith("http://")) {
@@ -789,7 +781,7 @@ function assertSecureEndpoint(endpoint) {
789
781
  );
790
782
  }
791
783
  }
792
- var ROSTER_DELETIONS_PATH = path6.join(EXE_AI_DIR, "roster-deletions.json");
784
+ var ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
793
785
 
794
786
  // src/bin/exe-cloud.ts
795
787
  var BAR = "\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550";
@@ -1271,13 +1271,50 @@ var init_compress = __esm({
1271
1271
  }
1272
1272
  });
1273
1273
 
1274
+ // src/lib/license.ts
1275
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
1276
+ import { randomUUID } from "crypto";
1277
+ import path3 from "path";
1278
+ import { jwtVerify, importSPKI } from "jose";
1279
+ function loadDeviceId() {
1280
+ const deviceJsonPath = path3.join(EXE_AI_DIR, "device.json");
1281
+ try {
1282
+ if (existsSync3(deviceJsonPath)) {
1283
+ const data = JSON.parse(readFileSync2(deviceJsonPath, "utf8"));
1284
+ if (data.deviceId) return data.deviceId;
1285
+ }
1286
+ } catch {
1287
+ }
1288
+ try {
1289
+ if (existsSync3(DEVICE_ID_PATH)) {
1290
+ const id2 = readFileSync2(DEVICE_ID_PATH, "utf8").trim();
1291
+ if (id2) return id2;
1292
+ }
1293
+ } catch {
1294
+ }
1295
+ const id = randomUUID();
1296
+ mkdirSync(EXE_AI_DIR, { recursive: true });
1297
+ writeFileSync(DEVICE_ID_PATH, id, "utf8");
1298
+ return id;
1299
+ }
1300
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH;
1301
+ var init_license = __esm({
1302
+ "src/lib/license.ts"() {
1303
+ "use strict";
1304
+ init_config();
1305
+ LICENSE_PATH = path3.join(EXE_AI_DIR, "license.key");
1306
+ CACHE_PATH = path3.join(EXE_AI_DIR, "license-cache.json");
1307
+ DEVICE_ID_PATH = path3.join(EXE_AI_DIR, "device-id");
1308
+ }
1309
+ });
1310
+
1274
1311
  // src/lib/employees.ts
1275
1312
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
1276
- import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
1313
+ import { existsSync as existsSync4, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
1277
1314
  import { execSync } from "child_process";
1278
- import path3 from "path";
1315
+ import path4 from "path";
1279
1316
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1280
- if (!existsSync3(employeesPath)) {
1317
+ if (!existsSync4(employeesPath)) {
1281
1318
  return [];
1282
1319
  }
1283
1320
  const raw = await readFile3(employeesPath, "utf-8");
@@ -1288,7 +1325,7 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1288
1325
  }
1289
1326
  }
1290
1327
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
1291
- await mkdir3(path3.dirname(employeesPath), { recursive: true });
1328
+ await mkdir3(path4.dirname(employeesPath), { recursive: true });
1292
1329
  await writeFile3(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1293
1330
  }
1294
1331
  function findExeBin() {
@@ -1307,7 +1344,7 @@ function registerBinSymlinks(name) {
1307
1344
  errors.push("Could not find 'exe-os' in PATH");
1308
1345
  return { created, skipped, errors };
1309
1346
  }
1310
- const binDir = path3.dirname(exeBinPath);
1347
+ const binDir = path4.dirname(exeBinPath);
1311
1348
  let target;
1312
1349
  try {
1313
1350
  target = readlinkSync(exeBinPath);
@@ -1317,8 +1354,8 @@ function registerBinSymlinks(name) {
1317
1354
  }
1318
1355
  for (const suffix of ["", "-opencode"]) {
1319
1356
  const linkName = `${name}${suffix}`;
1320
- const linkPath = path3.join(binDir, linkName);
1321
- if (existsSync3(linkPath)) {
1357
+ const linkPath = path4.join(binDir, linkName);
1358
+ if (existsSync4(linkPath)) {
1322
1359
  skipped.push(linkName);
1323
1360
  continue;
1324
1361
  }
@@ -1336,237 +1373,7 @@ var init_employees = __esm({
1336
1373
  "src/lib/employees.ts"() {
1337
1374
  "use strict";
1338
1375
  init_config();
1339
- EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
1340
- }
1341
- });
1342
-
1343
- // src/lib/license.ts
1344
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync4, mkdirSync } from "fs";
1345
- import { randomUUID } from "crypto";
1346
- import path4 from "path";
1347
- import { jwtVerify, importSPKI } from "jose";
1348
- async function fetchRetry(url, init) {
1349
- try {
1350
- return await fetch(url, init);
1351
- } catch {
1352
- await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
1353
- return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
1354
- }
1355
- }
1356
- function loadDeviceId() {
1357
- const deviceJsonPath = path4.join(EXE_AI_DIR, "device.json");
1358
- try {
1359
- if (existsSync4(deviceJsonPath)) {
1360
- const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
1361
- if (data.deviceId) return data.deviceId;
1362
- }
1363
- } catch {
1364
- }
1365
- try {
1366
- if (existsSync4(DEVICE_ID_PATH)) {
1367
- const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
1368
- if (id2) return id2;
1369
- }
1370
- } catch {
1371
- }
1372
- const id = randomUUID();
1373
- mkdirSync(EXE_AI_DIR, { recursive: true });
1374
- writeFileSync(DEVICE_ID_PATH, id, "utf8");
1375
- return id;
1376
- }
1377
- function loadLicense() {
1378
- try {
1379
- if (!existsSync4(LICENSE_PATH)) return null;
1380
- return readFileSync3(LICENSE_PATH, "utf8").trim();
1381
- } catch {
1382
- return null;
1383
- }
1384
- }
1385
- function saveLicense(apiKey) {
1386
- mkdirSync(EXE_AI_DIR, { recursive: true });
1387
- writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
1388
- }
1389
- async function verifyLicenseJwt(token) {
1390
- try {
1391
- const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
1392
- const { payload } = await jwtVerify(token, key, {
1393
- algorithms: [LICENSE_JWT_ALG]
1394
- });
1395
- const plan = payload.plan ?? "free";
1396
- const email = payload.sub ?? "";
1397
- const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
1398
- return {
1399
- valid: true,
1400
- plan,
1401
- email,
1402
- expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
1403
- deviceLimit: limits.devices,
1404
- employeeLimit: limits.employees,
1405
- memoryLimit: limits.memories
1406
- };
1407
- } catch {
1408
- return null;
1409
- }
1410
- }
1411
- async function getCachedLicense() {
1412
- try {
1413
- if (!existsSync4(CACHE_PATH)) return null;
1414
- const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
1415
- if (!raw.token || typeof raw.token !== "string") return null;
1416
- return await verifyLicenseJwt(raw.token);
1417
- } catch {
1418
- return null;
1419
- }
1420
- }
1421
- function cacheResponse(token) {
1422
- try {
1423
- writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
1424
- } catch {
1425
- }
1426
- }
1427
- async function validateLicense(apiKey, deviceId) {
1428
- const did = deviceId ?? loadDeviceId();
1429
- try {
1430
- const res = await fetchRetry(`${API_BASE}/auth/activate`, {
1431
- method: "POST",
1432
- headers: { "Content-Type": "application/json" },
1433
- body: JSON.stringify({ apiKey, deviceId: did }),
1434
- signal: AbortSignal.timeout(1e4)
1435
- });
1436
- if (res.ok) {
1437
- const data = await res.json();
1438
- if (data.error === "device_limit_exceeded") {
1439
- const cached2 = await getCachedLicense();
1440
- if (cached2) return cached2;
1441
- return { ...FREE_LICENSE, valid: false, plan: "free" };
1442
- }
1443
- if (data.token) {
1444
- cacheResponse(data.token);
1445
- const verified = await verifyLicenseJwt(data.token);
1446
- if (verified) return verified;
1447
- }
1448
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
1449
- return {
1450
- valid: data.valid,
1451
- plan: data.plan,
1452
- email: data.email,
1453
- expiresAt: data.expiresAt,
1454
- deviceLimit: limits.devices,
1455
- employeeLimit: limits.employees,
1456
- memoryLimit: limits.memories
1457
- };
1458
- }
1459
- const cached = await getCachedLicense();
1460
- if (cached) return cached;
1461
- return { ...FREE_LICENSE, valid: false, plan: "free" };
1462
- } catch {
1463
- const cached = await getCachedLicense();
1464
- if (cached) return cached;
1465
- return { ...FREE_LICENSE, valid: false, error: "offline" };
1466
- }
1467
- }
1468
- function getCacheAgeMs() {
1469
- try {
1470
- const { statSync } = __require("fs");
1471
- const s = statSync(CACHE_PATH);
1472
- return Date.now() - s.mtimeMs;
1473
- } catch {
1474
- return Infinity;
1475
- }
1476
- }
1477
- async function checkLicense() {
1478
- let key = loadLicense();
1479
- if (!key) {
1480
- try {
1481
- const configPath = path4.join(EXE_AI_DIR, "config.json");
1482
- if (existsSync4(configPath)) {
1483
- const raw = JSON.parse(readFileSync3(configPath, "utf8"));
1484
- const cloud = raw.cloud;
1485
- if (cloud?.apiKey) {
1486
- key = cloud.apiKey;
1487
- saveLicense(key);
1488
- }
1489
- }
1490
- } catch {
1491
- }
1492
- }
1493
- if (!key) return FREE_LICENSE;
1494
- const cached = await getCachedLicense();
1495
- if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
1496
- const deviceId = loadDeviceId();
1497
- return validateLicense(key, deviceId);
1498
- }
1499
- function isFeatureAllowed(license, feature) {
1500
- switch (feature) {
1501
- case "cloud_sync":
1502
- case "external_agents":
1503
- case "wiki":
1504
- return license.plan !== "free";
1505
- case "unlimited_employees":
1506
- return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
1507
- }
1508
- }
1509
- var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, CACHE_MAX_AGE_MS;
1510
- var init_license = __esm({
1511
- "src/lib/license.ts"() {
1512
- "use strict";
1513
- init_config();
1514
- LICENSE_PATH = path4.join(EXE_AI_DIR, "license.key");
1515
- CACHE_PATH = path4.join(EXE_AI_DIR, "license-cache.json");
1516
- DEVICE_ID_PATH = path4.join(EXE_AI_DIR, "device-id");
1517
- API_BASE = "https://askexe.com/cloud";
1518
- RETRY_DELAY_MS = 500;
1519
- LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1520
- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1521
- 4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
1522
- -----END PUBLIC KEY-----`;
1523
- LICENSE_JWT_ALG = "ES256";
1524
- PLAN_LIMITS = {
1525
- free: { devices: 1, employees: 1, memories: 5e3 },
1526
- pro: { devices: 2, employees: 5, memories: 1e5 },
1527
- team: { devices: 10, employees: 20, memories: 1e6 },
1528
- agency: { devices: 50, employees: 100, memories: 1e7 },
1529
- enterprise: { devices: -1, employees: -1, memories: -1 }
1530
- };
1531
- FREE_LICENSE = {
1532
- valid: true,
1533
- plan: "free",
1534
- email: "",
1535
- expiresAt: null,
1536
- deviceLimit: 1,
1537
- employeeLimit: 1,
1538
- memoryLimit: 5e3
1539
- };
1540
- CACHE_MAX_AGE_MS = 36e5;
1541
- }
1542
- });
1543
-
1544
- // src/lib/plan-limits.ts
1545
- import { readFileSync as readFileSync4, existsSync as existsSync5 } from "fs";
1546
- import path5 from "path";
1547
- async function assertFeature(feature) {
1548
- const license = await checkLicense();
1549
- if (!isFeatureAllowed(license, feature)) {
1550
- throw new PlanLimitError(
1551
- `Feature "${feature}" requires a paid plan. Current plan: ${license.plan}. Upgrade at https://askexe.com.`
1552
- );
1553
- }
1554
- }
1555
- var PlanLimitError, CACHE_PATH2;
1556
- var init_plan_limits = __esm({
1557
- "src/lib/plan-limits.ts"() {
1558
- "use strict";
1559
- init_database();
1560
- init_employees();
1561
- init_license();
1562
- init_config();
1563
- PlanLimitError = class extends Error {
1564
- constructor(message) {
1565
- super(message);
1566
- this.name = "PlanLimitError";
1567
- }
1568
- };
1569
- CACHE_PATH2 = path5.join(EXE_AI_DIR, "license-cache.json");
1376
+ EMPLOYEES_PATH = path4.join(EXE_AI_DIR, "exe-employees.json");
1570
1377
  }
1571
1378
  });
1572
1379
 
@@ -1596,13 +1403,13 @@ __export(cloud_sync_exports, {
1596
1403
  mergeRosterFromRemote: () => mergeRosterFromRemote,
1597
1404
  recordRosterDeletion: () => recordRosterDeletion
1598
1405
  });
1599
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync6, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
1406
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
1600
1407
  import crypto3 from "crypto";
1601
- import path6 from "path";
1408
+ import path5 from "path";
1602
1409
  import { homedir } from "os";
1603
1410
  function logError(msg) {
1604
1411
  try {
1605
- const logPath = path6.join(homedir(), ".exe-os", "workers.log");
1412
+ const logPath = path5.join(homedir(), ".exe-os", "workers.log");
1606
1413
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
1607
1414
  `);
1608
1415
  } catch {
@@ -1616,7 +1423,7 @@ async function withRosterLock(fn) {
1616
1423
  } catch (err) {
1617
1424
  if (err.code === "EEXIST") {
1618
1425
  try {
1619
- const ts = parseInt(readFileSync5(ROSTER_LOCK_PATH, "utf-8"), 10);
1426
+ const ts = parseInt(readFileSync4(ROSTER_LOCK_PATH, "utf-8"), 10);
1620
1427
  if (Date.now() - ts < LOCK_STALE_MS) {
1621
1428
  throw new Error("Roster merge already in progress \u2014 another sync is running");
1622
1429
  }
@@ -1743,7 +1550,6 @@ async function cloudPull(sinceVersion, config) {
1743
1550
  }
1744
1551
  }
1745
1552
  async function cloudSync(config) {
1746
- await assertFeature("cloud_sync");
1747
1553
  let client;
1748
1554
  try {
1749
1555
  client = getClient();
@@ -1924,8 +1730,8 @@ async function cloudSync(config) {
1924
1730
  function recordRosterDeletion(name) {
1925
1731
  let deletions = [];
1926
1732
  try {
1927
- if (existsSync6(ROSTER_DELETIONS_PATH)) {
1928
- deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
1733
+ if (existsSync5(ROSTER_DELETIONS_PATH)) {
1734
+ deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
1929
1735
  }
1930
1736
  } catch {
1931
1737
  }
@@ -1934,8 +1740,8 @@ function recordRosterDeletion(name) {
1934
1740
  }
1935
1741
  function consumeRosterDeletions() {
1936
1742
  try {
1937
- if (!existsSync6(ROSTER_DELETIONS_PATH)) return [];
1938
- const deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
1743
+ if (!existsSync5(ROSTER_DELETIONS_PATH)) return [];
1744
+ const deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
1939
1745
  writeFileSync2(ROSTER_DELETIONS_PATH, "[]");
1940
1746
  return deletions;
1941
1747
  } catch {
@@ -1943,29 +1749,29 @@ function consumeRosterDeletions() {
1943
1749
  }
1944
1750
  }
1945
1751
  function buildRosterBlob(paths) {
1946
- const rosterPath = paths?.rosterPath ?? path6.join(EXE_AI_DIR, "exe-employees.json");
1947
- const identityDir = paths?.identityDir ?? path6.join(EXE_AI_DIR, "identity");
1948
- const configPath = paths?.configPath ?? path6.join(EXE_AI_DIR, "config.json");
1752
+ const rosterPath = paths?.rosterPath ?? path5.join(EXE_AI_DIR, "exe-employees.json");
1753
+ const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
1754
+ const configPath = paths?.configPath ?? path5.join(EXE_AI_DIR, "config.json");
1949
1755
  let roster = [];
1950
- if (existsSync6(rosterPath)) {
1756
+ if (existsSync5(rosterPath)) {
1951
1757
  try {
1952
- roster = JSON.parse(readFileSync5(rosterPath, "utf-8"));
1758
+ roster = JSON.parse(readFileSync4(rosterPath, "utf-8"));
1953
1759
  } catch {
1954
1760
  }
1955
1761
  }
1956
1762
  const identities = {};
1957
- if (existsSync6(identityDir)) {
1763
+ if (existsSync5(identityDir)) {
1958
1764
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
1959
1765
  try {
1960
- identities[file] = readFileSync5(path6.join(identityDir, file), "utf-8");
1766
+ identities[file] = readFileSync4(path5.join(identityDir, file), "utf-8");
1961
1767
  } catch {
1962
1768
  }
1963
1769
  }
1964
1770
  }
1965
1771
  let config;
1966
- if (existsSync6(configPath)) {
1772
+ if (existsSync5(configPath)) {
1967
1773
  try {
1968
- config = JSON.parse(readFileSync5(configPath, "utf-8"));
1774
+ config = JSON.parse(readFileSync4(configPath, "utf-8"));
1969
1775
  } catch {
1970
1776
  }
1971
1777
  }
@@ -2041,23 +1847,23 @@ async function cloudPullRoster(config) {
2041
1847
  }
2042
1848
  }
2043
1849
  function mergeConfig(remoteConfig, configPath) {
2044
- const cfgPath = configPath ?? path6.join(EXE_AI_DIR, "config.json");
1850
+ const cfgPath = configPath ?? path5.join(EXE_AI_DIR, "config.json");
2045
1851
  let local = {};
2046
- if (existsSync6(cfgPath)) {
1852
+ if (existsSync5(cfgPath)) {
2047
1853
  try {
2048
- local = JSON.parse(readFileSync5(cfgPath, "utf-8"));
1854
+ local = JSON.parse(readFileSync4(cfgPath, "utf-8"));
2049
1855
  } catch {
2050
1856
  }
2051
1857
  }
2052
1858
  const merged = { ...remoteConfig, ...local };
2053
- const dir = path6.dirname(cfgPath);
2054
- if (!existsSync6(dir)) mkdirSync2(dir, { recursive: true });
1859
+ const dir = path5.dirname(cfgPath);
1860
+ if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
2055
1861
  writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
2056
1862
  }
2057
1863
  async function mergeRosterFromRemote(remote, paths) {
2058
1864
  return withRosterLock(async () => {
2059
1865
  const rosterPath = paths?.rosterPath ?? void 0;
2060
- const identityDir = paths?.identityDir ?? path6.join(EXE_AI_DIR, "identity");
1866
+ const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
2061
1867
  const localEmployees = await loadEmployees(rosterPath);
2062
1868
  const localNames = new Set(localEmployees.map((e) => e.name));
2063
1869
  let added = 0;
@@ -2067,9 +1873,9 @@ async function mergeRosterFromRemote(remote, paths) {
2067
1873
  localNames.add(remoteEmp.name);
2068
1874
  added++;
2069
1875
  if (remote.identities[`${remoteEmp.name}.md`]) {
2070
- if (!existsSync6(identityDir)) mkdirSync2(identityDir, { recursive: true });
2071
- const idPath = path6.join(identityDir, `${remoteEmp.name}.md`);
2072
- if (!existsSync6(idPath)) {
1876
+ if (!existsSync5(identityDir)) mkdirSync2(identityDir, { recursive: true });
1877
+ const idPath = path5.join(identityDir, `${remoteEmp.name}.md`);
1878
+ if (!existsSync5(idPath)) {
2073
1879
  writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
2074
1880
  }
2075
1881
  }
@@ -2479,16 +2285,15 @@ var init_cloud_sync = __esm({
2479
2285
  init_database();
2480
2286
  init_crypto();
2481
2287
  init_compress();
2482
- init_plan_limits();
2483
2288
  init_license();
2484
2289
  init_config();
2485
2290
  init_employees();
2486
2291
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
2487
2292
  FETCH_TIMEOUT_MS = 3e4;
2488
2293
  PUSH_BATCH_SIZE = 5e3;
2489
- ROSTER_LOCK_PATH = path6.join(EXE_AI_DIR, "roster-merge.lock");
2294
+ ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
2490
2295
  LOCK_STALE_MS = 3e4;
2491
- ROSTER_DELETIONS_PATH = path6.join(EXE_AI_DIR, "roster-deletions.json");
2296
+ ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
2492
2297
  }
2493
2298
  });
2494
2299
 
@@ -230,9 +230,9 @@ function isMainModule(importMetaUrl) {
230
230
 
231
231
  // src/lib/cloud-sync.ts
232
232
  init_database();
233
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
233
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
234
234
  import crypto2 from "crypto";
235
- import path5 from "path";
235
+ import path4 from "path";
236
236
  import { homedir } from "os";
237
237
 
238
238
  // src/lib/crypto.ts
@@ -241,33 +241,25 @@ import crypto from "crypto";
241
241
  // src/lib/compress.ts
242
242
  import { brotliCompressSync, brotliDecompressSync, constants } from "zlib";
243
243
 
244
- // src/lib/plan-limits.ts
245
- init_database();
246
- import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
247
- import path4 from "path";
244
+ // src/lib/license.ts
245
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
246
+ import { randomUUID } from "crypto";
247
+ import path2 from "path";
248
+ import { jwtVerify, importSPKI } from "jose";
249
+ var LICENSE_PATH = path2.join(EXE_AI_DIR, "license.key");
250
+ var CACHE_PATH = path2.join(EXE_AI_DIR, "license-cache.json");
251
+ var DEVICE_ID_PATH = path2.join(EXE_AI_DIR, "device-id");
248
252
 
249
253
  // src/lib/employees.ts
250
254
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
251
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
255
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
252
256
  import { execSync } from "child_process";
253
- import path2 from "path";
254
- var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
255
-
256
- // src/lib/license.ts
257
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
258
- import { randomUUID } from "crypto";
259
257
  import path3 from "path";
260
- import { jwtVerify, importSPKI } from "jose";
261
- var LICENSE_PATH = path3.join(EXE_AI_DIR, "license.key");
262
- var CACHE_PATH = path3.join(EXE_AI_DIR, "license-cache.json");
263
- var DEVICE_ID_PATH = path3.join(EXE_AI_DIR, "device-id");
264
-
265
- // src/lib/plan-limits.ts
266
- var CACHE_PATH2 = path4.join(EXE_AI_DIR, "license-cache.json");
258
+ var EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
267
259
 
268
260
  // src/lib/cloud-sync.ts
269
261
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
270
- var ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
262
+ var ROSTER_LOCK_PATH = path4.join(EXE_AI_DIR, "roster-merge.lock");
271
263
  function assertSecureEndpoint(endpoint) {
272
264
  if (endpoint.startsWith("https://")) return;
273
265
  if (endpoint.startsWith("http://")) {
@@ -282,7 +274,7 @@ function assertSecureEndpoint(endpoint) {
282
274
  );
283
275
  }
284
276
  }
285
- var ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
277
+ var ROSTER_DELETIONS_PATH = path4.join(EXE_AI_DIR, "roster-deletions.json");
286
278
 
287
279
  // src/bin/exe-settings.ts
288
280
  function label(value) {
@@ -2709,7 +2709,6 @@ async function cloudPull(sinceVersion, config) {
2709
2709
  }
2710
2710
  }
2711
2711
  async function cloudSync(config) {
2712
- await assertFeature("cloud_sync");
2713
2712
  let client;
2714
2713
  try {
2715
2714
  client = getClient();
@@ -3445,7 +3444,6 @@ var init_cloud_sync = __esm({
3445
3444
  init_database();
3446
3445
  init_crypto();
3447
3446
  init_compress();
3448
- init_plan_limits();
3449
3447
  init_license();
3450
3448
  init_config();
3451
3449
  init_employees();
@@ -1,11 +1,5 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __getOwnPropNames = Object.getOwnPropertyNames;
3
- var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
4
- get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
5
- }) : x)(function(x) {
6
- if (typeof require !== "undefined") return require.apply(this, arguments);
7
- throw Error('Dynamic require of "' + x + '" is not supported');
8
- });
9
3
  var __esm = (fn, res) => function __init() {
10
4
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
5
  };
@@ -945,9 +939,9 @@ var init_database = __esm({
945
939
 
946
940
  // src/lib/cloud-sync.ts
947
941
  init_database();
948
- import { readFileSync as readFileSync5, writeFileSync as writeFileSync2, existsSync as existsSync5, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
942
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync2, existsSync as existsSync4, readdirSync, mkdirSync as mkdirSync2, appendFileSync, unlinkSync, openSync, closeSync } from "fs";
949
943
  import crypto2 from "crypto";
950
- import path5 from "path";
944
+ import path4 from "path";
951
945
  import { homedir } from "os";
952
946
 
953
947
  // src/lib/crypto.ts
@@ -999,16 +993,11 @@ function decompress(input) {
999
993
  return brotliDecompressSync(input);
1000
994
  }
1001
995
 
1002
- // src/lib/plan-limits.ts
1003
- init_database();
1004
- import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
1005
- import path4 from "path";
1006
-
1007
- // src/lib/employees.ts
1008
- import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1009
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2 } from "fs";
1010
- import { execSync } from "child_process";
996
+ // src/lib/license.ts
997
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
998
+ import { randomUUID } from "crypto";
1011
999
  import path2 from "path";
1000
+ import { jwtVerify, importSPKI } from "jose";
1012
1001
 
1013
1002
  // src/lib/config.ts
1014
1003
  import { readFile, writeFile, mkdir, chmod } from "fs/promises";
@@ -1101,10 +1090,40 @@ var DEFAULT_CONFIG = {
1101
1090
  }
1102
1091
  };
1103
1092
 
1093
+ // src/lib/license.ts
1094
+ var LICENSE_PATH = path2.join(EXE_AI_DIR, "license.key");
1095
+ var CACHE_PATH = path2.join(EXE_AI_DIR, "license-cache.json");
1096
+ var DEVICE_ID_PATH = path2.join(EXE_AI_DIR, "device-id");
1097
+ function loadDeviceId() {
1098
+ const deviceJsonPath = path2.join(EXE_AI_DIR, "device.json");
1099
+ try {
1100
+ if (existsSync2(deviceJsonPath)) {
1101
+ const data = JSON.parse(readFileSync2(deviceJsonPath, "utf8"));
1102
+ if (data.deviceId) return data.deviceId;
1103
+ }
1104
+ } catch {
1105
+ }
1106
+ try {
1107
+ if (existsSync2(DEVICE_ID_PATH)) {
1108
+ const id2 = readFileSync2(DEVICE_ID_PATH, "utf8").trim();
1109
+ if (id2) return id2;
1110
+ }
1111
+ } catch {
1112
+ }
1113
+ const id = randomUUID();
1114
+ mkdirSync(EXE_AI_DIR, { recursive: true });
1115
+ writeFileSync(DEVICE_ID_PATH, id, "utf8");
1116
+ return id;
1117
+ }
1118
+
1104
1119
  // src/lib/employees.ts
1105
- var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
1120
+ import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1121
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync3 } from "fs";
1122
+ import { execSync } from "child_process";
1123
+ import path3 from "path";
1124
+ var EMPLOYEES_PATH = path3.join(EXE_AI_DIR, "exe-employees.json");
1106
1125
  async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1107
- if (!existsSync2(employeesPath)) {
1126
+ if (!existsSync3(employeesPath)) {
1108
1127
  return [];
1109
1128
  }
1110
1129
  const raw = await readFile2(employeesPath, "utf-8");
@@ -1115,7 +1134,7 @@ async function loadEmployees(employeesPath = EMPLOYEES_PATH) {
1115
1134
  }
1116
1135
  }
1117
1136
  async function saveEmployees(employees, employeesPath = EMPLOYEES_PATH) {
1118
- await mkdir2(path2.dirname(employeesPath), { recursive: true });
1137
+ await mkdir2(path3.dirname(employeesPath), { recursive: true });
1119
1138
  await writeFile2(employeesPath, JSON.stringify(employees, null, 2) + "\n", "utf-8");
1120
1139
  }
1121
1140
  function findExeBin() {
@@ -1134,7 +1153,7 @@ function registerBinSymlinks(name) {
1134
1153
  errors.push("Could not find 'exe-os' in PATH");
1135
1154
  return { created, skipped, errors };
1136
1155
  }
1137
- const binDir = path2.dirname(exeBinPath);
1156
+ const binDir = path3.dirname(exeBinPath);
1138
1157
  let target;
1139
1158
  try {
1140
1159
  target = readlinkSync(exeBinPath);
@@ -1144,8 +1163,8 @@ function registerBinSymlinks(name) {
1144
1163
  }
1145
1164
  for (const suffix of ["", "-opencode"]) {
1146
1165
  const linkName = `${name}${suffix}`;
1147
- const linkPath = path2.join(binDir, linkName);
1148
- if (existsSync2(linkPath)) {
1166
+ const linkPath = path3.join(binDir, linkName);
1167
+ if (existsSync3(linkPath)) {
1149
1168
  skipped.push(linkName);
1150
1169
  continue;
1151
1170
  }
@@ -1159,221 +1178,10 @@ function registerBinSymlinks(name) {
1159
1178
  return { created, skipped, errors };
1160
1179
  }
1161
1180
 
1162
- // src/lib/license.ts
1163
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync } from "fs";
1164
- import { randomUUID } from "crypto";
1165
- import path3 from "path";
1166
- import { jwtVerify, importSPKI } from "jose";
1167
- var LICENSE_PATH = path3.join(EXE_AI_DIR, "license.key");
1168
- var CACHE_PATH = path3.join(EXE_AI_DIR, "license-cache.json");
1169
- var DEVICE_ID_PATH = path3.join(EXE_AI_DIR, "device-id");
1170
- var API_BASE = "https://askexe.com/cloud";
1171
- var RETRY_DELAY_MS = 500;
1172
- async function fetchRetry(url, init) {
1173
- try {
1174
- return await fetch(url, init);
1175
- } catch {
1176
- await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
1177
- return fetch(url, { ...init, signal: AbortSignal.timeout(1e4) });
1178
- }
1179
- }
1180
- var LICENSE_PUBLIC_KEY_PEM = `-----BEGIN PUBLIC KEY-----
1181
- MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1182
- 4uj+UqeKCcvtgNHKmOK278HJaJcANe9xAeji8AFYu27q3WtzCi04pHudow==
1183
- -----END PUBLIC KEY-----`;
1184
- var LICENSE_JWT_ALG = "ES256";
1185
- var PLAN_LIMITS = {
1186
- free: { devices: 1, employees: 1, memories: 5e3 },
1187
- pro: { devices: 2, employees: 5, memories: 1e5 },
1188
- team: { devices: 10, employees: 20, memories: 1e6 },
1189
- agency: { devices: 50, employees: 100, memories: 1e7 },
1190
- enterprise: { devices: -1, employees: -1, memories: -1 }
1191
- };
1192
- var FREE_LICENSE = {
1193
- valid: true,
1194
- plan: "free",
1195
- email: "",
1196
- expiresAt: null,
1197
- deviceLimit: 1,
1198
- employeeLimit: 1,
1199
- memoryLimit: 5e3
1200
- };
1201
- function loadDeviceId() {
1202
- const deviceJsonPath = path3.join(EXE_AI_DIR, "device.json");
1203
- try {
1204
- if (existsSync3(deviceJsonPath)) {
1205
- const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
1206
- if (data.deviceId) return data.deviceId;
1207
- }
1208
- } catch {
1209
- }
1210
- try {
1211
- if (existsSync3(DEVICE_ID_PATH)) {
1212
- const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
1213
- if (id2) return id2;
1214
- }
1215
- } catch {
1216
- }
1217
- const id = randomUUID();
1218
- mkdirSync(EXE_AI_DIR, { recursive: true });
1219
- writeFileSync(DEVICE_ID_PATH, id, "utf8");
1220
- return id;
1221
- }
1222
- function loadLicense() {
1223
- try {
1224
- if (!existsSync3(LICENSE_PATH)) return null;
1225
- return readFileSync3(LICENSE_PATH, "utf8").trim();
1226
- } catch {
1227
- return null;
1228
- }
1229
- }
1230
- function saveLicense(apiKey) {
1231
- mkdirSync(EXE_AI_DIR, { recursive: true });
1232
- writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
1233
- }
1234
- async function verifyLicenseJwt(token) {
1235
- try {
1236
- const key = await importSPKI(LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG);
1237
- const { payload } = await jwtVerify(token, key, {
1238
- algorithms: [LICENSE_JWT_ALG]
1239
- });
1240
- const plan = payload.plan ?? "free";
1241
- const email = payload.sub ?? "";
1242
- const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
1243
- return {
1244
- valid: true,
1245
- plan,
1246
- email,
1247
- expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
1248
- deviceLimit: limits.devices,
1249
- employeeLimit: limits.employees,
1250
- memoryLimit: limits.memories
1251
- };
1252
- } catch {
1253
- return null;
1254
- }
1255
- }
1256
- async function getCachedLicense() {
1257
- try {
1258
- if (!existsSync3(CACHE_PATH)) return null;
1259
- const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
1260
- if (!raw.token || typeof raw.token !== "string") return null;
1261
- return await verifyLicenseJwt(raw.token);
1262
- } catch {
1263
- return null;
1264
- }
1265
- }
1266
- function cacheResponse(token) {
1267
- try {
1268
- writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
1269
- } catch {
1270
- }
1271
- }
1272
- async function validateLicense(apiKey, deviceId) {
1273
- const did = deviceId ?? loadDeviceId();
1274
- try {
1275
- const res = await fetchRetry(`${API_BASE}/auth/activate`, {
1276
- method: "POST",
1277
- headers: { "Content-Type": "application/json" },
1278
- body: JSON.stringify({ apiKey, deviceId: did }),
1279
- signal: AbortSignal.timeout(1e4)
1280
- });
1281
- if (res.ok) {
1282
- const data = await res.json();
1283
- if (data.error === "device_limit_exceeded") {
1284
- const cached2 = await getCachedLicense();
1285
- if (cached2) return cached2;
1286
- return { ...FREE_LICENSE, valid: false, plan: "free" };
1287
- }
1288
- if (data.token) {
1289
- cacheResponse(data.token);
1290
- const verified = await verifyLicenseJwt(data.token);
1291
- if (verified) return verified;
1292
- }
1293
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
1294
- return {
1295
- valid: data.valid,
1296
- plan: data.plan,
1297
- email: data.email,
1298
- expiresAt: data.expiresAt,
1299
- deviceLimit: limits.devices,
1300
- employeeLimit: limits.employees,
1301
- memoryLimit: limits.memories
1302
- };
1303
- }
1304
- const cached = await getCachedLicense();
1305
- if (cached) return cached;
1306
- return { ...FREE_LICENSE, valid: false, plan: "free" };
1307
- } catch {
1308
- const cached = await getCachedLicense();
1309
- if (cached) return cached;
1310
- return { ...FREE_LICENSE, valid: false, error: "offline" };
1311
- }
1312
- }
1313
- var CACHE_MAX_AGE_MS = 36e5;
1314
- function getCacheAgeMs() {
1315
- try {
1316
- const { statSync } = __require("fs");
1317
- const s = statSync(CACHE_PATH);
1318
- return Date.now() - s.mtimeMs;
1319
- } catch {
1320
- return Infinity;
1321
- }
1322
- }
1323
- async function checkLicense() {
1324
- let key = loadLicense();
1325
- if (!key) {
1326
- try {
1327
- const configPath = path3.join(EXE_AI_DIR, "config.json");
1328
- if (existsSync3(configPath)) {
1329
- const raw = JSON.parse(readFileSync3(configPath, "utf8"));
1330
- const cloud = raw.cloud;
1331
- if (cloud?.apiKey) {
1332
- key = cloud.apiKey;
1333
- saveLicense(key);
1334
- }
1335
- }
1336
- } catch {
1337
- }
1338
- }
1339
- if (!key) return FREE_LICENSE;
1340
- const cached = await getCachedLicense();
1341
- if (cached && getCacheAgeMs() < CACHE_MAX_AGE_MS) return cached;
1342
- const deviceId = loadDeviceId();
1343
- return validateLicense(key, deviceId);
1344
- }
1345
- function isFeatureAllowed(license, feature) {
1346
- switch (feature) {
1347
- case "cloud_sync":
1348
- case "external_agents":
1349
- case "wiki":
1350
- return license.plan !== "free";
1351
- case "unlimited_employees":
1352
- return license.plan === "team" || license.plan === "agency" || license.plan === "enterprise";
1353
- }
1354
- }
1355
-
1356
- // src/lib/plan-limits.ts
1357
- var PlanLimitError = class extends Error {
1358
- constructor(message) {
1359
- super(message);
1360
- this.name = "PlanLimitError";
1361
- }
1362
- };
1363
- var CACHE_PATH2 = path4.join(EXE_AI_DIR, "license-cache.json");
1364
- async function assertFeature(feature) {
1365
- const license = await checkLicense();
1366
- if (!isFeatureAllowed(license, feature)) {
1367
- throw new PlanLimitError(
1368
- `Feature "${feature}" requires a paid plan. Current plan: ${license.plan}. Upgrade at https://askexe.com.`
1369
- );
1370
- }
1371
- }
1372
-
1373
1181
  // src/lib/cloud-sync.ts
1374
1182
  function logError(msg) {
1375
1183
  try {
1376
- const logPath = path5.join(homedir(), ".exe-os", "workers.log");
1184
+ const logPath = path4.join(homedir(), ".exe-os", "workers.log");
1377
1185
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
1378
1186
  `);
1379
1187
  } catch {
@@ -1382,7 +1190,7 @@ function logError(msg) {
1382
1190
  var LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
1383
1191
  var FETCH_TIMEOUT_MS = 3e4;
1384
1192
  var PUSH_BATCH_SIZE = 5e3;
1385
- var ROSTER_LOCK_PATH = path5.join(EXE_AI_DIR, "roster-merge.lock");
1193
+ var ROSTER_LOCK_PATH = path4.join(EXE_AI_DIR, "roster-merge.lock");
1386
1194
  var LOCK_STALE_MS = 3e4;
1387
1195
  async function withRosterLock(fn) {
1388
1196
  try {
@@ -1392,7 +1200,7 @@ async function withRosterLock(fn) {
1392
1200
  } catch (err) {
1393
1201
  if (err.code === "EEXIST") {
1394
1202
  try {
1395
- const ts = parseInt(readFileSync5(ROSTER_LOCK_PATH, "utf-8"), 10);
1203
+ const ts = parseInt(readFileSync4(ROSTER_LOCK_PATH, "utf-8"), 10);
1396
1204
  if (Date.now() - ts < LOCK_STALE_MS) {
1397
1205
  throw new Error("Roster merge already in progress \u2014 another sync is running");
1398
1206
  }
@@ -1519,7 +1327,6 @@ async function cloudPull(sinceVersion, config) {
1519
1327
  }
1520
1328
  }
1521
1329
  async function cloudSync(config) {
1522
- await assertFeature("cloud_sync");
1523
1330
  let client;
1524
1331
  try {
1525
1332
  client = getClient();
@@ -1697,12 +1504,12 @@ async function cloudSync(config) {
1697
1504
  documents: documentsResult
1698
1505
  };
1699
1506
  }
1700
- var ROSTER_DELETIONS_PATH = path5.join(EXE_AI_DIR, "roster-deletions.json");
1507
+ var ROSTER_DELETIONS_PATH = path4.join(EXE_AI_DIR, "roster-deletions.json");
1701
1508
  function recordRosterDeletion(name) {
1702
1509
  let deletions = [];
1703
1510
  try {
1704
- if (existsSync5(ROSTER_DELETIONS_PATH)) {
1705
- deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
1511
+ if (existsSync4(ROSTER_DELETIONS_PATH)) {
1512
+ deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
1706
1513
  }
1707
1514
  } catch {
1708
1515
  }
@@ -1711,8 +1518,8 @@ function recordRosterDeletion(name) {
1711
1518
  }
1712
1519
  function consumeRosterDeletions() {
1713
1520
  try {
1714
- if (!existsSync5(ROSTER_DELETIONS_PATH)) return [];
1715
- const deletions = JSON.parse(readFileSync5(ROSTER_DELETIONS_PATH, "utf-8"));
1521
+ if (!existsSync4(ROSTER_DELETIONS_PATH)) return [];
1522
+ const deletions = JSON.parse(readFileSync4(ROSTER_DELETIONS_PATH, "utf-8"));
1716
1523
  writeFileSync2(ROSTER_DELETIONS_PATH, "[]");
1717
1524
  return deletions;
1718
1525
  } catch {
@@ -1720,29 +1527,29 @@ function consumeRosterDeletions() {
1720
1527
  }
1721
1528
  }
1722
1529
  function buildRosterBlob(paths) {
1723
- const rosterPath = paths?.rosterPath ?? path5.join(EXE_AI_DIR, "exe-employees.json");
1724
- const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
1725
- const configPath = paths?.configPath ?? path5.join(EXE_AI_DIR, "config.json");
1530
+ const rosterPath = paths?.rosterPath ?? path4.join(EXE_AI_DIR, "exe-employees.json");
1531
+ const identityDir = paths?.identityDir ?? path4.join(EXE_AI_DIR, "identity");
1532
+ const configPath = paths?.configPath ?? path4.join(EXE_AI_DIR, "config.json");
1726
1533
  let roster = [];
1727
- if (existsSync5(rosterPath)) {
1534
+ if (existsSync4(rosterPath)) {
1728
1535
  try {
1729
- roster = JSON.parse(readFileSync5(rosterPath, "utf-8"));
1536
+ roster = JSON.parse(readFileSync4(rosterPath, "utf-8"));
1730
1537
  } catch {
1731
1538
  }
1732
1539
  }
1733
1540
  const identities = {};
1734
- if (existsSync5(identityDir)) {
1541
+ if (existsSync4(identityDir)) {
1735
1542
  for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
1736
1543
  try {
1737
- identities[file] = readFileSync5(path5.join(identityDir, file), "utf-8");
1544
+ identities[file] = readFileSync4(path4.join(identityDir, file), "utf-8");
1738
1545
  } catch {
1739
1546
  }
1740
1547
  }
1741
1548
  }
1742
1549
  let config;
1743
- if (existsSync5(configPath)) {
1550
+ if (existsSync4(configPath)) {
1744
1551
  try {
1745
- config = JSON.parse(readFileSync5(configPath, "utf-8"));
1552
+ config = JSON.parse(readFileSync4(configPath, "utf-8"));
1746
1553
  } catch {
1747
1554
  }
1748
1555
  }
@@ -1818,23 +1625,23 @@ async function cloudPullRoster(config) {
1818
1625
  }
1819
1626
  }
1820
1627
  function mergeConfig(remoteConfig, configPath) {
1821
- const cfgPath = configPath ?? path5.join(EXE_AI_DIR, "config.json");
1628
+ const cfgPath = configPath ?? path4.join(EXE_AI_DIR, "config.json");
1822
1629
  let local = {};
1823
- if (existsSync5(cfgPath)) {
1630
+ if (existsSync4(cfgPath)) {
1824
1631
  try {
1825
- local = JSON.parse(readFileSync5(cfgPath, "utf-8"));
1632
+ local = JSON.parse(readFileSync4(cfgPath, "utf-8"));
1826
1633
  } catch {
1827
1634
  }
1828
1635
  }
1829
1636
  const merged = { ...remoteConfig, ...local };
1830
- const dir = path5.dirname(cfgPath);
1831
- if (!existsSync5(dir)) mkdirSync2(dir, { recursive: true });
1637
+ const dir = path4.dirname(cfgPath);
1638
+ if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
1832
1639
  writeFileSync2(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
1833
1640
  }
1834
1641
  async function mergeRosterFromRemote(remote, paths) {
1835
1642
  return withRosterLock(async () => {
1836
1643
  const rosterPath = paths?.rosterPath ?? void 0;
1837
- const identityDir = paths?.identityDir ?? path5.join(EXE_AI_DIR, "identity");
1644
+ const identityDir = paths?.identityDir ?? path4.join(EXE_AI_DIR, "identity");
1838
1645
  const localEmployees = await loadEmployees(rosterPath);
1839
1646
  const localNames = new Set(localEmployees.map((e) => e.name));
1840
1647
  let added = 0;
@@ -1844,9 +1651,9 @@ async function mergeRosterFromRemote(remote, paths) {
1844
1651
  localNames.add(remoteEmp.name);
1845
1652
  added++;
1846
1653
  if (remote.identities[`${remoteEmp.name}.md`]) {
1847
- if (!existsSync5(identityDir)) mkdirSync2(identityDir, { recursive: true });
1848
- const idPath = path5.join(identityDir, `${remoteEmp.name}.md`);
1849
- if (!existsSync5(idPath)) {
1654
+ if (!existsSync4(identityDir)) mkdirSync2(identityDir, { recursive: true });
1655
+ const idPath = path4.join(identityDir, `${remoteEmp.name}.md`);
1656
+ if (!existsSync4(idPath)) {
1850
1657
  writeFileSync2(idPath, remote.identities[`${remoteEmp.name}.md`], "utf-8");
1851
1658
  }
1852
1659
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askexenow/exe-os",
3
- "version": "0.8.45",
3
+ "version": "0.8.46",
4
4
  "description": "AI employee operating system — persistent memory, task management, and multi-agent coordination for Claude Code.",
5
5
  "license": "CC-BY-NC-4.0",
6
6
  "type": "module",