@askexenow/exe-os 0.9.7 → 0.9.9

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 (101) hide show
  1. package/dist/bin/backfill-conversations.js +953 -105
  2. package/dist/bin/backfill-responses.js +952 -104
  3. package/dist/bin/backfill-vectors.js +956 -108
  4. package/dist/bin/cleanup-stale-review-tasks.js +802 -58
  5. package/dist/bin/cli.js +2292 -1070
  6. package/dist/bin/exe-agent-config.js +157 -101
  7. package/dist/bin/exe-agent.js +55 -29
  8. package/dist/bin/exe-assign.js +940 -92
  9. package/dist/bin/exe-boot.js +1424 -442
  10. package/dist/bin/exe-call.js +240 -141
  11. package/dist/bin/exe-cloud.js +198 -70
  12. package/dist/bin/exe-dispatch.js +951 -192
  13. package/dist/bin/exe-doctor.js +791 -51
  14. package/dist/bin/exe-export-behaviors.js +790 -42
  15. package/dist/bin/exe-forget.js +771 -31
  16. package/dist/bin/exe-gateway.js +1592 -521
  17. package/dist/bin/exe-heartbeat.js +850 -109
  18. package/dist/bin/exe-kill.js +783 -35
  19. package/dist/bin/exe-launch-agent.js +1030 -107
  20. package/dist/bin/exe-link.js +916 -110
  21. package/dist/bin/exe-new-employee.js +526 -217
  22. package/dist/bin/exe-pending-messages.js +1046 -62
  23. package/dist/bin/exe-pending-notifications.js +1318 -111
  24. package/dist/bin/exe-pending-reviews.js +1040 -72
  25. package/dist/bin/exe-rename.js +772 -59
  26. package/dist/bin/exe-review.js +772 -32
  27. package/dist/bin/exe-search.js +982 -128
  28. package/dist/bin/exe-session-cleanup.js +1180 -306
  29. package/dist/bin/exe-settings.js +185 -105
  30. package/dist/bin/exe-start-codex.js +886 -132
  31. package/dist/bin/exe-start-opencode.js +873 -119
  32. package/dist/bin/exe-status.js +803 -59
  33. package/dist/bin/exe-team.js +772 -32
  34. package/dist/bin/git-sweep.js +1046 -223
  35. package/dist/bin/graph-backfill.js +779 -31
  36. package/dist/bin/graph-export.js +785 -37
  37. package/dist/bin/install.js +632 -200
  38. package/dist/bin/scan-tasks.js +1055 -232
  39. package/dist/bin/setup.js +1419 -320
  40. package/dist/bin/shard-migrate.js +783 -35
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +782 -34
  43. package/dist/gateway/index.js +1444 -449
  44. package/dist/hooks/bug-report-worker.js +1141 -269
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +1044 -221
  47. package/dist/hooks/error-recall.js +989 -135
  48. package/dist/hooks/exe-heartbeat-hook.js +99 -75
  49. package/dist/hooks/ingest-worker.js +4176 -3226
  50. package/dist/hooks/ingest.js +920 -168
  51. package/dist/hooks/instructions-loaded.js +874 -70
  52. package/dist/hooks/notification.js +860 -56
  53. package/dist/hooks/post-compact.js +881 -73
  54. package/dist/hooks/pre-compact.js +1050 -227
  55. package/dist/hooks/pre-tool-use.js +1084 -159
  56. package/dist/hooks/prompt-ingest-worker.js +1089 -164
  57. package/dist/hooks/prompt-submit.js +1469 -515
  58. package/dist/hooks/response-ingest-worker.js +1104 -179
  59. package/dist/hooks/session-end.js +1085 -251
  60. package/dist/hooks/session-start.js +1241 -231
  61. package/dist/hooks/stop.js +935 -109
  62. package/dist/hooks/subagent-stop.js +881 -73
  63. package/dist/hooks/summary-worker.js +1323 -307
  64. package/dist/index.js +1449 -452
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +909 -115
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +42 -9
  69. package/dist/lib/database.js +739 -33
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +2359 -0
  72. package/dist/lib/device-registry.js +760 -47
  73. package/dist/lib/embedder.js +201 -73
  74. package/dist/lib/employee-templates.js +30 -4
  75. package/dist/lib/employees.js +290 -86
  76. package/dist/lib/exe-daemon-client.js +187 -83
  77. package/dist/lib/exe-daemon.js +1696 -616
  78. package/dist/lib/hybrid-search.js +982 -128
  79. package/dist/lib/identity.js +43 -13
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +167 -80
  82. package/dist/lib/reminders.js +35 -5
  83. package/dist/lib/schedules.js +772 -32
  84. package/dist/lib/skill-learning.js +54 -7
  85. package/dist/lib/store.js +779 -31
  86. package/dist/lib/task-router.js +94 -73
  87. package/dist/lib/tasks.js +298 -225
  88. package/dist/lib/tmux-routing.js +246 -172
  89. package/dist/lib/token-spend.js +52 -14
  90. package/dist/mcp/server.js +2893 -850
  91. package/dist/mcp/tools/complete-reminder.js +35 -5
  92. package/dist/mcp/tools/create-reminder.js +35 -5
  93. package/dist/mcp/tools/create-task.js +507 -323
  94. package/dist/mcp/tools/deactivate-behavior.js +40 -10
  95. package/dist/mcp/tools/list-reminders.js +35 -5
  96. package/dist/mcp/tools/list-tasks.js +277 -104
  97. package/dist/mcp/tools/send-message.js +129 -56
  98. package/dist/mcp/tools/update-task.js +1864 -188
  99. package/dist/runtime/index.js +1083 -259
  100. package/dist/tui/App.js +1501 -434
  101. package/package.json +3 -2
@@ -3,9 +3,18 @@ var __esm = (fn, res) => function __init() {
3
3
  return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
4
4
  };
5
5
 
6
+ // src/lib/secure-files.ts
7
+ import { chmodSync, existsSync, mkdirSync } from "fs";
8
+ import { chmod, mkdir } from "fs/promises";
9
+ var init_secure_files = __esm({
10
+ "src/lib/secure-files.ts"() {
11
+ "use strict";
12
+ }
13
+ });
14
+
6
15
  // src/lib/config.ts
7
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
8
- import { readFileSync, existsSync, renameSync } from "fs";
16
+ import { readFile, writeFile } from "fs/promises";
17
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
9
18
  import path from "path";
10
19
  import os from "os";
11
20
  function resolveDataDir() {
@@ -13,7 +22,7 @@ function resolveDataDir() {
13
22
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
14
23
  const newDir = path.join(os.homedir(), ".exe-os");
15
24
  const legacyDir = path.join(os.homedir(), ".exe-mem");
16
- if (!existsSync(newDir) && existsSync(legacyDir)) {
25
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
17
26
  try {
18
27
  renameSync(legacyDir, newDir);
19
28
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -28,6 +37,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
28
37
  var init_config = __esm({
29
38
  "src/lib/config.ts"() {
30
39
  "use strict";
40
+ init_secure_files();
31
41
  EXE_AI_DIR = resolveDataDir();
32
42
  DB_PATH = path.join(EXE_AI_DIR, "memories.db");
33
43
  MODELS_DIR = path.join(EXE_AI_DIR, "models");
@@ -96,9 +106,9 @@ var init_config = __esm({
96
106
 
97
107
  // src/lib/identity.ts
98
108
  init_config();
99
- import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
109
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
100
110
  import { readdirSync } from "fs";
101
- import path3 from "path";
111
+ import path4 from "path";
102
112
  import { createHash } from "crypto";
103
113
 
104
114
  // src/lib/database.ts
@@ -107,19 +117,39 @@ import { createClient } from "@libsql/client";
107
117
  // src/lib/employees.ts
108
118
  init_config();
109
119
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
110
- import { existsSync as existsSync2, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
120
+ import { existsSync as existsSync3, symlinkSync, readlinkSync, readFileSync as readFileSync2, renameSync as renameSync2, unlinkSync, writeFileSync } from "fs";
111
121
  import { execSync } from "child_process";
112
122
  import path2 from "path";
113
123
  import os2 from "os";
114
124
  var EMPLOYEES_PATH = path2.join(EXE_AI_DIR, "exe-employees.json");
125
+ var IDENTITY_DIR = path2.join(EXE_AI_DIR, "identity");
126
+
127
+ // src/lib/database-adapter.ts
128
+ import os3 from "os";
129
+ import path3 from "path";
130
+ import { createRequire } from "module";
131
+ import { pathToFileURL } from "url";
132
+ var BOOLEAN_COLUMNS_BY_TABLE = {
133
+ memories: /* @__PURE__ */ new Set(["has_error", "draft"]),
134
+ behaviors: /* @__PURE__ */ new Set(["active"]),
135
+ notifications: /* @__PURE__ */ new Set(["read"]),
136
+ users: /* @__PURE__ */ new Set(["has_personal_memory"])
137
+ };
138
+ var BOOLEAN_COLUMN_NAMES = new Set(
139
+ Object.values(BOOLEAN_COLUMNS_BY_TABLE).flatMap((cols) => [...cols])
140
+ );
115
141
 
116
142
  // src/lib/database.ts
117
143
  var _resilientClient = null;
118
144
  var _daemonClient = null;
145
+ var _adapterClient = null;
119
146
  function getClient() {
120
- if (!_resilientClient) {
147
+ if (!_adapterClient) {
121
148
  throw new Error("Database client not initialized. Call initDatabase() first.");
122
149
  }
150
+ if (process.env.DATABASE_URL) {
151
+ return _adapterClient;
152
+ }
123
153
  if (process.env.EXE_IS_DAEMON === "1") {
124
154
  return _resilientClient;
125
155
  }
@@ -130,14 +160,14 @@ function getClient() {
130
160
  }
131
161
 
132
162
  // src/lib/identity.ts
133
- var IDENTITY_DIR = path3.join(EXE_AI_DIR, "identity");
163
+ var IDENTITY_DIR2 = path4.join(EXE_AI_DIR, "identity");
134
164
  function ensureDir() {
135
- if (!existsSync3(IDENTITY_DIR)) {
136
- mkdirSync(IDENTITY_DIR, { recursive: true });
165
+ if (!existsSync4(IDENTITY_DIR2)) {
166
+ mkdirSync2(IDENTITY_DIR2, { recursive: true });
137
167
  }
138
168
  }
139
169
  function identityPath(agentId) {
140
- return path3.join(IDENTITY_DIR, `${agentId}.md`);
170
+ return path4.join(IDENTITY_DIR2, `${agentId}.md`);
141
171
  }
142
172
  function parseFrontmatter(raw) {
143
173
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -178,7 +208,7 @@ function contentHash(content) {
178
208
  }
179
209
  function getIdentity(agentId) {
180
210
  const filePath = identityPath(agentId);
181
- if (!existsSync3(filePath)) return null;
211
+ if (!existsSync4(filePath)) return null;
182
212
  const raw = readFileSync3(filePath, "utf-8");
183
213
  const { frontmatter, body } = parseFrontmatter(raw);
184
214
  return {
@@ -210,7 +240,7 @@ async function updateIdentity(agentId, content, updatedBy) {
210
240
  }
211
241
  function listIdentities() {
212
242
  ensureDir();
213
- const files = readdirSync(IDENTITY_DIR).filter((f) => f.endsWith(".md"));
243
+ const files = readdirSync(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
214
244
  const results = [];
215
245
  for (const file of files) {
216
246
  const agentId = file.replace(".md", "");
@@ -6,22 +6,31 @@ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require
6
6
  });
7
7
 
8
8
  // src/lib/license.ts
9
- import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
9
+ import { readFileSync as readFileSync2, writeFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
10
10
  import { randomUUID } from "crypto";
11
+ import { createRequire } from "module";
12
+ import { pathToFileURL } from "url";
13
+ import os2 from "os";
11
14
  import path2 from "path";
12
15
  import { jwtVerify, importSPKI } from "jose";
13
16
 
14
17
  // src/lib/config.ts
15
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
16
- import { readFileSync, existsSync, renameSync } from "fs";
18
+ import { readFile, writeFile } from "fs/promises";
19
+ import { readFileSync, existsSync as existsSync2, renameSync } from "fs";
17
20
  import path from "path";
18
21
  import os from "os";
22
+
23
+ // src/lib/secure-files.ts
24
+ import { chmodSync, existsSync, mkdirSync } from "fs";
25
+ import { chmod, mkdir } from "fs/promises";
26
+
27
+ // src/lib/config.ts
19
28
  function resolveDataDir() {
20
29
  if (process.env.EXE_OS_DIR) return process.env.EXE_OS_DIR;
21
30
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
22
31
  const newDir = path.join(os.homedir(), ".exe-os");
23
32
  const legacyDir = path.join(os.homedir(), ".exe-mem");
24
- if (!existsSync(newDir) && existsSync(legacyDir)) {
33
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
25
34
  try {
26
35
  renameSync(legacyDir, newDir);
27
36
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -134,34 +143,34 @@ var FREE_LICENSE = {
134
143
  function loadDeviceId() {
135
144
  const deviceJsonPath = path2.join(EXE_AI_DIR, "device.json");
136
145
  try {
137
- if (existsSync2(deviceJsonPath)) {
146
+ if (existsSync3(deviceJsonPath)) {
138
147
  const data = JSON.parse(readFileSync2(deviceJsonPath, "utf8"));
139
148
  if (data.deviceId) return data.deviceId;
140
149
  }
141
150
  } catch {
142
151
  }
143
152
  try {
144
- if (existsSync2(DEVICE_ID_PATH)) {
153
+ if (existsSync3(DEVICE_ID_PATH)) {
145
154
  const id2 = readFileSync2(DEVICE_ID_PATH, "utf8").trim();
146
155
  if (id2) return id2;
147
156
  }
148
157
  } catch {
149
158
  }
150
159
  const id = randomUUID();
151
- mkdirSync(EXE_AI_DIR, { recursive: true });
160
+ mkdirSync2(EXE_AI_DIR, { recursive: true });
152
161
  writeFileSync(DEVICE_ID_PATH, id, "utf8");
153
162
  return id;
154
163
  }
155
164
  function loadLicense() {
156
165
  try {
157
- if (!existsSync2(LICENSE_PATH)) return null;
166
+ if (!existsSync3(LICENSE_PATH)) return null;
158
167
  return readFileSync2(LICENSE_PATH, "utf8").trim();
159
168
  } catch {
160
169
  return null;
161
170
  }
162
171
  }
163
172
  function saveLicense(apiKey) {
164
- mkdirSync(EXE_AI_DIR, { recursive: true });
173
+ mkdirSync2(EXE_AI_DIR, { recursive: true });
165
174
  writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
166
175
  }
167
176
  async function verifyLicenseJwt(token) {
@@ -188,7 +197,7 @@ async function verifyLicenseJwt(token) {
188
197
  }
189
198
  async function getCachedLicense() {
190
199
  try {
191
- if (!existsSync2(CACHE_PATH)) return null;
200
+ if (!existsSync3(CACHE_PATH)) return null;
192
201
  const raw = JSON.parse(readFileSync2(CACHE_PATH, "utf8"));
193
202
  if (!raw.token || typeof raw.token !== "string") return null;
194
203
  return await verifyLicenseJwt(raw.token);
@@ -198,7 +207,7 @@ async function getCachedLicense() {
198
207
  }
199
208
  function readCachedToken() {
200
209
  try {
201
- if (!existsSync2(CACHE_PATH)) return null;
210
+ if (!existsSync3(CACHE_PATH)) return null;
202
211
  const raw = JSON.parse(readFileSync2(CACHE_PATH, "utf8"));
203
212
  return typeof raw.token === "string" ? raw.token : null;
204
213
  } catch {
@@ -237,52 +246,128 @@ function cacheResponse(token) {
237
246
  } catch {
238
247
  }
239
248
  }
240
- async function validateLicense(apiKey, deviceId) {
241
- const did = deviceId ?? loadDeviceId();
249
+ var _prismaPromise = null;
250
+ var _prismaFailed = false;
251
+ function loadPrismaForLicense() {
252
+ if (_prismaFailed) return null;
253
+ const dbUrl = process.env.DATABASE_URL;
254
+ if (!dbUrl) {
255
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path2.join(os2.homedir(), "exe-db");
256
+ if (!existsSync3(path2.join(exeDbRoot, "package.json"))) {
257
+ _prismaFailed = true;
258
+ return null;
259
+ }
260
+ }
261
+ if (!_prismaPromise) {
262
+ _prismaPromise = (async () => {
263
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
264
+ if (explicitPath) {
265
+ const mod2 = await import(pathToFileURL(explicitPath).href);
266
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
267
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
268
+ return new Ctor2();
269
+ }
270
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path2.join(os2.homedir(), "exe-db");
271
+ const req = createRequire(path2.join(exeDbRoot, "package.json"));
272
+ const entry = req.resolve("@prisma/client");
273
+ const mod = await import(pathToFileURL(entry).href);
274
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
275
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
276
+ return new Ctor();
277
+ })().catch((err) => {
278
+ _prismaFailed = true;
279
+ _prismaPromise = null;
280
+ throw err;
281
+ });
282
+ }
283
+ return _prismaPromise;
284
+ }
285
+ async function validateViaPostgres(apiKey) {
286
+ const loader = loadPrismaForLicense();
287
+ if (!loader) return null;
288
+ try {
289
+ const prisma = await loader;
290
+ const rows = await prisma.$queryRawUnsafe(
291
+ `SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
292
+ FROM billing.licenses WHERE key = $1 LIMIT 1`,
293
+ apiKey
294
+ );
295
+ if (!rows || rows.length === 0) return null;
296
+ const row = rows[0];
297
+ if (row.status !== "active") return null;
298
+ if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
299
+ const plan = row.plan;
300
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
301
+ return {
302
+ valid: true,
303
+ plan,
304
+ email: row.email,
305
+ expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
306
+ deviceLimit: row.device_limit ?? limits.devices,
307
+ employeeLimit: row.employee_limit ?? limits.employees,
308
+ memoryLimit: row.memory_limit ?? limits.memories
309
+ };
310
+ } catch {
311
+ return null;
312
+ }
313
+ }
314
+ async function validateViaCFWorker(apiKey, deviceId) {
242
315
  try {
243
316
  const res = await fetchRetry(`${API_BASE}/auth/activate`, {
244
317
  method: "POST",
245
318
  headers: { "Content-Type": "application/json" },
246
- body: JSON.stringify({ apiKey, deviceId: did }),
319
+ body: JSON.stringify({ apiKey, deviceId }),
247
320
  signal: AbortSignal.timeout(1e4)
248
321
  });
249
- if (res.ok) {
250
- const data = await res.json();
251
- if (data.error === "device_limit_exceeded") {
252
- const cached2 = await getCachedLicense();
253
- if (cached2) return cached2;
254
- const raw2 = getRawCachedPlan();
255
- if (raw2) return { ...raw2, valid: false };
256
- return { ...FREE_LICENSE, valid: false, plan: "free" };
257
- }
258
- if (data.token) {
259
- cacheResponse(data.token);
260
- const verified = await verifyLicenseJwt(data.token);
261
- if (verified) return verified;
322
+ if (!res.ok) return null;
323
+ const data = await res.json();
324
+ if (data.error === "device_limit_exceeded") return null;
325
+ if (!data.valid) return null;
326
+ if (data.token) {
327
+ cacheResponse(data.token);
328
+ const verified = await verifyLicenseJwt(data.token);
329
+ if (verified) return verified;
330
+ }
331
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
332
+ return {
333
+ valid: data.valid,
334
+ plan: data.plan,
335
+ email: data.email,
336
+ expiresAt: data.expiresAt,
337
+ deviceLimit: limits.devices,
338
+ employeeLimit: limits.employees,
339
+ memoryLimit: limits.memories
340
+ };
341
+ } catch {
342
+ return null;
343
+ }
344
+ }
345
+ async function validateLicense(apiKey, deviceId) {
346
+ const did = deviceId ?? loadDeviceId();
347
+ const pgResult = await validateViaPostgres(apiKey);
348
+ if (pgResult) {
349
+ try {
350
+ writeFileSync(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
351
+ } catch {
352
+ }
353
+ return pgResult;
354
+ }
355
+ const cfResult = await validateViaCFWorker(apiKey, did);
356
+ if (cfResult) return cfResult;
357
+ const cached = await getCachedLicense();
358
+ if (cached) return cached;
359
+ try {
360
+ if (existsSync3(CACHE_PATH)) {
361
+ const raw = JSON.parse(readFileSync2(CACHE_PATH, "utf8"));
362
+ if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
363
+ return raw.pgLicense;
262
364
  }
263
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
264
- return {
265
- valid: data.valid,
266
- plan: data.plan,
267
- email: data.email,
268
- expiresAt: data.expiresAt,
269
- deviceLimit: limits.devices,
270
- employeeLimit: limits.employees,
271
- memoryLimit: limits.memories
272
- };
273
365
  }
274
- const cached = await getCachedLicense();
275
- if (cached) return cached;
276
- const raw = getRawCachedPlan();
277
- if (raw) return raw;
278
- return { ...FREE_LICENSE, valid: false, plan: "free" };
279
366
  } catch {
280
- const cached = await getCachedLicense();
281
- if (cached) return cached;
282
- const rawFallback = getRawCachedPlan();
283
- if (rawFallback) return rawFallback;
284
- return { ...FREE_LICENSE, valid: false, error: "offline" };
285
367
  }
368
+ const rawFallback = getRawCachedPlan();
369
+ if (rawFallback) return rawFallback;
370
+ return { ...FREE_LICENSE, valid: false };
286
371
  }
287
372
  var CACHE_MAX_AGE_MS = 36e5;
288
373
  function getCacheAgeMs() {
@@ -299,7 +384,7 @@ async function checkLicense() {
299
384
  if (!key) {
300
385
  try {
301
386
  const configPath = path2.join(EXE_AI_DIR, "config.json");
302
- if (existsSync2(configPath)) {
387
+ if (existsSync3(configPath)) {
303
388
  const raw = JSON.parse(readFileSync2(configPath, "utf8"));
304
389
  const cloud = raw.cloud;
305
390
  if (cloud?.apiKey) {