@askexenow/exe-os 0.9.30 → 0.9.32

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 (64) hide show
  1. package/dist/bin/backfill-conversations.js +135 -7
  2. package/dist/bin/backfill-responses.js +135 -7
  3. package/dist/bin/backfill-vectors.js +135 -7
  4. package/dist/bin/cleanup-stale-review-tasks.js +139 -11
  5. package/dist/bin/cli.js +812 -486
  6. package/dist/bin/exe-assign.js +135 -7
  7. package/dist/bin/exe-boot.js +422 -113
  8. package/dist/bin/exe-cloud.js +160 -9
  9. package/dist/bin/exe-dispatch.js +136 -8
  10. package/dist/bin/exe-doctor.js +255 -13
  11. package/dist/bin/exe-export-behaviors.js +136 -8
  12. package/dist/bin/exe-forget.js +136 -8
  13. package/dist/bin/exe-gateway.js +171 -24
  14. package/dist/bin/exe-heartbeat.js +141 -13
  15. package/dist/bin/exe-kill.js +140 -12
  16. package/dist/bin/exe-launch-agent.js +143 -15
  17. package/dist/bin/exe-link.js +357 -48
  18. package/dist/bin/exe-pending-messages.js +136 -8
  19. package/dist/bin/exe-pending-notifications.js +136 -8
  20. package/dist/bin/exe-pending-reviews.js +138 -10
  21. package/dist/bin/exe-review.js +136 -8
  22. package/dist/bin/exe-search.js +155 -20
  23. package/dist/bin/exe-session-cleanup.js +166 -38
  24. package/dist/bin/exe-start-codex.js +142 -14
  25. package/dist/bin/exe-start-opencode.js +140 -12
  26. package/dist/bin/exe-status.js +148 -20
  27. package/dist/bin/exe-team.js +136 -8
  28. package/dist/bin/git-sweep.js +138 -10
  29. package/dist/bin/graph-backfill.js +135 -7
  30. package/dist/bin/graph-export.js +136 -8
  31. package/dist/bin/intercom-check.js +153 -25
  32. package/dist/bin/scan-tasks.js +138 -10
  33. package/dist/bin/setup.js +447 -121
  34. package/dist/bin/shard-migrate.js +135 -7
  35. package/dist/gateway/index.js +151 -23
  36. package/dist/hooks/bug-report-worker.js +151 -23
  37. package/dist/hooks/codex-stop-task-finalizer.js +145 -17
  38. package/dist/hooks/commit-complete.js +138 -10
  39. package/dist/hooks/error-recall.js +159 -24
  40. package/dist/hooks/ingest.js +142 -14
  41. package/dist/hooks/instructions-loaded.js +136 -8
  42. package/dist/hooks/notification.js +136 -8
  43. package/dist/hooks/post-compact.js +136 -8
  44. package/dist/hooks/post-tool-combined.js +159 -24
  45. package/dist/hooks/pre-compact.js +136 -8
  46. package/dist/hooks/pre-tool-use.js +144 -16
  47. package/dist/hooks/prompt-submit.js +195 -55
  48. package/dist/hooks/session-end.js +141 -13
  49. package/dist/hooks/session-start.js +165 -30
  50. package/dist/hooks/stop.js +136 -8
  51. package/dist/hooks/subagent-stop.js +136 -8
  52. package/dist/hooks/summary-worker.js +374 -65
  53. package/dist/index.js +136 -8
  54. package/dist/lib/cloud-sync.js +355 -46
  55. package/dist/lib/consolidation.js +1 -0
  56. package/dist/lib/exe-daemon.js +469 -127
  57. package/dist/lib/hybrid-search.js +155 -20
  58. package/dist/lib/keychain.js +191 -7
  59. package/dist/lib/schedules.js +138 -10
  60. package/dist/lib/store.js +135 -7
  61. package/dist/mcp/server.js +706 -213
  62. package/dist/runtime/index.js +136 -8
  63. package/dist/tui/App.js +208 -31
  64. package/package.json +1 -1
package/dist/bin/setup.js CHANGED
@@ -304,6 +304,7 @@ __export(keychain_exports, {
304
304
  });
305
305
  import { readFile as readFile2, writeFile as writeFile2, unlink, mkdir as mkdir2, chmod as chmod2 } from "fs/promises";
306
306
  import { existsSync as existsSync3 } from "fs";
307
+ import { execSync } from "child_process";
307
308
  import path2 from "path";
308
309
  import os2 from "os";
309
310
  function getKeyDir() {
@@ -312,6 +313,83 @@ function getKeyDir() {
312
313
  function getKeyPath() {
313
314
  return path2.join(getKeyDir(), "master.key");
314
315
  }
316
+ function macKeychainGet() {
317
+ if (process.platform !== "darwin") return null;
318
+ try {
319
+ return execSync(
320
+ `security find-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w 2>/dev/null`,
321
+ { encoding: "utf-8", timeout: 5e3 }
322
+ ).trim();
323
+ } catch {
324
+ return null;
325
+ }
326
+ }
327
+ function macKeychainSet(value) {
328
+ if (process.platform !== "darwin") return false;
329
+ try {
330
+ try {
331
+ execSync(
332
+ `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
333
+ { timeout: 5e3 }
334
+ );
335
+ } catch {
336
+ }
337
+ execSync(
338
+ `security add-generic-password -s "${SERVICE}" -a "${ACCOUNT}" -w "${value}"`,
339
+ { timeout: 5e3 }
340
+ );
341
+ return true;
342
+ } catch {
343
+ return false;
344
+ }
345
+ }
346
+ function macKeychainDelete() {
347
+ if (process.platform !== "darwin") return false;
348
+ try {
349
+ execSync(
350
+ `security delete-generic-password -s "${SERVICE}" -a "${ACCOUNT}" 2>/dev/null`,
351
+ { timeout: 5e3 }
352
+ );
353
+ return true;
354
+ } catch {
355
+ return false;
356
+ }
357
+ }
358
+ function linuxSecretGet() {
359
+ if (process.platform !== "linux") return null;
360
+ try {
361
+ return execSync(
362
+ `secret-tool lookup service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
363
+ { encoding: "utf-8", timeout: 5e3 }
364
+ ).trim();
365
+ } catch {
366
+ return null;
367
+ }
368
+ }
369
+ function linuxSecretSet(value) {
370
+ if (process.platform !== "linux") return false;
371
+ try {
372
+ execSync(
373
+ `echo -n "${value}" | secret-tool store --label="exe-os master key" service "${SERVICE}" account "${ACCOUNT}"`,
374
+ { timeout: 5e3 }
375
+ );
376
+ return true;
377
+ } catch {
378
+ return false;
379
+ }
380
+ }
381
+ function linuxSecretDelete() {
382
+ if (process.platform !== "linux") return false;
383
+ try {
384
+ execSync(
385
+ `secret-tool clear service "${SERVICE}" account "${ACCOUNT}" 2>/dev/null`,
386
+ { timeout: 5e3 }
387
+ );
388
+ return true;
389
+ } catch {
390
+ return false;
391
+ }
392
+ }
315
393
  async function tryKeytar() {
316
394
  try {
317
395
  return await import("keytar");
@@ -319,13 +397,72 @@ async function tryKeytar() {
319
397
  return null;
320
398
  }
321
399
  }
400
+ function deriveMachineKey() {
401
+ try {
402
+ const crypto5 = __require("crypto");
403
+ const material = [
404
+ os2.hostname(),
405
+ os2.userInfo().username,
406
+ os2.arch(),
407
+ os2.platform(),
408
+ // Machine ID on Linux (stable across reboots)
409
+ process.platform === "linux" ? readMachineId() : ""
410
+ ].join("|");
411
+ return crypto5.createHash("sha256").update(material).digest();
412
+ } catch {
413
+ return null;
414
+ }
415
+ }
416
+ function readMachineId() {
417
+ try {
418
+ const { readFileSync: readFileSync13 } = __require("fs");
419
+ return readFileSync13("/etc/machine-id", "utf-8").trim();
420
+ } catch {
421
+ return "";
422
+ }
423
+ }
424
+ function encryptWithMachineKey(plaintext, machineKey) {
425
+ const crypto5 = __require("crypto");
426
+ const iv = crypto5.randomBytes(12);
427
+ const cipher = crypto5.createCipheriv("aes-256-gcm", machineKey, iv);
428
+ let encrypted = cipher.update(plaintext, "utf-8", "base64");
429
+ encrypted += cipher.final("base64");
430
+ const authTag = cipher.getAuthTag().toString("base64");
431
+ return `${ENCRYPTED_PREFIX}${iv.toString("base64")}:${authTag}:${encrypted}`;
432
+ }
433
+ function decryptWithMachineKey(encrypted, machineKey) {
434
+ if (!encrypted.startsWith(ENCRYPTED_PREFIX)) return null;
435
+ try {
436
+ const crypto5 = __require("crypto");
437
+ const parts = encrypted.slice(ENCRYPTED_PREFIX.length).split(":");
438
+ if (parts.length !== 3) return null;
439
+ const [ivB64, tagB64, cipherB64] = parts;
440
+ const iv = Buffer.from(ivB64, "base64");
441
+ const authTag = Buffer.from(tagB64, "base64");
442
+ const decipher = crypto5.createDecipheriv("aes-256-gcm", machineKey, iv);
443
+ decipher.setAuthTag(authTag);
444
+ let decrypted = decipher.update(cipherB64, "base64", "utf-8");
445
+ decrypted += decipher.final("utf-8");
446
+ return decrypted;
447
+ } catch {
448
+ return null;
449
+ }
450
+ }
322
451
  async function getMasterKey() {
452
+ const nativeValue = macKeychainGet() ?? linuxSecretGet();
453
+ if (nativeValue) {
454
+ return Buffer.from(nativeValue, "base64");
455
+ }
323
456
  const keytar = await tryKeytar();
324
457
  if (keytar) {
325
458
  try {
326
- const stored = await keytar.getPassword(SERVICE, ACCOUNT);
327
- if (stored) {
328
- return Buffer.from(stored, "base64");
459
+ const keytarValue = await keytar.getPassword(SERVICE, ACCOUNT);
460
+ if (keytarValue) {
461
+ const migrated = macKeychainSet(keytarValue) || linuxSecretSet(keytarValue);
462
+ if (migrated) {
463
+ process.stderr.write("[keychain] Migrated key from keytar to native keychain.\n");
464
+ }
465
+ return Buffer.from(keytarValue, "base64");
329
466
  }
330
467
  } catch {
331
468
  }
@@ -339,8 +476,31 @@ async function getMasterKey() {
339
476
  return null;
340
477
  }
341
478
  try {
342
- const content = await readFile2(keyPath, "utf-8");
343
- return Buffer.from(content.trim(), "base64");
479
+ const content = (await readFile2(keyPath, "utf-8")).trim();
480
+ let b64Value;
481
+ if (content.startsWith(ENCRYPTED_PREFIX)) {
482
+ const machineKey = deriveMachineKey();
483
+ if (!machineKey) {
484
+ process.stderr.write("[keychain] Cannot derive machine key to decrypt stored key.\n");
485
+ return null;
486
+ }
487
+ const decrypted = decryptWithMachineKey(content, machineKey);
488
+ if (!decrypted) {
489
+ process.stderr.write(
490
+ "[keychain] Key decryption failed \u2014 machine may have changed.\n Use your 24-word recovery phrase: exe-os link import\n"
491
+ );
492
+ return null;
493
+ }
494
+ b64Value = decrypted;
495
+ } else {
496
+ b64Value = content;
497
+ }
498
+ const key = Buffer.from(b64Value, "base64");
499
+ const migrated = macKeychainSet(b64Value) || linuxSecretSet(b64Value);
500
+ if (migrated) {
501
+ process.stderr.write("[keychain] Migrated key from file to native keychain.\n");
502
+ }
503
+ return key;
344
504
  } catch (err) {
345
505
  process.stderr.write(
346
506
  `[keychain] Key read failed at ${keyPath}: ${err instanceof Error ? err.message : String(err)}
@@ -351,6 +511,9 @@ async function getMasterKey() {
351
511
  }
352
512
  async function setMasterKey(key) {
353
513
  const b64 = key.toString("base64");
514
+ if (macKeychainSet(b64) || linuxSecretSet(b64)) {
515
+ return;
516
+ }
354
517
  const keytar = await tryKeytar();
355
518
  if (keytar) {
356
519
  try {
@@ -362,10 +525,23 @@ async function setMasterKey(key) {
362
525
  const dir = getKeyDir();
363
526
  await mkdir2(dir, { recursive: true });
364
527
  const keyPath = getKeyPath();
365
- await writeFile2(keyPath, b64 + "\n", "utf-8");
366
- await chmod2(keyPath, 384);
528
+ const machineKey = deriveMachineKey();
529
+ if (machineKey) {
530
+ const encrypted = encryptWithMachineKey(b64, machineKey);
531
+ await writeFile2(keyPath, encrypted + "\n", "utf-8");
532
+ await chmod2(keyPath, 384);
533
+ process.stderr.write("[keychain] Key stored encrypted (machine-bound).\n");
534
+ } else {
535
+ await writeFile2(keyPath, b64 + "\n", "utf-8");
536
+ await chmod2(keyPath, 384);
537
+ process.stderr.write(
538
+ "[keychain] WARNING: Key stored in plaintext file \u2014 no OS keychain available.\n"
539
+ );
540
+ }
367
541
  }
368
542
  async function deleteMasterKey() {
543
+ macKeychainDelete();
544
+ linuxSecretDelete();
369
545
  const keytar = await tryKeytar();
370
546
  if (keytar) {
371
547
  try {
@@ -407,12 +583,13 @@ async function importMnemonic(mnemonic) {
407
583
  const entropy = mnemonicToEntropy(trimmed);
408
584
  return Buffer.from(entropy, "hex");
409
585
  }
410
- var SERVICE, ACCOUNT;
586
+ var SERVICE, ACCOUNT, ENCRYPTED_PREFIX;
411
587
  var init_keychain = __esm({
412
588
  "src/lib/keychain.ts"() {
413
589
  "use strict";
414
590
  SERVICE = "exe-mem";
415
591
  ACCOUNT = "master-key";
592
+ ENCRYPTED_PREFIX = "enc:";
416
593
  }
417
594
  });
418
595
 
@@ -530,8 +707,8 @@ function findPackageRoot() {
530
707
  function getAvailableMemoryGB() {
531
708
  if (process.platform === "darwin") {
532
709
  try {
533
- const { execSync: execSync2 } = __require("child_process");
534
- const vmstat = execSync2("vm_stat", { encoding: "utf8" });
710
+ const { execSync: execSync3 } = __require("child_process");
711
+ const vmstat = execSync3("vm_stat", { encoding: "utf8" });
535
712
  const pageSize = 16384;
536
713
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
537
714
  const actualPageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : pageSize;
@@ -931,10 +1108,10 @@ async function disposeEmbedder() {
931
1108
  async function embedDirect(text) {
932
1109
  const llamaCpp = await import("node-llama-cpp");
933
1110
  const { MODELS_DIR: MODELS_DIR2 } = await Promise.resolve().then(() => (init_config(), config_exports));
934
- const { existsSync: existsSync16 } = await import("fs");
935
- const path16 = await import("path");
936
- const modelPath = path16.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
937
- if (!existsSync16(modelPath)) {
1111
+ const { existsSync: existsSync17 } = await import("fs");
1112
+ const path17 = await import("path");
1113
+ const modelPath = path17.join(MODELS_DIR2, "jina-embeddings-v5-small-q4_k_m.gguf");
1114
+ if (!existsSync17(modelPath)) {
938
1115
  throw new Error(`Embedding model not found at ${modelPath}. Run '/exe-setup' to download it.`);
939
1116
  }
940
1117
  const llama = await llamaCpp.getLlama();
@@ -1223,8 +1400,8 @@ async function validateLicense(apiKey, deviceId) {
1223
1400
  }
1224
1401
  function getCacheAgeMs() {
1225
1402
  try {
1226
- const { statSync: statSync2 } = __require("fs");
1227
- const s = statSync2(CACHE_PATH);
1403
+ const { statSync: statSync3 } = __require("fs");
1404
+ const s = statSync3(CACHE_PATH);
1228
1405
  return Date.now() - s.mtimeMs;
1229
1406
  } catch {
1230
1407
  return Infinity;
@@ -1687,7 +1864,7 @@ __export(employees_exports, {
1687
1864
  });
1688
1865
  import { readFile as readFile3, writeFile as writeFile3, mkdir as mkdir4 } from "fs/promises";
1689
1866
  import { existsSync as existsSync9, symlinkSync, readlinkSync, readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
1690
- import { execSync } from "child_process";
1867
+ import { execSync as execSync2 } from "child_process";
1691
1868
  import path8 from "path";
1692
1869
  import os5 from "os";
1693
1870
  function normalizeRole(role) {
@@ -1862,7 +2039,7 @@ async function normalizeRosterCase(rosterPath) {
1862
2039
  }
1863
2040
  function findExeBin() {
1864
2041
  try {
1865
- return execSync(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
2042
+ return execSync2(process.platform === "win32" ? "where exe-os" : "which exe-os", { encoding: "utf8" }).trim();
1866
2043
  } catch {
1867
2044
  return null;
1868
2045
  }
@@ -4062,6 +4239,109 @@ var init_crdt_sync = __esm({
4062
4239
  }
4063
4240
  });
4064
4241
 
4242
+ // src/lib/db-backup.ts
4243
+ var db_backup_exports = {};
4244
+ __export(db_backup_exports, {
4245
+ createBackup: () => createBackup,
4246
+ findActiveDb: () => findActiveDb,
4247
+ getBackupDir: () => getBackupDir,
4248
+ getLatestBackup: () => getLatestBackup,
4249
+ hasBackupToday: () => hasBackupToday,
4250
+ listBackups: () => listBackups,
4251
+ rotateBackups: () => rotateBackups
4252
+ });
4253
+ import { copyFileSync, existsSync as existsSync11, mkdirSync as mkdirSync4, readdirSync, unlinkSync as unlinkSync5, statSync as statSync2 } from "fs";
4254
+ import path11 from "path";
4255
+ function findActiveDb() {
4256
+ for (const name of DB_NAMES) {
4257
+ const p = path11.join(EXE_AI_DIR, name);
4258
+ if (existsSync11(p)) return p;
4259
+ }
4260
+ return null;
4261
+ }
4262
+ function createBackup(reason = "manual") {
4263
+ const dbPath = findActiveDb();
4264
+ if (!dbPath) return null;
4265
+ mkdirSync4(BACKUP_DIR, { recursive: true });
4266
+ const dbName = path11.basename(dbPath, ".db");
4267
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
4268
+ const backupName = `${dbName}-${reason}-${timestamp}.db`;
4269
+ const backupPath = path11.join(BACKUP_DIR, backupName);
4270
+ copyFileSync(dbPath, backupPath);
4271
+ const walPath = dbPath + "-wal";
4272
+ if (existsSync11(walPath)) {
4273
+ try {
4274
+ copyFileSync(walPath, backupPath + "-wal");
4275
+ } catch {
4276
+ }
4277
+ }
4278
+ const shmPath = dbPath + "-shm";
4279
+ if (existsSync11(shmPath)) {
4280
+ try {
4281
+ copyFileSync(shmPath, backupPath + "-shm");
4282
+ } catch {
4283
+ }
4284
+ }
4285
+ return backupPath;
4286
+ }
4287
+ function rotateBackups(keepDays = DEFAULT_KEEP_DAYS) {
4288
+ if (!existsSync11(BACKUP_DIR)) return 0;
4289
+ const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1e3;
4290
+ let deleted = 0;
4291
+ try {
4292
+ const files = readdirSync(BACKUP_DIR);
4293
+ for (const file of files) {
4294
+ if (!file.endsWith(".db") && !file.endsWith(".db-wal") && !file.endsWith(".db-shm")) continue;
4295
+ const filePath = path11.join(BACKUP_DIR, file);
4296
+ try {
4297
+ const stat = statSync2(filePath);
4298
+ if (stat.mtimeMs < cutoff) {
4299
+ unlinkSync5(filePath);
4300
+ deleted++;
4301
+ }
4302
+ } catch {
4303
+ }
4304
+ }
4305
+ } catch {
4306
+ }
4307
+ return deleted;
4308
+ }
4309
+ function listBackups() {
4310
+ if (!existsSync11(BACKUP_DIR)) return [];
4311
+ try {
4312
+ const files = readdirSync(BACKUP_DIR).filter((f) => f.endsWith(".db") && !f.endsWith("-wal") && !f.endsWith("-shm"));
4313
+ return files.map((name) => {
4314
+ const p = path11.join(BACKUP_DIR, name);
4315
+ const stat = statSync2(p);
4316
+ return { path: p, name, size: stat.size, date: stat.mtime };
4317
+ }).sort((a, b) => b.date.getTime() - a.date.getTime());
4318
+ } catch {
4319
+ return [];
4320
+ }
4321
+ }
4322
+ function hasBackupToday(reason) {
4323
+ const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
4324
+ const backups = listBackups();
4325
+ return backups.some((b) => b.name.includes(reason) && b.name.includes(today.replace(/-/g, "-")));
4326
+ }
4327
+ function getLatestBackup() {
4328
+ const backups = listBackups();
4329
+ return backups.length > 0 ? backups[0].path : null;
4330
+ }
4331
+ function getBackupDir() {
4332
+ return BACKUP_DIR;
4333
+ }
4334
+ var BACKUP_DIR, DEFAULT_KEEP_DAYS, DB_NAMES;
4335
+ var init_db_backup = __esm({
4336
+ "src/lib/db-backup.ts"() {
4337
+ "use strict";
4338
+ init_config();
4339
+ BACKUP_DIR = path11.join(EXE_AI_DIR, "backups");
4340
+ DEFAULT_KEEP_DAYS = 3;
4341
+ DB_NAMES = ["memories.db", "exe-mem.db", "exe-os.db", "exe.db"];
4342
+ }
4343
+ });
4344
+
4065
4345
  // src/lib/cloud-sync.ts
4066
4346
  var cloud_sync_exports = {};
4067
4347
  __export(cloud_sync_exports, {
@@ -4091,16 +4371,16 @@ __export(cloud_sync_exports, {
4091
4371
  pushToPostgres: () => pushToPostgres,
4092
4372
  recordRosterDeletion: () => recordRosterDeletion
4093
4373
  });
4094
- import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync11, readdirSync, mkdirSync as mkdirSync4, appendFileSync, unlinkSync as unlinkSync5, openSync as openSync2, closeSync as closeSync2 } from "fs";
4374
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync6, existsSync as existsSync12, readdirSync as readdirSync2, mkdirSync as mkdirSync5, appendFileSync, unlinkSync as unlinkSync6, openSync as openSync2, closeSync as closeSync2 } from "fs";
4095
4375
  import crypto3 from "crypto";
4096
- import path11 from "path";
4376
+ import path12 from "path";
4097
4377
  import { homedir as homedir2 } from "os";
4098
4378
  function sqlSafe(v) {
4099
4379
  return v === void 0 ? null : v;
4100
4380
  }
4101
4381
  function logError(msg) {
4102
4382
  try {
4103
- const logPath = path11.join(homedir2(), ".exe-os", "workers.log");
4383
+ const logPath = path12.join(homedir2(), ".exe-os", "workers.log");
4104
4384
  appendFileSync(logPath, `${(/* @__PURE__ */ new Date()).toISOString()} ${msg}
4105
4385
  `);
4106
4386
  } catch {
@@ -4109,10 +4389,10 @@ function logError(msg) {
4109
4389
  function loadPgClient() {
4110
4390
  if (_pgFailed) return null;
4111
4391
  const postgresUrl = process.env.DATABASE_URL;
4112
- const configPath = path11.join(EXE_AI_DIR, "config.json");
4392
+ const configPath = path12.join(EXE_AI_DIR, "config.json");
4113
4393
  let cloudPostgresUrl;
4114
4394
  try {
4115
- if (existsSync11(configPath)) {
4395
+ if (existsSync12(configPath)) {
4116
4396
  const cfg = JSON.parse(readFileSync8(configPath, "utf8"));
4117
4397
  cloudPostgresUrl = cfg.cloud?.postgresUrl;
4118
4398
  if (cfg.cloud?.syncToPostgres === false) {
@@ -4131,8 +4411,8 @@ function loadPgClient() {
4131
4411
  _pgPromise = (async () => {
4132
4412
  const { createRequire: createRequire3 } = await import("module");
4133
4413
  const { pathToFileURL: pathToFileURL3 } = await import("url");
4134
- const exeDbRoot = process.env.EXE_DB_ROOT ?? path11.join(homedir2(), "exe-db");
4135
- const req = createRequire3(path11.join(exeDbRoot, "package.json"));
4414
+ const exeDbRoot = process.env.EXE_DB_ROOT ?? path12.join(homedir2(), "exe-db");
4415
+ const req = createRequire3(path12.join(exeDbRoot, "package.json"));
4136
4416
  const entry = req.resolve("@prisma/client");
4137
4417
  const mod = await import(pathToFileURL3(entry).href);
4138
4418
  const Ctor = mod.PrismaClient ?? mod.default?.PrismaClient;
@@ -4185,7 +4465,7 @@ async function withRosterLock(fn) {
4185
4465
  if (Date.now() - ts < LOCK_STALE_MS) {
4186
4466
  throw new Error("Roster merge already in progress \u2014 another sync is running");
4187
4467
  }
4188
- unlinkSync5(ROSTER_LOCK_PATH);
4468
+ unlinkSync6(ROSTER_LOCK_PATH);
4189
4469
  const fd = openSync2(ROSTER_LOCK_PATH, "wx");
4190
4470
  closeSync2(fd);
4191
4471
  writeFileSync6(ROSTER_LOCK_PATH, String(Date.now()));
@@ -4201,7 +4481,7 @@ async function withRosterLock(fn) {
4201
4481
  return await fn();
4202
4482
  } finally {
4203
4483
  try {
4204
- unlinkSync5(ROSTER_LOCK_PATH);
4484
+ unlinkSync6(ROSTER_LOCK_PATH);
4205
4485
  } catch {
4206
4486
  }
4207
4487
  }
@@ -4572,13 +4852,42 @@ async function cloudSync(config) {
4572
4852
  try {
4573
4853
  const employees = await loadEmployees();
4574
4854
  rosterResult.employees = employees.length;
4575
- const idDir = path11.join(EXE_AI_DIR, "identity");
4576
- if (existsSync11(idDir)) {
4577
- rosterResult.identities = readdirSync(idDir).filter((f) => f.endsWith(".md")).length;
4855
+ const idDir = path12.join(EXE_AI_DIR, "identity");
4856
+ if (existsSync12(idDir)) {
4857
+ rosterResult.identities = readdirSync2(idDir).filter((f) => f.endsWith(".md")).length;
4578
4858
  }
4579
4859
  } catch {
4580
4860
  }
4581
4861
  const totalMemories = await countRows("SELECT COUNT(*) as cnt FROM memories WHERE status = 'active' OR status IS NULL");
4862
+ try {
4863
+ const { getLatestBackup: getLatestBackup2 } = await Promise.resolve().then(() => (init_db_backup(), db_backup_exports));
4864
+ const { statSync: statFile } = await import("fs");
4865
+ const latestBackup = getLatestBackup2();
4866
+ if (latestBackup) {
4867
+ const backupSize = statFile(latestBackup).size;
4868
+ const MAX_CLOUD_BACKUP_BYTES = 50 * 1024 * 1024;
4869
+ if (backupSize <= MAX_CLOUD_BACKUP_BYTES) {
4870
+ const backupData = readFileSync8(latestBackup);
4871
+ const deviceId = loadDeviceId() ?? "unknown";
4872
+ const encrypted = encryptSyncBlob(backupData);
4873
+ const backupRes = await fetchWithRetry(`${config.endpoint}/sync/push-db-backup`, {
4874
+ method: "POST",
4875
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${config.apiKey}` },
4876
+ body: JSON.stringify({
4877
+ device_id: deviceId,
4878
+ filename: path12.basename(latestBackup),
4879
+ blob: encrypted,
4880
+ size: backupData.length
4881
+ })
4882
+ });
4883
+ if (backupRes && !backupRes.ok) {
4884
+ logError(`[cloud-sync] DB backup upload failed: ${backupRes.status}`);
4885
+ }
4886
+ }
4887
+ }
4888
+ } catch (err) {
4889
+ logError(`[cloud-sync] DB backup upload error: ${err instanceof Error ? err.message : String(err)}`);
4890
+ }
4582
4891
  return {
4583
4892
  pushed,
4584
4893
  pulled,
@@ -4594,7 +4903,7 @@ async function cloudSync(config) {
4594
4903
  function recordRosterDeletion(name) {
4595
4904
  let deletions = [];
4596
4905
  try {
4597
- if (existsSync11(ROSTER_DELETIONS_PATH)) {
4906
+ if (existsSync12(ROSTER_DELETIONS_PATH)) {
4598
4907
  deletions = JSON.parse(readFileSync8(ROSTER_DELETIONS_PATH, "utf-8"));
4599
4908
  }
4600
4909
  } catch {
@@ -4604,7 +4913,7 @@ function recordRosterDeletion(name) {
4604
4913
  }
4605
4914
  function consumeRosterDeletions() {
4606
4915
  try {
4607
- if (!existsSync11(ROSTER_DELETIONS_PATH)) return [];
4916
+ if (!existsSync12(ROSTER_DELETIONS_PATH)) return [];
4608
4917
  const deletions = JSON.parse(readFileSync8(ROSTER_DELETIONS_PATH, "utf-8"));
4609
4918
  writeFileSync6(ROSTER_DELETIONS_PATH, "[]");
4610
4919
  return deletions;
@@ -4613,35 +4922,35 @@ function consumeRosterDeletions() {
4613
4922
  }
4614
4923
  }
4615
4924
  function buildRosterBlob(paths) {
4616
- const rosterPath = paths?.rosterPath ?? path11.join(EXE_AI_DIR, "exe-employees.json");
4617
- const identityDir = paths?.identityDir ?? path11.join(EXE_AI_DIR, "identity");
4618
- const configPath = paths?.configPath ?? path11.join(EXE_AI_DIR, "config.json");
4925
+ const rosterPath = paths?.rosterPath ?? path12.join(EXE_AI_DIR, "exe-employees.json");
4926
+ const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
4927
+ const configPath = paths?.configPath ?? path12.join(EXE_AI_DIR, "config.json");
4619
4928
  let roster = [];
4620
- if (existsSync11(rosterPath)) {
4929
+ if (existsSync12(rosterPath)) {
4621
4930
  try {
4622
4931
  roster = JSON.parse(readFileSync8(rosterPath, "utf-8"));
4623
4932
  } catch {
4624
4933
  }
4625
4934
  }
4626
4935
  const identities = {};
4627
- if (existsSync11(identityDir)) {
4628
- for (const file of readdirSync(identityDir).filter((f) => f.endsWith(".md"))) {
4936
+ if (existsSync12(identityDir)) {
4937
+ for (const file of readdirSync2(identityDir).filter((f) => f.endsWith(".md"))) {
4629
4938
  try {
4630
- identities[file] = readFileSync8(path11.join(identityDir, file), "utf-8");
4939
+ identities[file] = readFileSync8(path12.join(identityDir, file), "utf-8");
4631
4940
  } catch {
4632
4941
  }
4633
4942
  }
4634
4943
  }
4635
4944
  let config;
4636
- if (existsSync11(configPath)) {
4945
+ if (existsSync12(configPath)) {
4637
4946
  try {
4638
4947
  config = JSON.parse(readFileSync8(configPath, "utf-8"));
4639
4948
  } catch {
4640
4949
  }
4641
4950
  }
4642
4951
  let agentConfig;
4643
- const agentConfigPath = path11.join(EXE_AI_DIR, "agent-config.json");
4644
- if (existsSync11(agentConfigPath)) {
4952
+ const agentConfigPath = path12.join(EXE_AI_DIR, "agent-config.json");
4953
+ if (existsSync12(agentConfigPath)) {
4645
4954
  try {
4646
4955
  agentConfig = JSON.parse(readFileSync8(agentConfigPath, "utf-8"));
4647
4956
  } catch {
@@ -4719,16 +5028,16 @@ async function cloudPullRoster(config) {
4719
5028
  }
4720
5029
  }
4721
5030
  function mergeConfig(remoteConfig, configPath) {
4722
- const cfgPath = configPath ?? path11.join(EXE_AI_DIR, "config.json");
5031
+ const cfgPath = configPath ?? path12.join(EXE_AI_DIR, "config.json");
4723
5032
  let local = {};
4724
- if (existsSync11(cfgPath)) {
5033
+ if (existsSync12(cfgPath)) {
4725
5034
  try {
4726
5035
  local = JSON.parse(readFileSync8(cfgPath, "utf-8"));
4727
5036
  } catch {
4728
5037
  }
4729
5038
  }
4730
5039
  const merged = { ...remoteConfig, ...local };
4731
- const dir = path11.dirname(cfgPath);
5040
+ const dir = path12.dirname(cfgPath);
4732
5041
  ensurePrivateDirSync(dir);
4733
5042
  writeFileSync6(cfgPath, JSON.stringify(merged, null, 2), "utf-8");
4734
5043
  enforcePrivateFileSync(cfgPath);
@@ -4736,7 +5045,7 @@ function mergeConfig(remoteConfig, configPath) {
4736
5045
  async function mergeRosterFromRemote(remote, paths) {
4737
5046
  return withRosterLock(async () => {
4738
5047
  const rosterPath = paths?.rosterPath ?? void 0;
4739
- const identityDir = paths?.identityDir ?? path11.join(EXE_AI_DIR, "identity");
5048
+ const identityDir = paths?.identityDir ?? path12.join(EXE_AI_DIR, "identity");
4740
5049
  const localEmployees = await loadEmployees(rosterPath);
4741
5050
  const localNames = new Set(localEmployees.map((e) => e.name));
4742
5051
  let added = 0;
@@ -4757,11 +5066,11 @@ async function mergeRosterFromRemote(remote, paths) {
4757
5066
  ) ?? lookupKey;
4758
5067
  const remoteIdentity = remote.identities[matchedKey];
4759
5068
  if (remoteIdentity) {
4760
- if (!existsSync11(identityDir)) mkdirSync4(identityDir, { recursive: true });
4761
- const idPath = path11.join(identityDir, `${remoteEmp.name}.md`);
5069
+ if (!existsSync12(identityDir)) mkdirSync5(identityDir, { recursive: true });
5070
+ const idPath = path12.join(identityDir, `${remoteEmp.name}.md`);
4762
5071
  let localIdentity = null;
4763
5072
  try {
4764
- localIdentity = existsSync11(idPath) ? readFileSync8(idPath, "utf-8") : null;
5073
+ localIdentity = existsSync12(idPath) ? readFileSync8(idPath, "utf-8") : null;
4765
5074
  } catch {
4766
5075
  }
4767
5076
  if (localIdentity !== remoteIdentity) {
@@ -4791,16 +5100,16 @@ async function mergeRosterFromRemote(remote, paths) {
4791
5100
  }
4792
5101
  if (remote.agentConfig && Object.keys(remote.agentConfig).length > 0) {
4793
5102
  try {
4794
- const agentConfigPath = path11.join(EXE_AI_DIR, "agent-config.json");
5103
+ const agentConfigPath = path12.join(EXE_AI_DIR, "agent-config.json");
4795
5104
  let local = {};
4796
- if (existsSync11(agentConfigPath)) {
5105
+ if (existsSync12(agentConfigPath)) {
4797
5106
  try {
4798
5107
  local = JSON.parse(readFileSync8(agentConfigPath, "utf-8"));
4799
5108
  } catch {
4800
5109
  }
4801
5110
  }
4802
5111
  const merged = { ...remote.agentConfig, ...local };
4803
- ensurePrivateDirSync(path11.dirname(agentConfigPath));
5112
+ ensurePrivateDirSync(path12.dirname(agentConfigPath));
4804
5113
  writeFileSync6(agentConfigPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
4805
5114
  enforcePrivateFileSync(agentConfigPath);
4806
5115
  } catch {
@@ -5241,11 +5550,11 @@ var init_cloud_sync = __esm({
5241
5550
  LOCALHOST_PATTERNS = /^(localhost|127\.0\.0\.1|\[::1\])$/i;
5242
5551
  FETCH_TIMEOUT_MS = 3e4;
5243
5552
  PUSH_BATCH_SIZE = 5e3;
5244
- ROSTER_LOCK_PATH = path11.join(EXE_AI_DIR, "roster-merge.lock");
5553
+ ROSTER_LOCK_PATH = path12.join(EXE_AI_DIR, "roster-merge.lock");
5245
5554
  LOCK_STALE_MS = 3e4;
5246
5555
  _pgPromise = null;
5247
5556
  _pgFailed = false;
5248
- ROSTER_DELETIONS_PATH = path11.join(EXE_AI_DIR, "roster-deletions.json");
5557
+ ROSTER_DELETIONS_PATH = path12.join(EXE_AI_DIR, "roster-deletions.json");
5249
5558
  }
5250
5559
  });
5251
5560
 
@@ -5255,12 +5564,12 @@ __export(preferences_exports, {
5255
5564
  loadPreferences: () => loadPreferences,
5256
5565
  savePreferences: () => savePreferences
5257
5566
  });
5258
- import { existsSync as existsSync12, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
5259
- import path12 from "path";
5567
+ import { existsSync as existsSync13, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
5568
+ import path13 from "path";
5260
5569
  import os7 from "os";
5261
5570
  function loadPreferences(homeDir = os7.homedir()) {
5262
- const configPath = path12.join(homeDir, ".exe-os", "config.json");
5263
- if (!existsSync12(configPath)) return {};
5571
+ const configPath = path13.join(homeDir, ".exe-os", "config.json");
5572
+ if (!existsSync13(configPath)) return {};
5264
5573
  try {
5265
5574
  const config = JSON.parse(readFileSync9(configPath, "utf-8"));
5266
5575
  return config.preferences ?? {};
@@ -5269,11 +5578,11 @@ function loadPreferences(homeDir = os7.homedir()) {
5269
5578
  }
5270
5579
  }
5271
5580
  function savePreferences(prefs, homeDir = os7.homedir()) {
5272
- const configDir = path12.join(homeDir, ".exe-os");
5273
- const configPath = path12.join(configDir, "config.json");
5581
+ const configDir = path13.join(homeDir, ".exe-os");
5582
+ const configPath = path13.join(configDir, "config.json");
5274
5583
  ensurePrivateDirSync(configDir);
5275
5584
  let config = {};
5276
- if (existsSync12(configPath)) {
5585
+ if (existsSync13(configPath)) {
5277
5586
  try {
5278
5587
  config = JSON.parse(readFileSync9(configPath, "utf-8"));
5279
5588
  } catch {
@@ -6120,17 +6429,17 @@ __export(identity_exports, {
6120
6429
  listIdentities: () => listIdentities,
6121
6430
  updateIdentity: () => updateIdentity
6122
6431
  });
6123
- import { existsSync as existsSync13, mkdirSync as mkdirSync5, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
6124
- import { readdirSync as readdirSync2 } from "fs";
6125
- import path13 from "path";
6432
+ import { existsSync as existsSync14, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
6433
+ import { readdirSync as readdirSync3 } from "fs";
6434
+ import path14 from "path";
6126
6435
  import { createHash as createHash2 } from "crypto";
6127
6436
  function ensureDir() {
6128
- if (!existsSync13(IDENTITY_DIR2)) {
6129
- mkdirSync5(IDENTITY_DIR2, { recursive: true });
6437
+ if (!existsSync14(IDENTITY_DIR2)) {
6438
+ mkdirSync6(IDENTITY_DIR2, { recursive: true });
6130
6439
  }
6131
6440
  }
6132
6441
  function identityPath(agentId) {
6133
- return path13.join(IDENTITY_DIR2, `${agentId}.md`);
6442
+ return path14.join(IDENTITY_DIR2, `${agentId}.md`);
6134
6443
  }
6135
6444
  function parseFrontmatter(raw) {
6136
6445
  const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
@@ -6171,7 +6480,7 @@ function contentHash(content) {
6171
6480
  }
6172
6481
  function getIdentity(agentId) {
6173
6482
  const filePath = identityPath(agentId);
6174
- if (!existsSync13(filePath)) return null;
6483
+ if (!existsSync14(filePath)) return null;
6175
6484
  const raw = readFileSync10(filePath, "utf-8");
6176
6485
  const { frontmatter, body } = parseFrontmatter(raw);
6177
6486
  return {
@@ -6203,7 +6512,7 @@ async function updateIdentity(agentId, content, updatedBy) {
6203
6512
  }
6204
6513
  function listIdentities() {
6205
6514
  ensureDir();
6206
- const files = readdirSync2(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
6515
+ const files = readdirSync3(IDENTITY_DIR2).filter((f) => f.endsWith(".md"));
6207
6516
  const results = [];
6208
6517
  for (const file of files) {
6209
6518
  const agentId = file.replace(".md", "");
@@ -6242,7 +6551,7 @@ var init_identity = __esm({
6242
6551
  "use strict";
6243
6552
  init_config();
6244
6553
  init_database();
6245
- IDENTITY_DIR2 = path13.join(EXE_AI_DIR, "identity");
6554
+ IDENTITY_DIR2 = path14.join(EXE_AI_DIR, "identity");
6246
6555
  }
6247
6556
  });
6248
6557
 
@@ -6793,28 +7102,28 @@ __export(session_wrappers_exports, {
6793
7102
  generateSessionWrappers: () => generateSessionWrappers
6794
7103
  });
6795
7104
  import {
6796
- existsSync as existsSync14,
7105
+ existsSync as existsSync15,
6797
7106
  readFileSync as readFileSync11,
6798
7107
  writeFileSync as writeFileSync9,
6799
- mkdirSync as mkdirSync6,
7108
+ mkdirSync as mkdirSync7,
6800
7109
  chmodSync as chmodSync2,
6801
- readdirSync as readdirSync3,
6802
- unlinkSync as unlinkSync6
7110
+ readdirSync as readdirSync4,
7111
+ unlinkSync as unlinkSync7
6803
7112
  } from "fs";
6804
- import path14 from "path";
7113
+ import path15 from "path";
6805
7114
  import { homedir as homedir3 } from "os";
6806
7115
  function generateSessionWrappers(packageRoot, homeDir) {
6807
7116
  const home = homeDir ?? homedir3();
6808
- const binDir = path14.join(home, ".exe-os", "bin");
6809
- const rosterPath = path14.join(home, ".exe-os", "exe-employees.json");
6810
- mkdirSync6(binDir, { recursive: true });
6811
- const exeStartDst = path14.join(binDir, "exe-start");
7117
+ const binDir = path15.join(home, ".exe-os", "bin");
7118
+ const rosterPath = path15.join(home, ".exe-os", "exe-employees.json");
7119
+ mkdirSync7(binDir, { recursive: true });
7120
+ const exeStartDst = path15.join(binDir, "exe-start");
6812
7121
  const candidates = [
6813
- path14.join(packageRoot, "dist", "bin", "exe-start.sh"),
6814
- path14.join(packageRoot, "src", "bin", "exe-start.sh")
7122
+ path15.join(packageRoot, "dist", "bin", "exe-start.sh"),
7123
+ path15.join(packageRoot, "src", "bin", "exe-start.sh")
6815
7124
  ];
6816
7125
  for (const src of candidates) {
6817
- if (existsSync14(src)) {
7126
+ if (existsSync15(src)) {
6818
7127
  writeFileSync9(exeStartDst, readFileSync11(src));
6819
7128
  chmodSync2(exeStartDst, 493);
6820
7129
  break;
@@ -6830,13 +7139,13 @@ function generateSessionWrappers(packageRoot, homeDir) {
6830
7139
  return { created: 0, pathConfigured: false };
6831
7140
  }
6832
7141
  try {
6833
- for (const f of readdirSync3(binDir)) {
7142
+ for (const f of readdirSync4(binDir)) {
6834
7143
  if (f === "exe-start") continue;
6835
- const fPath = path14.join(binDir, f);
7144
+ const fPath = path15.join(binDir, f);
6836
7145
  try {
6837
7146
  const content = readFileSync11(fPath, "utf8");
6838
7147
  if (content.includes("exe-start")) {
6839
- unlinkSync6(fPath);
7148
+ unlinkSync7(fPath);
6840
7149
  }
6841
7150
  } catch {
6842
7151
  }
@@ -6849,30 +7158,30 @@ exec "${exeStartDst}" "$0" "$@"
6849
7158
  `;
6850
7159
  for (const emp of employees) {
6851
7160
  for (let n = 1; n <= MAX_N; n++) {
6852
- const wrapperPath = path14.join(binDir, `${emp.name}${n}`);
7161
+ const wrapperPath = path15.join(binDir, `${emp.name}${n}`);
6853
7162
  writeFileSync9(wrapperPath, wrapperContent);
6854
7163
  chmodSync2(wrapperPath, 493);
6855
7164
  created++;
6856
- const codexPath = path14.join(binDir, `${emp.name}${n}-codex`);
7165
+ const codexPath = path15.join(binDir, `${emp.name}${n}-codex`);
6857
7166
  writeFileSync9(codexPath, wrapperContent);
6858
7167
  chmodSync2(codexPath, 493);
6859
7168
  created++;
6860
7169
  }
6861
7170
  }
6862
7171
  const codexLauncherCandidates = [
6863
- path14.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
6864
- path14.join(packageRoot, "src", "bin", "exe-start-codex.ts")
7172
+ path15.join(packageRoot, "dist", "bin", "exe-start-codex.js"),
7173
+ path15.join(packageRoot, "src", "bin", "exe-start-codex.ts")
6865
7174
  ];
6866
7175
  let codexLauncher = null;
6867
7176
  for (const c of codexLauncherCandidates) {
6868
- if (existsSync14(c)) {
7177
+ if (existsSync15(c)) {
6869
7178
  codexLauncher = c;
6870
7179
  break;
6871
7180
  }
6872
7181
  }
6873
7182
  if (codexLauncher) {
6874
7183
  for (const emp of employees) {
6875
- const wrapperPath = path14.join(binDir, `${emp.name}-codex`);
7184
+ const wrapperPath = path15.join(binDir, `${emp.name}-codex`);
6876
7185
  const content = `#!/bin/bash
6877
7186
  exec node "${codexLauncher}" --agent ${emp.name} "$@"
6878
7187
  `;
@@ -6895,12 +7204,12 @@ export PATH="${binDir}:$PATH"
6895
7204
  const shell = process.env.SHELL ?? "/bin/bash";
6896
7205
  const profilePaths = [];
6897
7206
  if (shell.includes("zsh")) {
6898
- profilePaths.push(path14.join(home, ".zshrc"));
7207
+ profilePaths.push(path15.join(home, ".zshrc"));
6899
7208
  } else if (shell.includes("bash")) {
6900
- profilePaths.push(path14.join(home, ".bashrc"));
6901
- profilePaths.push(path14.join(home, ".bash_profile"));
7209
+ profilePaths.push(path15.join(home, ".bashrc"));
7210
+ profilePaths.push(path15.join(home, ".bash_profile"));
6902
7211
  } else {
6903
- profilePaths.push(path14.join(home, ".profile"));
7212
+ profilePaths.push(path15.join(home, ".profile"));
6904
7213
  }
6905
7214
  for (const profilePath of profilePaths) {
6906
7215
  try {
@@ -6932,9 +7241,9 @@ var init_session_wrappers = __esm({
6932
7241
  init_config();
6933
7242
  init_keychain();
6934
7243
  import crypto4 from "crypto";
6935
- import { existsSync as existsSync15, mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync10, unlinkSync as unlinkSync7 } from "fs";
7244
+ import { existsSync as existsSync16, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync10, unlinkSync as unlinkSync8 } from "fs";
6936
7245
  import os8 from "os";
6937
- import path15 from "path";
7246
+ import path16 from "path";
6938
7247
  import { createInterface } from "readline";
6939
7248
 
6940
7249
  // src/lib/model-downloader.ts
@@ -7026,22 +7335,22 @@ async function fileHash(filePath) {
7026
7335
 
7027
7336
  // src/lib/setup-wizard.ts
7028
7337
  function findPackageRoot2() {
7029
- let dir = path15.dirname(new URL(import.meta.url).pathname);
7030
- const root = path15.parse(dir).root;
7338
+ let dir = path16.dirname(new URL(import.meta.url).pathname);
7339
+ const root = path16.parse(dir).root;
7031
7340
  while (dir !== root) {
7032
- const pkgPath = path15.join(dir, "package.json");
7033
- if (existsSync15(pkgPath)) {
7341
+ const pkgPath = path16.join(dir, "package.json");
7342
+ if (existsSync16(pkgPath)) {
7034
7343
  try {
7035
7344
  const pkg = JSON.parse(readFileSync12(pkgPath, "utf-8"));
7036
7345
  if (pkg.name === "@askexenow/exe-os" || pkg.name === "exe-os") return dir;
7037
7346
  } catch {
7038
7347
  }
7039
7348
  }
7040
- dir = path15.dirname(dir);
7349
+ dir = path16.dirname(dir);
7041
7350
  }
7042
7351
  return null;
7043
7352
  }
7044
- var SETUP_STATE_PATH = path15.join(os8.homedir(), ".exe-os", "setup-state.json");
7353
+ var SETUP_STATE_PATH = path16.join(os8.homedir(), ".exe-os", "setup-state.json");
7045
7354
  function loadSetupState() {
7046
7355
  try {
7047
7356
  return JSON.parse(readFileSync12(SETUP_STATE_PATH, "utf8"));
@@ -7050,12 +7359,12 @@ function loadSetupState() {
7050
7359
  }
7051
7360
  }
7052
7361
  function saveSetupState(state) {
7053
- mkdirSync7(path15.dirname(SETUP_STATE_PATH), { recursive: true });
7362
+ mkdirSync8(path16.dirname(SETUP_STATE_PATH), { recursive: true });
7054
7363
  writeFileSync10(SETUP_STATE_PATH, JSON.stringify(state, null, 2));
7055
7364
  }
7056
7365
  function clearSetupState() {
7057
7366
  try {
7058
- unlinkSync7(SETUP_STATE_PATH);
7367
+ unlinkSync8(SETUP_STATE_PATH);
7059
7368
  } catch {
7060
7369
  }
7061
7370
  }
@@ -7072,8 +7381,8 @@ function ask(rl, prompt) {
7072
7381
  function getAvailableMemoryGB2() {
7073
7382
  if (process.platform === "darwin") {
7074
7383
  try {
7075
- const { execSync: execSync2 } = __require("child_process");
7076
- const vmstat = execSync2("vm_stat", { encoding: "utf8" });
7384
+ const { execSync: execSync3 } = __require("child_process");
7385
+ const vmstat = execSync3("vm_stat", { encoding: "utf8" });
7077
7386
  const pageSizeMatch = vmstat.match(/page size of (\d+) bytes/);
7078
7387
  const pageSize = pageSizeMatch ? parseInt(pageSizeMatch[1], 10) : 16384;
7079
7388
  const free = vmstat.match(/Pages free:\s+(\d+)/);
@@ -7101,10 +7410,10 @@ async function validateModel(log) {
7101
7410
  if (totalGB <= 8 || isLowMemory()) {
7102
7411
  log(`System memory: ${totalGB.toFixed(0)}GB total, ${freeGB.toFixed(1)}GB free`);
7103
7412
  log("Skipping in-memory model validation (low memory \u2014 will validate on first use).");
7104
- const modelPath = path15.join(MODELS_DIR, LOCAL_FILENAME);
7105
- if (existsSync15(modelPath)) {
7106
- const { statSync: statSync2 } = await import("fs");
7107
- const size = statSync2(modelPath).size;
7413
+ const modelPath = path16.join(MODELS_DIR, LOCAL_FILENAME);
7414
+ if (existsSync16(modelPath)) {
7415
+ const { statSync: statSync3 } = await import("fs");
7416
+ const size = statSync3(modelPath).size;
7108
7417
  if (size > 300 * 1e6) {
7109
7418
  log(`Model file verified (${(size / 1e6).toFixed(0)} MB).`);
7110
7419
  return;
@@ -7193,7 +7502,7 @@ async function runSetupWizard(opts = {}) {
7193
7502
  if (state.completedSteps.length > 0) {
7194
7503
  log(`Resuming setup from step ${Math.max(...state.completedSteps) + 1}...`);
7195
7504
  }
7196
- if (existsSync15(LEGACY_LANCE_PATH)) {
7505
+ if (existsSync16(LEGACY_LANCE_PATH)) {
7197
7506
  log("\u26A0 Found v1.0 LanceDB at ~/.exe-os/local.lance");
7198
7507
  log(" v1.1 uses libSQL (SQLite). Your existing memories are not automatically migrated.");
7199
7508
  log(" The old directory will not be modified or deleted.");
@@ -7208,6 +7517,23 @@ async function runSetupWizard(opts = {}) {
7208
7517
  const key = crypto4.randomBytes(32);
7209
7518
  await setMasterKey(key);
7210
7519
  log("Encryption key generated and stored securely.");
7520
+ log("");
7521
+ const { exportMnemonic: exportMnemonic2 } = await Promise.resolve().then(() => (init_keychain(), keychain_exports));
7522
+ const mnemonic = await exportMnemonic2(key);
7523
+ log("=============================================================");
7524
+ log(" YOUR 24-WORD RECOVERY PHRASE");
7525
+ log("=============================================================");
7526
+ log("");
7527
+ log(" " + mnemonic);
7528
+ log("");
7529
+ log(" SAVE THIS NOW. Write it down or store it in your password");
7530
+ log(" manager. This phrase is the ONLY way to decrypt your memories");
7531
+ log(" if you lose this computer. We cannot recover it for you.");
7532
+ log("");
7533
+ log(" Like a Bitcoin wallet \u2014 lose the phrase, lose everything.");
7534
+ log("=============================================================");
7535
+ log("");
7536
+ await ask(rl, "Press Enter after you've saved your recovery phrase...");
7211
7537
  }
7212
7538
  state.completedSteps.push(1);
7213
7539
  saveSetupState(state);
@@ -7357,7 +7683,7 @@ async function runSetupWizard(opts = {}) {
7357
7683
  await saveConfig(config);
7358
7684
  log("");
7359
7685
  try {
7360
- const claudeJsonPath = path15.join(os8.homedir(), ".claude.json");
7686
+ const claudeJsonPath = path16.join(os8.homedir(), ".claude.json");
7361
7687
  let claudeJson = {};
7362
7688
  try {
7363
7689
  claudeJson = JSON.parse(readFileSync12(claudeJsonPath, "utf8"));
@@ -7383,7 +7709,7 @@ async function runSetupWizard(opts = {}) {
7383
7709
  const prefs = { ...existingPrefs };
7384
7710
  log("=== Config Defaults ===");
7385
7711
  log("");
7386
- const ghosttyDetected = existsSync15(path15.join(os8.homedir(), ".config", "ghostty")) || existsSync15(path15.join(os8.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
7712
+ const ghosttyDetected = existsSync16(path16.join(os8.homedir(), ".config", "ghostty")) || existsSync16(path16.join(os8.homedir(), "Library", "Application Support", "com.mitchellh.ghostty"));
7387
7713
  if (ghosttyDetected) {
7388
7714
  const ghosttyAnswer = await ask(rl, "Detected Ghostty terminal. Use exe-os Ghostty defaults? (Y/n) ");
7389
7715
  prefs.ghostty = ghosttyAnswer.toLowerCase() !== "n";
@@ -7430,7 +7756,7 @@ async function runSetupWizard(opts = {}) {
7430
7756
  let missingIdentities = [];
7431
7757
  for (const emp of roster) {
7432
7758
  const idPath = identityPath2(emp.name);
7433
- if (!existsSync15(idPath)) {
7759
+ if (!existsSync16(idPath)) {
7434
7760
  missingIdentities.push(emp.name);
7435
7761
  }
7436
7762
  }
@@ -7462,7 +7788,7 @@ async function runSetupWizard(opts = {}) {
7462
7788
  }
7463
7789
  missingIdentities = [];
7464
7790
  for (const emp of roster) {
7465
- if (!existsSync15(identityPath2(emp.name))) {
7791
+ if (!existsSync16(identityPath2(emp.name))) {
7466
7792
  missingIdentities.push(emp.name);
7467
7793
  }
7468
7794
  }
@@ -7527,7 +7853,7 @@ async function runSetupWizard(opts = {}) {
7527
7853
  const cooIdentityContent = getIdentityTemplate("coo");
7528
7854
  if (cooIdentityContent) {
7529
7855
  const cooIdPath = identityPath2(cooName);
7530
- mkdirSync7(path15.dirname(cooIdPath), { recursive: true });
7856
+ mkdirSync8(path16.dirname(cooIdPath), { recursive: true });
7531
7857
  const replaced = cooIdentityContent.replace(/agent_id:\s*exe/g, `agent_id: ${cooName}`).replace(/\$\{agent_id\}/g, cooName);
7532
7858
  writeFileSync10(cooIdPath, replaced, "utf-8");
7533
7859
  }
@@ -7623,7 +7949,7 @@ async function runSetupWizard(opts = {}) {
7623
7949
  const ctoIdentityContent = getIdentityTemplate("cto");
7624
7950
  if (ctoIdentityContent) {
7625
7951
  const ctoIdPath = identityPath2(ctoName);
7626
- mkdirSync7(path15.dirname(ctoIdPath), { recursive: true });
7952
+ mkdirSync8(path16.dirname(ctoIdPath), { recursive: true });
7627
7953
  const replaced = ctoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${ctoName}`).replace(/\$\{agent_id\}/g, ctoName);
7628
7954
  writeFileSync10(ctoIdPath, replaced, "utf-8");
7629
7955
  }
@@ -7646,7 +7972,7 @@ async function runSetupWizard(opts = {}) {
7646
7972
  const cmoIdentityContent = getIdentityTemplate("cmo");
7647
7973
  if (cmoIdentityContent) {
7648
7974
  const cmoIdPath = identityPath2(cmoName);
7649
- mkdirSync7(path15.dirname(cmoIdPath), { recursive: true });
7975
+ mkdirSync8(path16.dirname(cmoIdPath), { recursive: true });
7650
7976
  const replaced = cmoIdentityContent.replace(/agent_id:\s*\w+/g, `agent_id: ${cmoName}`).replace(/\$\{agent_id\}/g, cmoName);
7651
7977
  writeFileSync10(cmoIdPath, replaced, "utf-8");
7652
7978
  }
@@ -7670,7 +7996,7 @@ async function runSetupWizard(opts = {}) {
7670
7996
  log(`Session shortcuts generated (${cooName}1, ${cooName}2, ...)`);
7671
7997
  }
7672
7998
  if (wrapResult.pathConfigured) {
7673
- const binDir = path15.join(os8.homedir(), ".exe-os", "bin");
7999
+ const binDir = path16.join(os8.homedir(), ".exe-os", "bin");
7674
8000
  process.env.PATH = `${binDir}:${process.env.PATH ?? ""}`;
7675
8001
  pathJustConfigured = true;
7676
8002
  }
@@ -7713,7 +8039,7 @@ async function runSetupWizard(opts = {}) {
7713
8039
  const pkgRoot2 = findPackageRoot2();
7714
8040
  if (pkgRoot2) {
7715
8041
  try {
7716
- version = JSON.parse(readFileSync12(path15.join(pkgRoot2, "package.json"), "utf-8")).version;
8042
+ version = JSON.parse(readFileSync12(path16.join(pkgRoot2, "package.json"), "utf-8")).version;
7717
8043
  } catch {
7718
8044
  }
7719
8045
  }