@askexenow/exe-os 0.9.8 → 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 +222 -49
  2. package/dist/bin/backfill-responses.js +221 -48
  3. package/dist/bin/backfill-vectors.js +225 -52
  4. package/dist/bin/cleanup-stale-review-tasks.js +150 -28
  5. package/dist/bin/cli.js +1295 -856
  6. package/dist/bin/exe-agent-config.js +36 -8
  7. package/dist/bin/exe-agent.js +14 -4
  8. package/dist/bin/exe-assign.js +221 -48
  9. package/dist/bin/exe-boot.js +778 -427
  10. package/dist/bin/exe-call.js +41 -13
  11. package/dist/bin/exe-cloud.js +163 -58
  12. package/dist/bin/exe-dispatch.js +276 -139
  13. package/dist/bin/exe-doctor.js +145 -27
  14. package/dist/bin/exe-export-behaviors.js +141 -23
  15. package/dist/bin/exe-forget.js +137 -19
  16. package/dist/bin/exe-gateway.js +677 -388
  17. package/dist/bin/exe-heartbeat.js +227 -108
  18. package/dist/bin/exe-kill.js +138 -20
  19. package/dist/bin/exe-launch-agent.js +172 -39
  20. package/dist/bin/exe-link.js +291 -100
  21. package/dist/bin/exe-new-employee.js +214 -106
  22. package/dist/bin/exe-pending-messages.js +395 -33
  23. package/dist/bin/exe-pending-notifications.js +684 -99
  24. package/dist/bin/exe-pending-reviews.js +420 -74
  25. package/dist/bin/exe-rename.js +147 -49
  26. package/dist/bin/exe-review.js +138 -20
  27. package/dist/bin/exe-search.js +240 -69
  28. package/dist/bin/exe-session-cleanup.js +440 -250
  29. package/dist/bin/exe-settings.js +61 -17
  30. package/dist/bin/exe-start-codex.js +158 -39
  31. package/dist/bin/exe-start-opencode.js +157 -38
  32. package/dist/bin/exe-status.js +151 -29
  33. package/dist/bin/exe-team.js +138 -20
  34. package/dist/bin/git-sweep.js +404 -212
  35. package/dist/bin/graph-backfill.js +137 -19
  36. package/dist/bin/graph-export.js +140 -22
  37. package/dist/bin/install.js +90 -61
  38. package/dist/bin/scan-tasks.js +412 -220
  39. package/dist/bin/setup.js +564 -293
  40. package/dist/bin/shard-migrate.js +139 -21
  41. package/dist/bin/update.js +138 -49
  42. package/dist/bin/wiki-sync.js +137 -19
  43. package/dist/gateway/index.js +533 -320
  44. package/dist/hooks/bug-report-worker.js +344 -193
  45. package/dist/hooks/codex-stop-task-finalizer.js +4678 -0
  46. package/dist/hooks/commit-complete.js +402 -210
  47. package/dist/hooks/error-recall.js +245 -74
  48. package/dist/hooks/exe-heartbeat-hook.js +16 -6
  49. package/dist/hooks/ingest-worker.js +3423 -3157
  50. package/dist/hooks/ingest.js +832 -97
  51. package/dist/hooks/instructions-loaded.js +227 -54
  52. package/dist/hooks/notification.js +216 -43
  53. package/dist/hooks/post-compact.js +239 -62
  54. package/dist/hooks/pre-compact.js +408 -216
  55. package/dist/hooks/pre-tool-use.js +268 -90
  56. package/dist/hooks/prompt-ingest-worker.js +352 -102
  57. package/dist/hooks/prompt-submit.js +541 -328
  58. package/dist/hooks/response-ingest-worker.js +372 -122
  59. package/dist/hooks/session-end.js +443 -240
  60. package/dist/hooks/session-start.js +313 -127
  61. package/dist/hooks/stop.js +293 -98
  62. package/dist/hooks/subagent-stop.js +239 -62
  63. package/dist/hooks/summary-worker.js +568 -236
  64. package/dist/index.js +538 -324
  65. package/dist/lib/agent-config.js +28 -6
  66. package/dist/lib/cloud-sync.js +284 -105
  67. package/dist/lib/config.js +30 -10
  68. package/dist/lib/consolidation.js +16 -6
  69. package/dist/lib/database.js +123 -25
  70. package/dist/lib/db-daemon-client.js +73 -19
  71. package/dist/lib/db.js +123 -25
  72. package/dist/lib/device-registry.js +133 -35
  73. package/dist/lib/embedder.js +107 -32
  74. package/dist/lib/employee-templates.js +14 -4
  75. package/dist/lib/employees.js +41 -13
  76. package/dist/lib/exe-daemon-client.js +88 -22
  77. package/dist/lib/exe-daemon.js +935 -587
  78. package/dist/lib/hybrid-search.js +240 -69
  79. package/dist/lib/identity.js +18 -8
  80. package/dist/lib/license.js +133 -48
  81. package/dist/lib/messaging.js +116 -56
  82. package/dist/lib/reminders.js +14 -4
  83. package/dist/lib/schedules.js +137 -19
  84. package/dist/lib/skill-learning.js +33 -6
  85. package/dist/lib/store.js +137 -19
  86. package/dist/lib/task-router.js +14 -4
  87. package/dist/lib/tasks.js +280 -234
  88. package/dist/lib/tmux-routing.js +172 -125
  89. package/dist/lib/token-spend.js +26 -8
  90. package/dist/mcp/server.js +1326 -609
  91. package/dist/mcp/tools/complete-reminder.js +14 -4
  92. package/dist/mcp/tools/create-reminder.js +14 -4
  93. package/dist/mcp/tools/create-task.js +306 -248
  94. package/dist/mcp/tools/deactivate-behavior.js +16 -6
  95. package/dist/mcp/tools/list-reminders.js +14 -4
  96. package/dist/mcp/tools/list-tasks.js +123 -107
  97. package/dist/mcp/tools/send-message.js +75 -29
  98. package/dist/mcp/tools/update-task.js +1848 -199
  99. package/dist/runtime/index.js +441 -248
  100. package/dist/tui/App.js +761 -424
  101. package/package.json +1 -1
@@ -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,7 +106,7 @@ 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
111
  import path4 from "path";
102
112
  import { createHash } from "crypto";
@@ -107,7 +117,7 @@ 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";
@@ -152,8 +162,8 @@ function getClient() {
152
162
  // src/lib/identity.ts
153
163
  var IDENTITY_DIR2 = path4.join(EXE_AI_DIR, "identity");
154
164
  function ensureDir() {
155
- if (!existsSync3(IDENTITY_DIR2)) {
156
- mkdirSync(IDENTITY_DIR2, { recursive: true });
165
+ if (!existsSync4(IDENTITY_DIR2)) {
166
+ mkdirSync2(IDENTITY_DIR2, { recursive: true });
157
167
  }
158
168
  }
159
169
  function identityPath(agentId) {
@@ -198,7 +208,7 @@ function contentHash(content) {
198
208
  }
199
209
  function getIdentity(agentId) {
200
210
  const filePath = identityPath(agentId);
201
- if (!existsSync3(filePath)) return null;
211
+ if (!existsSync4(filePath)) return null;
202
212
  const raw = readFileSync3(filePath, "utf-8");
203
213
  const { frontmatter, body } = parseFrontmatter(raw);
204
214
  return {
@@ -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) {