@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
@@ -15,9 +15,18 @@ var __export = (target, all) => {
15
15
  __defProp(target, name, { get: all[name], enumerable: true });
16
16
  };
17
17
 
18
+ // src/lib/secure-files.ts
19
+ import { chmodSync, existsSync, mkdirSync } from "fs";
20
+ import { chmod, mkdir } from "fs/promises";
21
+ var init_secure_files = __esm({
22
+ "src/lib/secure-files.ts"() {
23
+ "use strict";
24
+ }
25
+ });
26
+
18
27
  // src/lib/config.ts
19
- import { readFile, writeFile, mkdir, chmod } from "fs/promises";
20
- import { readFileSync as readFileSync2, existsSync, renameSync } from "fs";
28
+ import { readFile, writeFile } from "fs/promises";
29
+ import { readFileSync as readFileSync2, existsSync as existsSync2, renameSync } from "fs";
21
30
  import path2 from "path";
22
31
  import os from "os";
23
32
  function resolveDataDir() {
@@ -25,7 +34,7 @@ function resolveDataDir() {
25
34
  if (process.env.EXE_MEM_DIR) return process.env.EXE_MEM_DIR;
26
35
  const newDir = path2.join(os.homedir(), ".exe-os");
27
36
  const legacyDir = path2.join(os.homedir(), ".exe-mem");
28
- if (!existsSync(newDir) && existsSync(legacyDir)) {
37
+ if (!existsSync2(newDir) && existsSync2(legacyDir)) {
29
38
  try {
30
39
  renameSync(legacyDir, newDir);
31
40
  process.stderr.write(`[exe-os] Migrated data directory: ~/.exe-mem \u2192 ~/.exe-os
@@ -40,6 +49,7 @@ var EXE_AI_DIR, DB_PATH, MODELS_DIR, CONFIG_PATH, LEGACY_LANCE_PATH, CURRENT_CON
40
49
  var init_config = __esm({
41
50
  "src/lib/config.ts"() {
42
51
  "use strict";
52
+ init_secure_files();
43
53
  EXE_AI_DIR = resolveDataDir();
44
54
  DB_PATH = path2.join(EXE_AI_DIR, "memories.db");
45
55
  MODELS_DIR = path2.join(EXE_AI_DIR, "models");
@@ -123,8 +133,11 @@ __export(license_exports, {
123
133
  stopLicenseRevalidation: () => stopLicenseRevalidation,
124
134
  validateLicense: () => validateLicense
125
135
  });
126
- import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync2, mkdirSync } from "fs";
136
+ import { readFileSync as readFileSync3, writeFileSync, existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
127
137
  import { randomUUID } from "crypto";
138
+ import { createRequire } from "module";
139
+ import { pathToFileURL } from "url";
140
+ import os2 from "os";
128
141
  import path3 from "path";
129
142
  import { jwtVerify, importSPKI } from "jose";
130
143
  async function fetchRetry(url, init) {
@@ -138,34 +151,34 @@ async function fetchRetry(url, init) {
138
151
  function loadDeviceId() {
139
152
  const deviceJsonPath = path3.join(EXE_AI_DIR, "device.json");
140
153
  try {
141
- if (existsSync2(deviceJsonPath)) {
154
+ if (existsSync3(deviceJsonPath)) {
142
155
  const data = JSON.parse(readFileSync3(deviceJsonPath, "utf8"));
143
156
  if (data.deviceId) return data.deviceId;
144
157
  }
145
158
  } catch {
146
159
  }
147
160
  try {
148
- if (existsSync2(DEVICE_ID_PATH)) {
161
+ if (existsSync3(DEVICE_ID_PATH)) {
149
162
  const id2 = readFileSync3(DEVICE_ID_PATH, "utf8").trim();
150
163
  if (id2) return id2;
151
164
  }
152
165
  } catch {
153
166
  }
154
167
  const id = randomUUID();
155
- mkdirSync(EXE_AI_DIR, { recursive: true });
168
+ mkdirSync2(EXE_AI_DIR, { recursive: true });
156
169
  writeFileSync(DEVICE_ID_PATH, id, "utf8");
157
170
  return id;
158
171
  }
159
172
  function loadLicense() {
160
173
  try {
161
- if (!existsSync2(LICENSE_PATH)) return null;
174
+ if (!existsSync3(LICENSE_PATH)) return null;
162
175
  return readFileSync3(LICENSE_PATH, "utf8").trim();
163
176
  } catch {
164
177
  return null;
165
178
  }
166
179
  }
167
180
  function saveLicense(apiKey) {
168
- mkdirSync(EXE_AI_DIR, { recursive: true });
181
+ mkdirSync2(EXE_AI_DIR, { recursive: true });
169
182
  writeFileSync(LICENSE_PATH, apiKey.trim(), { encoding: "utf8", mode: 384 });
170
183
  }
171
184
  async function verifyLicenseJwt(token) {
@@ -192,7 +205,7 @@ async function verifyLicenseJwt(token) {
192
205
  }
193
206
  async function getCachedLicense() {
194
207
  try {
195
- if (!existsSync2(CACHE_PATH)) return null;
208
+ if (!existsSync3(CACHE_PATH)) return null;
196
209
  const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
197
210
  if (!raw.token || typeof raw.token !== "string") return null;
198
211
  return await verifyLicenseJwt(raw.token);
@@ -202,7 +215,7 @@ async function getCachedLicense() {
202
215
  }
203
216
  function readCachedToken() {
204
217
  try {
205
- if (!existsSync2(CACHE_PATH)) return null;
218
+ if (!existsSync3(CACHE_PATH)) return null;
206
219
  const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
207
220
  return typeof raw.token === "string" ? raw.token : null;
208
221
  } catch {
@@ -241,52 +254,126 @@ function cacheResponse(token) {
241
254
  } catch {
242
255
  }
243
256
  }
244
- async function validateLicense(apiKey, deviceId) {
245
- const did = deviceId ?? loadDeviceId();
257
+ function loadPrismaForLicense() {
258
+ if (_prismaFailed) return null;
259
+ const dbUrl = process.env.DATABASE_URL;
260
+ if (!dbUrl) {
261
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os2.homedir(), "exe-db");
262
+ if (!existsSync3(path3.join(exeDbRoot, "package.json"))) {
263
+ _prismaFailed = true;
264
+ return null;
265
+ }
266
+ }
267
+ if (!_prismaPromise) {
268
+ _prismaPromise = (async () => {
269
+ const explicitPath = process.env.EXE_OS_PRISMA_CLIENT_PATH;
270
+ if (explicitPath) {
271
+ const mod2 = await import(pathToFileURL(explicitPath).href);
272
+ const Ctor2 = mod2.PrismaClient ?? mod2.default?.PrismaClient;
273
+ if (!Ctor2) throw new Error(`No PrismaClient at ${explicitPath}`);
274
+ return new Ctor2();
275
+ }
276
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path3.join(os2.homedir(), "exe-db");
277
+ const req = createRequire(path3.join(exeDbRoot, "package.json"));
278
+ const entry = req.resolve("@prisma/client");
279
+ const mod = await import(pathToFileURL(entry).href);
280
+ const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
281
+ if (!Ctor) throw new Error(`No PrismaClient in ${entry}`);
282
+ return new Ctor();
283
+ })().catch((err) => {
284
+ _prismaFailed = true;
285
+ _prismaPromise = null;
286
+ throw err;
287
+ });
288
+ }
289
+ return _prismaPromise;
290
+ }
291
+ async function validateViaPostgres(apiKey) {
292
+ const loader = loadPrismaForLicense();
293
+ if (!loader) return null;
294
+ try {
295
+ const prisma = await loader;
296
+ const rows = await prisma.$queryRawUnsafe(
297
+ `SELECT plan, email, status, device_limit, employee_limit, memory_limit, expires_at
298
+ FROM billing.licenses WHERE key = $1 LIMIT 1`,
299
+ apiKey
300
+ );
301
+ if (!rows || rows.length === 0) return null;
302
+ const row = rows[0];
303
+ if (row.status !== "active") return null;
304
+ if (row.expires_at && new Date(row.expires_at) < /* @__PURE__ */ new Date()) return null;
305
+ const plan = row.plan;
306
+ const limits = PLAN_LIMITS[plan] ?? PLAN_LIMITS.free;
307
+ return {
308
+ valid: true,
309
+ plan,
310
+ email: row.email,
311
+ expiresAt: row.expires_at ? new Date(row.expires_at).toISOString() : null,
312
+ deviceLimit: row.device_limit ?? limits.devices,
313
+ employeeLimit: row.employee_limit ?? limits.employees,
314
+ memoryLimit: row.memory_limit ?? limits.memories
315
+ };
316
+ } catch {
317
+ return null;
318
+ }
319
+ }
320
+ async function validateViaCFWorker(apiKey, deviceId) {
246
321
  try {
247
322
  const res = await fetchRetry(`${API_BASE}/auth/activate`, {
248
323
  method: "POST",
249
324
  headers: { "Content-Type": "application/json" },
250
- body: JSON.stringify({ apiKey, deviceId: did }),
325
+ body: JSON.stringify({ apiKey, deviceId }),
251
326
  signal: AbortSignal.timeout(1e4)
252
327
  });
253
- if (res.ok) {
254
- const data = await res.json();
255
- if (data.error === "device_limit_exceeded") {
256
- const cached2 = await getCachedLicense();
257
- if (cached2) return cached2;
258
- const raw2 = getRawCachedPlan();
259
- if (raw2) return { ...raw2, valid: false };
260
- return { ...FREE_LICENSE, valid: false, plan: "free" };
261
- }
262
- if (data.token) {
263
- cacheResponse(data.token);
264
- const verified = await verifyLicenseJwt(data.token);
265
- if (verified) return verified;
328
+ if (!res.ok) return null;
329
+ const data = await res.json();
330
+ if (data.error === "device_limit_exceeded") return null;
331
+ if (!data.valid) return null;
332
+ if (data.token) {
333
+ cacheResponse(data.token);
334
+ const verified = await verifyLicenseJwt(data.token);
335
+ if (verified) return verified;
336
+ }
337
+ const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
338
+ return {
339
+ valid: data.valid,
340
+ plan: data.plan,
341
+ email: data.email,
342
+ expiresAt: data.expiresAt,
343
+ deviceLimit: limits.devices,
344
+ employeeLimit: limits.employees,
345
+ memoryLimit: limits.memories
346
+ };
347
+ } catch {
348
+ return null;
349
+ }
350
+ }
351
+ async function validateLicense(apiKey, deviceId) {
352
+ const did = deviceId ?? loadDeviceId();
353
+ const pgResult = await validateViaPostgres(apiKey);
354
+ if (pgResult) {
355
+ try {
356
+ writeFileSync(CACHE_PATH, JSON.stringify({ pgLicense: pgResult, ts: Date.now() }), "utf8");
357
+ } catch {
358
+ }
359
+ return pgResult;
360
+ }
361
+ const cfResult = await validateViaCFWorker(apiKey, did);
362
+ if (cfResult) return cfResult;
363
+ const cached = await getCachedLicense();
364
+ if (cached) return cached;
365
+ try {
366
+ if (existsSync3(CACHE_PATH)) {
367
+ const raw = JSON.parse(readFileSync3(CACHE_PATH, "utf8"));
368
+ if (raw.pgLicense && raw.ts && Date.now() - raw.ts < 7 * 24 * 60 * 60 * 1e3) {
369
+ return raw.pgLicense;
266
370
  }
267
- const limits = PLAN_LIMITS[data.plan] ?? PLAN_LIMITS.free;
268
- return {
269
- valid: data.valid,
270
- plan: data.plan,
271
- email: data.email,
272
- expiresAt: data.expiresAt,
273
- deviceLimit: limits.devices,
274
- employeeLimit: limits.employees,
275
- memoryLimit: limits.memories
276
- };
277
371
  }
278
- const cached = await getCachedLicense();
279
- if (cached) return cached;
280
- const raw = getRawCachedPlan();
281
- if (raw) return raw;
282
- return { ...FREE_LICENSE, valid: false, plan: "free" };
283
372
  } catch {
284
- const cached = await getCachedLicense();
285
- if (cached) return cached;
286
- const rawFallback = getRawCachedPlan();
287
- if (rawFallback) return rawFallback;
288
- return { ...FREE_LICENSE, valid: false, error: "offline" };
289
373
  }
374
+ const rawFallback = getRawCachedPlan();
375
+ if (rawFallback) return rawFallback;
376
+ return { ...FREE_LICENSE, valid: false };
290
377
  }
291
378
  function getCacheAgeMs() {
292
379
  try {
@@ -302,7 +389,7 @@ async function checkLicense() {
302
389
  if (!key) {
303
390
  try {
304
391
  const configPath = path3.join(EXE_AI_DIR, "config.json");
305
- if (existsSync2(configPath)) {
392
+ if (existsSync3(configPath)) {
306
393
  const raw = JSON.parse(readFileSync3(configPath, "utf8"));
307
394
  const cloud = raw.cloud;
308
395
  if (cloud?.apiKey) {
@@ -457,7 +544,7 @@ function stopLicenseRevalidation() {
457
544
  _revalTimer = null;
458
545
  }
459
546
  }
460
- 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, _revalTimer;
547
+ var LICENSE_PATH, CACHE_PATH, DEVICE_ID_PATH, API_BASE, RETRY_DELAY_MS, LICENSE_PUBLIC_KEY_PEM, LICENSE_JWT_ALG, PLAN_LIMITS, FREE_LICENSE, _prismaPromise, _prismaFailed, CACHE_MAX_AGE_MS, _revalTimer;
461
548
  var init_license = __esm({
462
549
  "src/lib/license.ts"() {
463
550
  "use strict";
@@ -488,6 +575,8 @@ MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEeHztAMOpR/ZMh+rWuOASjEZ54CGY
488
575
  employeeLimit: 1,
489
576
  memoryLimit: 5e3
490
577
  };
578
+ _prismaPromise = null;
579
+ _prismaFailed = false;
491
580
  CACHE_MAX_AGE_MS = 36e5;
492
581
  _revalTimer = null;
493
582
  }