@askexenow/exe-os 0.8.59 → 0.8.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/cli.js CHANGED
@@ -4864,6 +4864,32 @@ function readCachedToken() {
4864
4864
  return null;
4865
4865
  }
4866
4866
  }
4867
+ function getRawCachedPlan() {
4868
+ try {
4869
+ const token = readCachedToken();
4870
+ if (!token) return null;
4871
+ const parts = token.split(".");
4872
+ if (parts.length !== 3) return null;
4873
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
4874
+ const plan = payload.plan ?? "free";
4875
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
4876
+ process.stderr.write(
4877
+ `[license] WARN: using unverified cached plan (API unreachable, JWT expired). Plan: ${plan}
4878
+ `
4879
+ );
4880
+ return {
4881
+ valid: true,
4882
+ plan,
4883
+ email: payload.sub ?? "",
4884
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
4885
+ deviceLimit: limits.devices,
4886
+ employeeLimit: limits.employees,
4887
+ memoryLimit: limits.memories
4888
+ };
4889
+ } catch {
4890
+ return null;
4891
+ }
4892
+ }
4867
4893
  function cacheResponse(token) {
4868
4894
  try {
4869
4895
  writeFileSync2(CACHE_PATH, JSON.stringify({ token }), "utf8");
@@ -4884,6 +4910,8 @@ async function validateLicense(apiKey, deviceId) {
4884
4910
  if (data.error === "device_limit_exceeded") {
4885
4911
  const cached2 = await getCachedLicense();
4886
4912
  if (cached2) return cached2;
4913
+ const raw2 = getRawCachedPlan();
4914
+ if (raw2) return { ...raw2, valid: false };
4887
4915
  return { ...FREE_LICENSE, valid: false, plan: "free" };
4888
4916
  }
4889
4917
  if (data.token) {
@@ -4904,10 +4932,14 @@ async function validateLicense(apiKey, deviceId) {
4904
4932
  }
4905
4933
  const cached = await getCachedLicense();
4906
4934
  if (cached) return cached;
4935
+ const raw = getRawCachedPlan();
4936
+ if (raw) return raw;
4907
4937
  return { ...FREE_LICENSE, valid: false, plan: "free" };
4908
4938
  } catch {
4909
4939
  const cached = await getCachedLicense();
4910
4940
  if (cached) return cached;
4941
+ const rawFallback = getRawCachedPlan();
4942
+ if (rawFallback) return rawFallback;
4911
4943
  return { ...FREE_LICENSE, valid: false, error: "offline" };
4912
4944
  }
4913
4945
  }
@@ -5096,8 +5128,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
5096
5128
  -----END PUBLIC KEY-----`;
5097
5129
  LICENSE_JWT_ALG = "ES256";
5098
5130
  PLAN_LIMITS = {
5099
- free: { devices: 1, employees: 1, memories: 5e3 },
5100
- pro: { devices: 2, employees: 5, memories: 1e5 },
5131
+ free: { devices: 1, employees: 1, memories: 5e4 },
5132
+ pro: { devices: 2, employees: 5, memories: 25e4 },
5101
5133
  team: { devices: 10, employees: 20, memories: 1e6 },
5102
5134
  agency: { devices: 50, employees: 100, memories: 1e7 },
5103
5135
  enterprise: { devices: -1, employees: -1, memories: -1 }
@@ -5109,7 +5141,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
5109
5141
  expiresAt: null,
5110
5142
  deviceLimit: 1,
5111
5143
  employeeLimit: 1,
5112
- memoryLimit: 5e3
5144
+ memoryLimit: 5e4
5113
5145
  };
5114
5146
  CACHE_MAX_AGE_MS = 36e5;
5115
5147
  _revalTimer = null;
@@ -15004,18 +15036,21 @@ function exportBehaviorsSync(agentId, projectName, sessionKey) {
15004
15036
  function getMySession() {
15005
15037
  return getTransport().getMySession();
15006
15038
  }
15039
+ function isRootSession(name) {
15040
+ return name.length > 0 && !name.includes("-");
15041
+ }
15007
15042
  function employeeSessionName(employee, exeSession, instance) {
15008
- if (!/^exe\d+$/.test(exeSession)) {
15043
+ if (!isRootSession(exeSession)) {
15009
15044
  const root = extractRootExe(exeSession);
15010
15045
  if (root) {
15011
15046
  process.stderr.write(
15012
- `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
15047
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root session, using "${root}" instead
15013
15048
  `
15014
15049
  );
15015
15050
  exeSession = root;
15016
15051
  } else {
15017
15052
  throw new Error(
15018
- `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
15053
+ `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
15019
15054
  );
15020
15055
  }
15021
15056
  }
@@ -15023,7 +15058,7 @@ function employeeSessionName(employee, exeSession, instance) {
15023
15058
  const name = `${employee}${suffix}-${exeSession}`;
15024
15059
  if (!VALID_SESSION_NAME.test(name)) {
15025
15060
  throw new Error(
15026
- `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
15061
+ `Invalid session name "${name}" \u2014 must match {agent}-{rootSession} or {agent}{instance}-{rootSession}`
15027
15062
  );
15028
15063
  }
15029
15064
  return name;
@@ -15266,11 +15301,11 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
15266
15301
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
15267
15302
  };
15268
15303
  }
15269
- if (!/^exe\d+$/.test(exeSession)) {
15304
+ if (!isRootSession(exeSession)) {
15270
15305
  const root = extractRootExe(exeSession);
15271
15306
  if (root) {
15272
15307
  process.stderr.write(
15273
- `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
15308
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root session). Auto-correcting to "${root}".
15274
15309
  `
15275
15310
  );
15276
15311
  exeSession = root;
@@ -15278,7 +15313,7 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
15278
15313
  return {
15279
15314
  status: "failed",
15280
15315
  sessionName: "",
15281
- error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
15316
+ error: `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
15282
15317
  };
15283
15318
  }
15284
15319
  }
@@ -15543,7 +15578,7 @@ var init_tmux_routing = __esm({
15543
15578
  SPAWN_LOCK_DIR = path24.join(os9.homedir(), ".exe-os", "spawn-locks");
15544
15579
  SESSION_CACHE = path24.join(os9.homedir(), ".exe-os", "session-cache");
15545
15580
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
15546
- VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
15581
+ VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
15547
15582
  VERIFY_PANE_LINES = 200;
15548
15583
  INTERCOM_DEBOUNCE_MS = 3e4;
15549
15584
  INTERCOM_LOG2 = path24.join(os9.homedir(), ".exe-os", "intercom.log");
@@ -2418,6 +2418,32 @@ function readCachedToken() {
2418
2418
  return null;
2419
2419
  }
2420
2420
  }
2421
+ function getRawCachedPlan() {
2422
+ try {
2423
+ const token = readCachedToken();
2424
+ if (!token) return null;
2425
+ const parts = token.split(".");
2426
+ if (parts.length !== 3) return null;
2427
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
2428
+ const plan = payload.plan ?? "free";
2429
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
2430
+ process.stderr.write(
2431
+ `[license] WARN: using unverified cached plan (API unreachable, JWT expired). Plan: ${plan}
2432
+ `
2433
+ );
2434
+ return {
2435
+ valid: true,
2436
+ plan,
2437
+ email: payload.sub ?? "",
2438
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
2439
+ deviceLimit: limits.devices,
2440
+ employeeLimit: limits.employees,
2441
+ memoryLimit: limits.memories
2442
+ };
2443
+ } catch {
2444
+ return null;
2445
+ }
2446
+ }
2421
2447
  function cacheResponse(token) {
2422
2448
  try {
2423
2449
  writeFileSync3(CACHE_PATH, JSON.stringify({ token }), "utf8");
@@ -2438,6 +2464,8 @@ async function validateLicense(apiKey, deviceId) {
2438
2464
  if (data.error === "device_limit_exceeded") {
2439
2465
  const cached2 = await getCachedLicense();
2440
2466
  if (cached2) return cached2;
2467
+ const raw2 = getRawCachedPlan();
2468
+ if (raw2) return { ...raw2, valid: false };
2441
2469
  return { ...FREE_LICENSE, valid: false, plan: "free" };
2442
2470
  }
2443
2471
  if (data.token) {
@@ -2458,10 +2486,14 @@ async function validateLicense(apiKey, deviceId) {
2458
2486
  }
2459
2487
  const cached = await getCachedLicense();
2460
2488
  if (cached) return cached;
2489
+ const raw = getRawCachedPlan();
2490
+ if (raw) return raw;
2461
2491
  return { ...FREE_LICENSE, valid: false, plan: "free" };
2462
2492
  } catch {
2463
2493
  const cached = await getCachedLicense();
2464
2494
  if (cached) return cached;
2495
+ const rawFallback = getRawCachedPlan();
2496
+ if (rawFallback) return rawFallback;
2465
2497
  return { ...FREE_LICENSE, valid: false, error: "offline" };
2466
2498
  }
2467
2499
  }
@@ -2650,8 +2682,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2650
2682
  -----END PUBLIC KEY-----`;
2651
2683
  LICENSE_JWT_ALG = "ES256";
2652
2684
  PLAN_LIMITS = {
2653
- free: { devices: 1, employees: 1, memories: 5e3 },
2654
- pro: { devices: 2, employees: 5, memories: 1e5 },
2685
+ free: { devices: 1, employees: 1, memories: 5e4 },
2686
+ pro: { devices: 2, employees: 5, memories: 25e4 },
2655
2687
  team: { devices: 10, employees: 20, memories: 1e6 },
2656
2688
  agency: { devices: 50, employees: 100, memories: 1e7 },
2657
2689
  enterprise: { devices: -1, employees: -1, memories: -1 }
@@ -2663,7 +2695,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
2663
2695
  expiresAt: null,
2664
2696
  deviceLimit: 1,
2665
2697
  employeeLimit: 1,
2666
- memoryLimit: 5e3
2698
+ memoryLimit: 5e4
2667
2699
  };
2668
2700
  CACHE_MAX_AGE_MS = 36e5;
2669
2701
  _revalTimer = null;
@@ -4786,18 +4818,21 @@ function exportBehaviorsSync(agentId, projectName, sessionKey) {
4786
4818
  function getMySession() {
4787
4819
  return getTransport().getMySession();
4788
4820
  }
4821
+ function isRootSession(name) {
4822
+ return name.length > 0 && !name.includes("-");
4823
+ }
4789
4824
  function employeeSessionName(employee, exeSession, instance) {
4790
- if (!/^exe\d+$/.test(exeSession)) {
4825
+ if (!isRootSession(exeSession)) {
4791
4826
  const root = extractRootExe(exeSession);
4792
4827
  if (root) {
4793
4828
  process.stderr.write(
4794
- `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
4829
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root session, using "${root}" instead
4795
4830
  `
4796
4831
  );
4797
4832
  exeSession = root;
4798
4833
  } else {
4799
4834
  throw new Error(
4800
- `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
4835
+ `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
4801
4836
  );
4802
4837
  }
4803
4838
  }
@@ -4805,7 +4840,7 @@ function employeeSessionName(employee, exeSession, instance) {
4805
4840
  const name = `${employee}${suffix}-${exeSession}`;
4806
4841
  if (!VALID_SESSION_NAME.test(name)) {
4807
4842
  throw new Error(
4808
- `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
4843
+ `Invalid session name "${name}" \u2014 must match {agent}-{rootSession} or {agent}{instance}-{rootSession}`
4809
4844
  );
4810
4845
  }
4811
4846
  return name;
@@ -5048,11 +5083,11 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
5048
5083
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
5049
5084
  };
5050
5085
  }
5051
- if (!/^exe\d+$/.test(exeSession)) {
5086
+ if (!isRootSession(exeSession)) {
5052
5087
  const root = extractRootExe(exeSession);
5053
5088
  if (root) {
5054
5089
  process.stderr.write(
5055
- `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
5090
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root session). Auto-correcting to "${root}".
5056
5091
  `
5057
5092
  );
5058
5093
  exeSession = root;
@@ -5060,7 +5095,7 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
5060
5095
  return {
5061
5096
  status: "failed",
5062
5097
  sessionName: "",
5063
- error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
5098
+ error: `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
5064
5099
  };
5065
5100
  }
5066
5101
  }
@@ -5325,7 +5360,7 @@ var init_tmux_routing = __esm({
5325
5360
  SPAWN_LOCK_DIR = path15.join(os6.homedir(), ".exe-os", "spawn-locks");
5326
5361
  SESSION_CACHE = path15.join(os6.homedir(), ".exe-os", "session-cache");
5327
5362
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
5328
- VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
5363
+ VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
5329
5364
  VERIFY_PANE_LINES = 200;
5330
5365
  INTERCOM_DEBOUNCE_MS = 3e4;
5331
5366
  INTERCOM_LOG2 = path15.join(os6.homedir(), ".exe-os", "intercom.log");
@@ -316,6 +316,32 @@ function readCachedToken() {
316
316
  return null;
317
317
  }
318
318
  }
319
+ function getRawCachedPlan() {
320
+ try {
321
+ const token = readCachedToken();
322
+ if (!token) return null;
323
+ const parts = token.split(".");
324
+ if (parts.length !== 3) return null;
325
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
326
+ const plan = payload.plan ?? "free";
327
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
328
+ process.stderr.write(
329
+ `[license] WARN: using unverified cached plan (API unreachable, JWT expired). Plan: ${plan}
330
+ `
331
+ );
332
+ return {
333
+ valid: true,
334
+ plan,
335
+ email: payload.sub ?? "",
336
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
337
+ deviceLimit: limits.devices,
338
+ employeeLimit: limits.employees,
339
+ memoryLimit: limits.memories
340
+ };
341
+ } catch {
342
+ return null;
343
+ }
344
+ }
319
345
  function cacheResponse(token) {
320
346
  try {
321
347
  writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
@@ -336,6 +362,8 @@ async function validateLicense(apiKey, deviceId) {
336
362
  if (data.error === "device_limit_exceeded") {
337
363
  const cached2 = await getCachedLicense();
338
364
  if (cached2) return cached2;
365
+ const raw2 = getRawCachedPlan();
366
+ if (raw2) return { ...raw2, valid: false };
339
367
  return { ...FREE_LICENSE, valid: false, plan: "free" };
340
368
  }
341
369
  if (data.token) {
@@ -356,10 +384,14 @@ async function validateLicense(apiKey, deviceId) {
356
384
  }
357
385
  const cached = await getCachedLicense();
358
386
  if (cached) return cached;
387
+ const raw = getRawCachedPlan();
388
+ if (raw) return raw;
359
389
  return { ...FREE_LICENSE, valid: false, plan: "free" };
360
390
  } catch {
361
391
  const cached = await getCachedLicense();
362
392
  if (cached) return cached;
393
+ const rawFallback = getRawCachedPlan();
394
+ if (rawFallback) return rawFallback;
363
395
  return { ...FREE_LICENSE, valid: false, error: "offline" };
364
396
  }
365
397
  }
@@ -548,8 +580,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
548
580
  -----END PUBLIC KEY-----`;
549
581
  LICENSE_JWT_ALG = "ES256";
550
582
  PLAN_LIMITS = {
551
- free: { devices: 1, employees: 1, memories: 5e3 },
552
- pro: { devices: 2, employees: 5, memories: 1e5 },
583
+ free: { devices: 1, employees: 1, memories: 5e4 },
584
+ pro: { devices: 2, employees: 5, memories: 25e4 },
553
585
  team: { devices: 10, employees: 20, memories: 1e6 },
554
586
  agency: { devices: 50, employees: 100, memories: 1e7 },
555
587
  enterprise: { devices: -1, employees: -1, memories: -1 }
@@ -561,7 +593,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
561
593
  expiresAt: null,
562
594
  deviceLimit: 1,
563
595
  employeeLimit: 1,
564
- memoryLimit: 5e3
596
+ memoryLimit: 5e4
565
597
  };
566
598
  CACHE_MAX_AGE_MS = 36e5;
567
599
  _revalTimer = null;
@@ -1460,8 +1460,8 @@ var init_license = __esm({
1460
1460
  CACHE_PATH = path5.join(EXE_AI_DIR, "license-cache.json");
1461
1461
  DEVICE_ID_PATH = path5.join(EXE_AI_DIR, "device-id");
1462
1462
  PLAN_LIMITS = {
1463
- free: { devices: 1, employees: 1, memories: 5e3 },
1464
- pro: { devices: 2, employees: 5, memories: 1e5 },
1463
+ free: { devices: 1, employees: 1, memories: 5e4 },
1464
+ pro: { devices: 2, employees: 5, memories: 25e4 },
1465
1465
  team: { devices: 10, employees: 20, memories: 1e6 },
1466
1466
  agency: { devices: 50, employees: 100, memories: 1e7 },
1467
1467
  enterprise: { devices: -1, employees: -1, memories: -1 }
@@ -3464,18 +3464,21 @@ function exportBehaviorsSync(agentId, projectName, sessionKey) {
3464
3464
  function getMySession() {
3465
3465
  return getTransport().getMySession();
3466
3466
  }
3467
+ function isRootSession(name) {
3468
+ return name.length > 0 && !name.includes("-");
3469
+ }
3467
3470
  function employeeSessionName(employee, exeSession2, instance) {
3468
- if (!/^exe\d+$/.test(exeSession2)) {
3471
+ if (!isRootSession(exeSession2)) {
3469
3472
  const root = extractRootExe(exeSession2);
3470
3473
  if (root) {
3471
3474
  process.stderr.write(
3472
- `[tmux-routing] WARN: exeSession="${exeSession2}" is not a root exe session, using "${root}" instead
3475
+ `[tmux-routing] WARN: exeSession="${exeSession2}" is not a root session, using "${root}" instead
3473
3476
  `
3474
3477
  );
3475
3478
  exeSession2 = root;
3476
3479
  } else {
3477
3480
  throw new Error(
3478
- `Invalid exeSession "${exeSession2}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
3481
+ `Invalid exeSession "${exeSession2}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
3479
3482
  );
3480
3483
  }
3481
3484
  }
@@ -3483,7 +3486,7 @@ function employeeSessionName(employee, exeSession2, instance) {
3483
3486
  const name = `${employee}${suffix}-${exeSession2}`;
3484
3487
  if (!VALID_SESSION_NAME.test(name)) {
3485
3488
  throw new Error(
3486
- `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
3489
+ `Invalid session name "${name}" \u2014 must match {agent}-{rootSession} or {agent}{instance}-{rootSession}`
3487
3490
  );
3488
3491
  }
3489
3492
  return name;
@@ -3726,11 +3729,11 @@ function ensureEmployee(employeeName2, exeSession2, projectDir2, opts) {
3726
3729
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName2}')`
3727
3730
  };
3728
3731
  }
3729
- if (!/^exe\d+$/.test(exeSession2)) {
3732
+ if (!isRootSession(exeSession2)) {
3730
3733
  const root = extractRootExe(exeSession2);
3731
3734
  if (root) {
3732
3735
  process.stderr.write(
3733
- `[ensureEmployee] WARN: caller passed exeSession="${exeSession2}" (not a root exe). Auto-correcting to "${root}".
3736
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession2}" (not a root session). Auto-correcting to "${root}".
3734
3737
  `
3735
3738
  );
3736
3739
  exeSession2 = root;
@@ -3738,7 +3741,7 @@ function ensureEmployee(employeeName2, exeSession2, projectDir2, opts) {
3738
3741
  return {
3739
3742
  status: "failed",
3740
3743
  sessionName: "",
3741
- error: `Invalid exeSession "${exeSession2}" \u2014 must be a root exe session (e.g., "exe1")`
3744
+ error: `Invalid exeSession "${exeSession2}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
3742
3745
  };
3743
3746
  }
3744
3747
  }
@@ -4003,7 +4006,7 @@ var init_tmux_routing = __esm({
4003
4006
  SPAWN_LOCK_DIR = path13.join(os5.homedir(), ".exe-os", "spawn-locks");
4004
4007
  SESSION_CACHE = path13.join(os5.homedir(), ".exe-os", "session-cache");
4005
4008
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
4006
- VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
4009
+ VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
4007
4010
  VERIFY_PANE_LINES = 200;
4008
4011
  INTERCOM_DEBOUNCE_MS = 3e4;
4009
4012
  INTERCOM_LOG2 = path13.join(os5.homedir(), ".exe-os", "intercom.log");
@@ -3123,6 +3123,32 @@ function readCachedToken() {
3123
3123
  return null;
3124
3124
  }
3125
3125
  }
3126
+ function getRawCachedPlan() {
3127
+ try {
3128
+ const token = readCachedToken();
3129
+ if (!token) return null;
3130
+ const parts = token.split(".");
3131
+ if (parts.length !== 3) return null;
3132
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
3133
+ const plan = payload.plan ?? "free";
3134
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
3135
+ process.stderr.write(
3136
+ `[license] WARN: using unverified cached plan (API unreachable, JWT expired). Plan: ${plan}
3137
+ `
3138
+ );
3139
+ return {
3140
+ valid: true,
3141
+ plan,
3142
+ email: payload.sub ?? "",
3143
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
3144
+ deviceLimit: limits.devices,
3145
+ employeeLimit: limits.employees,
3146
+ memoryLimit: limits.memories
3147
+ };
3148
+ } catch {
3149
+ return null;
3150
+ }
3151
+ }
3126
3152
  function cacheResponse(token) {
3127
3153
  try {
3128
3154
  writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
@@ -3143,6 +3169,8 @@ async function validateLicense(apiKey, deviceId) {
3143
3169
  if (data.error === "device_limit_exceeded") {
3144
3170
  const cached2 = await getCachedLicense();
3145
3171
  if (cached2) return cached2;
3172
+ const raw2 = getRawCachedPlan();
3173
+ if (raw2) return { ...raw2, valid: false };
3146
3174
  return { ...FREE_LICENSE, valid: false, plan: "free" };
3147
3175
  }
3148
3176
  if (data.token) {
@@ -3163,10 +3191,14 @@ async function validateLicense(apiKey, deviceId) {
3163
3191
  }
3164
3192
  const cached = await getCachedLicense();
3165
3193
  if (cached) return cached;
3194
+ const raw = getRawCachedPlan();
3195
+ if (raw) return raw;
3166
3196
  return { ...FREE_LICENSE, valid: false, plan: "free" };
3167
3197
  } catch {
3168
3198
  const cached = await getCachedLicense();
3169
3199
  if (cached) return cached;
3200
+ const rawFallback = getRawCachedPlan();
3201
+ if (rawFallback) return rawFallback;
3170
3202
  return { ...FREE_LICENSE, valid: false, error: "offline" };
3171
3203
  }
3172
3204
  }
@@ -3355,8 +3387,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
3355
3387
  -----END PUBLIC KEY-----`;
3356
3388
  LICENSE_JWT_ALG = "ES256";
3357
3389
  PLAN_LIMITS = {
3358
- free: { devices: 1, employees: 1, memories: 5e3 },
3359
- pro: { devices: 2, employees: 5, memories: 1e5 },
3390
+ free: { devices: 1, employees: 1, memories: 5e4 },
3391
+ pro: { devices: 2, employees: 5, memories: 25e4 },
3360
3392
  team: { devices: 10, employees: 20, memories: 1e6 },
3361
3393
  agency: { devices: 50, employees: 100, memories: 1e7 },
3362
3394
  enterprise: { devices: -1, employees: -1, memories: -1 }
@@ -3368,7 +3400,7 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
3368
3400
  expiresAt: null,
3369
3401
  deviceLimit: 1,
3370
3402
  employeeLimit: 1,
3371
- memoryLimit: 5e3
3403
+ memoryLimit: 5e4
3372
3404
  };
3373
3405
  CACHE_MAX_AGE_MS = 36e5;
3374
3406
  _revalTimer = null;
@@ -6942,18 +6974,21 @@ function exportBehaviorsSync(agentId, projectName, sessionKey) {
6942
6974
  function getMySession() {
6943
6975
  return getTransport().getMySession();
6944
6976
  }
6977
+ function isRootSession(name) {
6978
+ return name.length > 0 && !name.includes("-");
6979
+ }
6945
6980
  function employeeSessionName(employee, exeSession, instance) {
6946
- if (!/^exe\d+$/.test(exeSession)) {
6981
+ if (!isRootSession(exeSession)) {
6947
6982
  const root = extractRootExe(exeSession);
6948
6983
  if (root) {
6949
6984
  process.stderr.write(
6950
- `[tmux-routing] WARN: exeSession="${exeSession}" is not a root exe session, using "${root}" instead
6985
+ `[tmux-routing] WARN: exeSession="${exeSession}" is not a root session, using "${root}" instead
6951
6986
  `
6952
6987
  );
6953
6988
  exeSession = root;
6954
6989
  } else {
6955
6990
  throw new Error(
6956
- `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1"), not an agent session`
6991
+ `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
6957
6992
  );
6958
6993
  }
6959
6994
  }
@@ -6961,7 +6996,7 @@ function employeeSessionName(employee, exeSession, instance) {
6961
6996
  const name = `${employee}${suffix}-${exeSession}`;
6962
6997
  if (!VALID_SESSION_NAME.test(name)) {
6963
6998
  throw new Error(
6964
- `Invalid session name "${name}" \u2014 must match {agent}-exe{N} or {agent}{instance}-exe{N}`
6999
+ `Invalid session name "${name}" \u2014 must match {agent}-{rootSession} or {agent}{instance}-{rootSession}`
6965
7000
  );
6966
7001
  }
6967
7002
  return name;
@@ -7204,11 +7239,11 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
7204
7239
  error: `Error: pass employee name ('${bare}'), not session name ('${employeeName}')`
7205
7240
  };
7206
7241
  }
7207
- if (!/^exe\d+$/.test(exeSession)) {
7242
+ if (!isRootSession(exeSession)) {
7208
7243
  const root = extractRootExe(exeSession);
7209
7244
  if (root) {
7210
7245
  process.stderr.write(
7211
- `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root exe). Auto-correcting to "${root}".
7246
+ `[ensureEmployee] WARN: caller passed exeSession="${exeSession}" (not a root session). Auto-correcting to "${root}".
7212
7247
  `
7213
7248
  );
7214
7249
  exeSession = root;
@@ -7216,7 +7251,7 @@ function ensureEmployee(employeeName, exeSession, projectDir, opts) {
7216
7251
  return {
7217
7252
  status: "failed",
7218
7253
  sessionName: "",
7219
- error: `Invalid exeSession "${exeSession}" \u2014 must be a root exe session (e.g., "exe1")`
7254
+ error: `Invalid exeSession "${exeSession}" \u2014 contains a dash but no recognizable root session. Pass a root session name (e.g., "exe1", "work", "yoda1")`
7220
7255
  };
7221
7256
  }
7222
7257
  }
@@ -7481,7 +7516,7 @@ var init_tmux_routing = __esm({
7481
7516
  SPAWN_LOCK_DIR = path17.join(os7.homedir(), ".exe-os", "spawn-locks");
7482
7517
  SESSION_CACHE = path17.join(os7.homedir(), ".exe-os", "session-cache");
7483
7518
  BEHAVIORS_EXPORT_TIMEOUT_MS = 1e4;
7484
- VALID_SESSION_NAME = /^[a-z]+-exe\d+$|^[a-z]+\d+-exe\d+$/;
7519
+ VALID_SESSION_NAME = /^[a-z]+\d*-[a-zA-Z0-9_]+$/;
7485
7520
  VERIFY_PANE_LINES = 200;
7486
7521
  INTERCOM_DEBOUNCE_MS = 3e4;
7487
7522
  INTERCOM_LOG2 = path17.join(os7.homedir(), ".exe-os", "intercom.log");
@@ -1370,8 +1370,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
1370
1370
  -----END PUBLIC KEY-----`;
1371
1371
  var LICENSE_JWT_ALG = "ES256";
1372
1372
  var PLAN_LIMITS = {
1373
- free: { devices: 1, employees: 1, memories: 5e3 },
1374
- pro: { devices: 2, employees: 5, memories: 1e5 },
1373
+ free: { devices: 1, employees: 1, memories: 5e4 },
1374
+ pro: { devices: 2, employees: 5, memories: 25e4 },
1375
1375
  team: { devices: 10, employees: 20, memories: 1e6 },
1376
1376
  agency: { devices: 50, employees: 100, memories: 1e7 },
1377
1377
  enterprise: { devices: -1, employees: -1, memories: -1 }
@@ -1383,7 +1383,7 @@ var FREE_LICENSE = {
1383
1383
  expiresAt: null,
1384
1384
  deviceLimit: 1,
1385
1385
  employeeLimit: 1,
1386
- memoryLimit: 5e3
1386
+ memoryLimit: 5e4
1387
1387
  };
1388
1388
  function loadDeviceId() {
1389
1389
  const deviceJsonPath = path3.join(EXE_AI_DIR, "device.json");
@@ -1450,6 +1450,41 @@ async function getCachedLicense() {
1450
1450
  return null;
1451
1451
  }
1452
1452
  }
1453
+ function readCachedToken() {
1454
+ try {
1455
+ if (!existsSync3(CACHE_PATH)) return null;
1456
+ const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
1457
+ return typeof raw.token === "string" ? raw.token : null;
1458
+ } catch {
1459
+ return null;
1460
+ }
1461
+ }
1462
+ function getRawCachedPlan() {
1463
+ try {
1464
+ const token = readCachedToken();
1465
+ if (!token) return null;
1466
+ const parts = token.split(".");
1467
+ if (parts.length !== 3) return null;
1468
+ const payload = JSON.parse(Buffer.from(parts[1], "base64url").toString());
1469
+ const plan = payload.plan ?? "free";
1470
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
1471
+ process.stderr.write(
1472
+ `[license] WARN: using unverified cached plan (API unreachable, JWT expired). Plan: ${plan}
1473
+ `
1474
+ );
1475
+ return {
1476
+ valid: true,
1477
+ plan,
1478
+ email: payload.sub ?? "",
1479
+ expiresAt: payload.exp ? new Date(payload.exp * 1e3).toISOString() : null,
1480
+ deviceLimit: limits.devices,
1481
+ employeeLimit: limits.employees,
1482
+ memoryLimit: limits.memories
1483
+ };
1484
+ } catch {
1485
+ return null;
1486
+ }
1487
+ }
1453
1488
  function cacheResponse(token) {
1454
1489
  try {
1455
1490
  writeFileSync(CACHE_PATH, JSON.stringify({ token }), "utf8");
@@ -1470,6 +1505,8 @@ async function validateLicense(apiKey, deviceId) {
1470
1505
  if (data.error === "device_limit_exceeded") {
1471
1506
  const cached2 = await getCachedLicense();
1472
1507
  if (cached2) return cached2;
1508
+ const raw2 = getRawCachedPlan();
1509
+ if (raw2) return { ...raw2, valid: false };
1473
1510
  return { ...FREE_LICENSE, valid: false, plan: "free" };
1474
1511
  }
1475
1512
  if (data.token) {
@@ -1490,10 +1527,14 @@ async function validateLicense(apiKey, deviceId) {
1490
1527
  }
1491
1528
  const cached = await getCachedLicense();
1492
1529
  if (cached) return cached;
1530
+ const raw = getRawCachedPlan();
1531
+ if (raw) return raw;
1493
1532
  return { ...FREE_LICENSE, valid: false, plan: "free" };
1494
1533
  } catch {
1495
1534
  const cached = await getCachedLicense();
1496
1535
  if (cached) return cached;
1536
+ const rawFallback = getRawCachedPlan();
1537
+ if (rawFallback) return rawFallback;
1497
1538
  return { ...FREE_LICENSE, valid: false, error: "offline" };
1498
1539
  }
1499
1540
  }